@@ -30,6 +30,9 @@ const defaultDocumentRoot = "public"
3030
3131var iniError = errors .New ("'php_ini' must be in the format: php_ini \" <key>\" \" <value>\" " )
3232
33+ // moduleWorkers is a package-level variable to store workers that can be accessed by both FrankenPHPModule and FrankenPHPApp
34+ var moduleWorkers []workerConfig
35+
3336func init () {
3437 caddy .RegisterModule (FrankenPHPApp {})
3538 caddy .RegisterModule (FrankenPHPModule {})
@@ -55,6 +58,8 @@ type workerConfig struct {
5558 Env map [string ]string `json:"env,omitempty"`
5659 // Directories to watch for file changes
5760 Watch []string `json:"watch,omitempty"`
61+ // ModuleID identifies which module created this worker
62+ ModuleID string `json:"module_id,omitempty"`
5863}
5964
6065type FrankenPHPApp struct {
@@ -84,6 +89,7 @@ func (f FrankenPHPApp) CaddyModule() caddy.ModuleInfo {
8489// Provision sets up the module.
8590func (f * FrankenPHPApp ) Provision (ctx caddy.Context ) error {
8691 f .logger = ctx .Logger ()
92+ f .logger .Info ("FrankenPHPApp provisioning 🐘" )
8793
8894 if httpApp , err := ctx .AppIfConfigured ("http" ); err == nil {
8995 if httpApp .(* caddyhttp.App ).Metrics != nil {
@@ -108,8 +114,14 @@ func (f *FrankenPHPApp) Start() error {
108114 frankenphp .WithPhpIni (f .PhpIni ),
109115 frankenphp .WithMaxWaitTime (f .MaxWaitTime ),
110116 }
117+ // Add workers from FrankenPHPApp configuration
111118 for _ , w := range f .Workers {
112- opts = append (opts , frankenphp .WithWorkers (w .Name , repl .ReplaceKnown (w .FileName , "" ), w .Num , w .Env , w .Watch ))
119+ opts = append (opts , frankenphp .WithWorkers (w .Name , repl .ReplaceKnown (w .FileName , "" ), w .Num , w .Env , w .Watch , w .ModuleID ))
120+ }
121+
122+ // Add workers from shared location (added by FrankenPHPModule)
123+ for _ , w := range moduleWorkers {
124+ opts = append (opts , frankenphp .WithWorkers (w .Name , repl .ReplaceKnown (w .FileName , "" ), w .Num , w .Env , w .Watch , w .ModuleID ))
113125 }
114126
115127 frankenphp .Shutdown ()
@@ -135,6 +147,9 @@ func (f *FrankenPHPApp) Stop() error {
135147 f .NumThreads = 0
136148 f .MaxWaitTime = 0
137149
150+ // reset shared workers
151+ moduleWorkers = nil
152+
138153 return nil
139154}
140155
@@ -341,6 +356,8 @@ type FrankenPHPModule struct {
341356 ResolveRootSymlink * bool `json:"resolve_root_symlink,omitempty"`
342357 // Env sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
343358 Env map [string ]string `json:"env,omitempty"`
359+ // Workers configures the worker scripts to start.
360+ Workers []workerConfig `json:"workers,omitempty"`
344361
345362 resolvedDocumentRoot string
346363 preparedEnv frankenphp.PreparedEnv
@@ -359,6 +376,7 @@ func (FrankenPHPModule) CaddyModule() caddy.ModuleInfo {
359376// Provision sets up the module.
360377func (f * FrankenPHPModule ) Provision (ctx caddy.Context ) error {
361378 f .logger = ctx .Logger ()
379+ f .logger .Info ("FrankenPHPModule provisioning 🐘" )
362380
363381 if f .Root == "" {
364382 if frankenphp .EmbeddedAppPath == "" {
@@ -412,6 +430,15 @@ func (f *FrankenPHPModule) Provision(ctx caddy.Context) error {
412430 }
413431 }
414432
433+ if len (f .Workers ) > 0 {
434+ // Tag workers with a unique module ID based on the module's memory address
435+ moduleID := fmt .Sprintf ("%p" , f )
436+ for i := range f .Workers {
437+ f .Workers [i ].ModuleID = moduleID
438+ }
439+ moduleWorkers = append (moduleWorkers , f .Workers ... )
440+ }
441+
415442 return nil
416443}
417444
@@ -422,7 +449,7 @@ func needReplacement(s string) bool {
422449
423450// ServeHTTP implements caddyhttp.MiddlewareHandler.
424451// TODO: Expose TLS versions as env vars, as Apache's mod_ssl: https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go#L298
425- func (f FrankenPHPModule ) ServeHTTP (w http.ResponseWriter , r * http.Request , _ caddyhttp.Handler ) error {
452+ func (f * FrankenPHPModule ) ServeHTTP (w http.ResponseWriter , r * http.Request , _ caddyhttp.Handler ) error {
426453 origReq := r .Context ().Value (caddyhttp .OriginalRequestCtxKey ).(http.Request )
427454 repl := r .Context ().Value (caddy .ReplacerCtxKey ).(* caddy.Replacer )
428455
@@ -447,6 +474,7 @@ func (f FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ ca
447474 frankenphp .WithRequestSplitPath (f .SplitPath ),
448475 frankenphp .WithRequestPreparedEnv (env ),
449476 frankenphp .WithOriginalRequest (& origReq ),
477+ frankenphp .WithModuleID (fmt .Sprintf ("%p" , f )),
450478 )
451479
452480 if err != nil {
@@ -466,6 +494,66 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
466494 for d .Next () {
467495 for d .NextBlock (0 ) {
468496 switch d .Val () {
497+ case "worker" :
498+ wc := workerConfig {}
499+ if d .NextArg () {
500+ wc .FileName = d .Val ()
501+ }
502+
503+ if d .NextArg () {
504+ v , err := strconv .Atoi (d .Val ())
505+ if err != nil {
506+ return err
507+ }
508+ wc .Num = v
509+ }
510+
511+ for d .NextBlock (1 ) {
512+ switch d .Val () {
513+ case "name" :
514+ if ! d .NextArg () {
515+ return d .ArgErr ()
516+ }
517+ wc .Name = d .Val ()
518+ case "file" :
519+ if ! d .NextArg () {
520+ return d .ArgErr ()
521+ }
522+ wc .FileName = d .Val ()
523+ case "num" :
524+ if ! d .NextArg () {
525+ return d .ArgErr ()
526+ }
527+ v , err := strconv .Atoi (d .Val ())
528+ if err != nil {
529+ return err
530+ }
531+ wc .Num = v
532+ case "env" :
533+ args := d .RemainingArgs ()
534+ if len (args ) != 2 {
535+ return d .ArgErr ()
536+ }
537+ if wc .Env == nil {
538+ wc .Env = make (map [string ]string )
539+ }
540+ wc .Env [args [0 ]] = args [1 ]
541+ case "watch" :
542+ if ! d .NextArg () {
543+ wc .Watch = append (wc .Watch , "./**/*.{php,yaml,yml,twig,env}" )
544+ } else {
545+ wc .Watch = append (wc .Watch , d .Val ())
546+ }
547+ default :
548+ return fmt .Errorf ("unknown worker subdirective: %s" , d .Val ())
549+ }
550+ }
551+
552+ if wc .FileName == "" {
553+ return errors .New (`the "file" argument must be specified` )
554+ }
555+
556+ f .Workers = append (f .Workers , wc )
469557 case "root" :
470558 if ! d .NextArg () {
471559 return d .ArgErr ()
@@ -505,7 +593,7 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
505593
506594 f .ResolveRootSymlink = & v
507595 default :
508- allowedDirectives := "root, split, env, resolve_root_symlink"
596+ allowedDirectives := "root, split, env, resolve_root_symlink, worker "
509597 return wrongSubDirectiveError ("php or php_server" , allowedDirectives , d .Val ())
510598 }
511599 }
@@ -516,7 +604,7 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
516604
517605// parseCaddyfile unmarshals tokens from h into a new Middleware.
518606func parseCaddyfile (h httpcaddyfile.Helper ) (caddyhttp.MiddlewareHandler , error ) {
519- m := FrankenPHPModule {}
607+ m := & FrankenPHPModule {}
520608 err := m .UnmarshalCaddyfile (h .Dispenser )
521609
522610 return m , err
@@ -753,6 +841,7 @@ func parsePhpServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
753841 // using the php directive syntax
754842 dispenser .Next () // consume the directive name
755843 err = phpsrv .UnmarshalCaddyfile (dispenser )
844+
756845 if err != nil {
757846 return nil , err
758847 }
0 commit comments