diff --git a/frankenphp.go b/frankenphp.go index c801e7a95f..a5fbfc0ca3 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -52,7 +52,8 @@ var ( ErrScriptExecution = errors.New("error during PHP script execution") ErrNotRunning = errors.New("FrankenPHP is not running. For proper configuration visit: https://frankenphp.dev/docs/config/#caddyfile-config") - isRunning bool + isRunning bool + onServerShutdown []func() loggerMu sync.RWMutex logger *slog.Logger @@ -216,7 +217,7 @@ func Init(options ...Option) error { // add registered external workers for _, ew := range extensionWorkers { - options = append(options, WithWorkers(ew.Name(), ew.FileName(), ew.MinThreads(), WithWorkerEnv(ew.Env()))) + options = append(options, WithWorkers(ew.name, ew.fileName, ew.num, ew.options...)) } opt := &opt{} @@ -291,6 +292,17 @@ func Init(options ...Option) error { logger.LogAttrs(ctx, slog.LevelInfo, "embedded PHP app 📦", slog.String("path", EmbeddedAppPath)) } + // register the startup/shutdown hooks (mainly useful for extensions) + onServerShutdown = nil + for _, w := range opt.workers { + if w.onServerStartup != nil { + w.onServerStartup() + } + if w.onServerShutdown != nil { + onServerShutdown = append(onServerShutdown, w.onServerShutdown) + } + } + return nil } @@ -300,6 +312,11 @@ func Shutdown() { return } + // call the shutdown hooks (mainly useful for extensions) + for _, fn := range onServerShutdown { + fn() + } + drainWatcher() drainAutoScaling() drainPHPThreads() diff --git a/options.go b/options.go index 18c5ba20fd..befe3a7fb8 100644 --- a/options.go +++ b/options.go @@ -35,6 +35,10 @@ type workerOpt struct { env PreparedEnv watch []string maxConsecutiveFailures int + onThreadReady func(int) + onThreadShutdown func(int) + onServerStartup func() + onServerShutdown func() } // WithNumThreads configures the number of PHP threads to start. @@ -116,6 +120,40 @@ func WithWorkerMaxFailures(maxFailures int) WorkerOption { } } +func WithWorkerOnReady(f func(int)) WorkerOption { + return func(w *workerOpt) error { + w.onThreadReady = f + + return nil + } +} + +func WithWorkerOnShutdown(f func(int)) WorkerOption { + return func(w *workerOpt) error { + w.onThreadShutdown = f + + return nil + } +} + +// WithWorkerOnServerStartup adds a function to be called right after server startup. Useful for extensions. +func WithWorkerOnServerStartup(f func()) WorkerOption { + return func(w *workerOpt) error { + w.onServerStartup = f + + return nil + } +} + +// WithWorkerOnServerShutdown adds a function to be called right before server shutdown. Useful for extensions. +func WithWorkerOnServerShutdown(f func()) WorkerOption { + return func(w *workerOpt) error { + w.onServerShutdown = f + + return nil + } +} + // WithLogger configures the global logger to use. func WithLogger(l *slog.Logger) Option { return func(o *opt) error { diff --git a/testdata/message-worker.php b/testdata/message-worker.php new file mode 100644 index 0000000000..a73f9fa64c --- /dev/null +++ b/testdata/message-worker.php @@ -0,0 +1,8 @@ +