diff --git a/builders/php/acceptance/flex_test.go b/builders/php/acceptance/flex_test.go index 8b6b8809f..af68d8bb9 100644 --- a/builders/php/acceptance/flex_test.go +++ b/builders/php/acceptance/flex_test.go @@ -21,6 +21,15 @@ func TestAcceptance(t *testing.T) { VersionInclusionConstraint: "< 8.2.0", MustUse: []string{flex, composer}, }, + { + Name: "overridden document root", + App: "front_controller", + VersionInclusionConstraint: "< 8.2.0", + MustUse: []string{flex, composer}, + Env: []string{"NGINX_DOCUMENT_ROOT=public"}, + Path: "/", + MustMatch: "from public dir", + }, { Name: "php ini override", App: "php_ini", diff --git a/builders/testdata/php/flex/front_controller/public/app.php b/builders/testdata/php/flex/front_controller/public/app.php new file mode 100644 index 000000000..5bc6ec810 --- /dev/null +++ b/builders/testdata/php/flex/front_controller/public/app.php @@ -0,0 +1 @@ +from public dir diff --git a/cmd/php/supervisor/main.go b/cmd/php/supervisor/main.go index 3d21f0b69..a96d882ec 100644 --- a/cmd/php/supervisor/main.go +++ b/cmd/php/supervisor/main.go @@ -70,6 +70,11 @@ func buildFn(ctx *gcp.Context) error { overrides := webconfig.OverriddenProperties(ctx, runtimeConfig) webconfig.SetEnvVariables(l, overrides) + err = overrides.UpdateFromEnvironment(ctx) + if err != nil { + return err + } + fpmConfFile, err := writeFpmConfig(l.Path, overrides) if err != nil { return err @@ -164,11 +169,17 @@ func nginxConfig(layer string, overrides webconfig.OverrideProperties) nginx.Con frontController = overrides.FrontController } + root := defaultRoot + if overrides.DocumentRoot != "" { + root = filepath.Join(defaultRoot, overrides.DocumentRoot) + } + nginx := nginx.Config{ Port: defaultNginxPort, FrontControllerScript: frontController, - Root: filepath.Join(defaultRoot, overrides.DocumentRoot), + Root: root, AppListenAddress: defaultAddress, + ServesStaticFiles: overrides.NginxServesStaticFiles, } if overrides.NginxServerConfInclude { @@ -205,6 +216,14 @@ func fpmConfig(layer string, overrides webconfig.OverrideProperties) (nginx.FPMC AddNoDecorateWorkers: true, } + if overrides.PHPFPMDynamicWorkers { + fpm.DynamicWorkers = overrides.PHPFPMDynamicWorkers + } + + if overrides.PHPFPMWorkers > 0 { + fpm.NumWorkers = overrides.PHPFPMWorkers + } + if overrides.PHPFPMOverride { fpm.ConfOverride = overrides.PHPFPMOverrideFileName } diff --git a/cmd/php/webconfig/main.go b/cmd/php/webconfig/main.go index 5dc826b51..bb6d4278c 100644 --- a/cmd/php/webconfig/main.go +++ b/cmd/php/webconfig/main.go @@ -82,16 +82,10 @@ func buildFn(ctx *gcp.Context) error { webconfig.SetEnvVariables(l, overrides) } - if customNginxConf, present := os.LookupEnv(php.CustomNginxConfig); present { - overrides.NginxConfOverride = true - overrides.NginxConfOverrideFileName = filepath.Join(defaultRoot, customNginxConf) - } - - nginxServesStaticFiles, err := env.IsPresentAndTrue(php.NginxServesStaticFiles) + err = overrides.UpdateFromEnvironment(ctx) if err != nil { return err } - overrides.NginxServesStaticFiles = nginxServesStaticFiles fpmConfFile, err := writeFpmConfig(ctx, l.Path, overrides) if err != nil { @@ -206,6 +200,14 @@ func fpmConfig(layer string, addNoDecorateWorkers bool, overrides webconfig.Over fpm.ListenAddress = defaultFlexAddress } + if overrides.PHPFPMDynamicWorkers { + fpm.DynamicWorkers = overrides.PHPFPMDynamicWorkers + } + + if overrides.PHPFPMWorkers > 0 { + fpm.NumWorkers = overrides.PHPFPMWorkers + } + if overrides.PHPFPMOverride { fpm.ConfOverride = overrides.PHPFPMOverrideFileName } diff --git a/pkg/env/env.go b/pkg/env/env.go index a35f31eeb..dfd37ab16 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -194,3 +194,18 @@ func IsPresentAndTrue(varName string) (bool, error) { return parsed, nil } + +// IsPresentAndInt returns true and the integer value if the environment variable evaluates exists and contains an integer. +func IsPresentAndInt(varName string) (bool, int, error) { + varValue, present := os.LookupEnv(varName) + if !present { + return false, 0, nil + } + + parsed, err := strconv.Atoi(varValue) + if err != nil { + return false, 0, fmt.Errorf("parsing %s: %v", varName, err) + } + + return true, parsed, nil +} diff --git a/pkg/php/php.go b/pkg/php/php.go index f4642f0b7..190559f64 100644 --- a/pkg/php/php.go +++ b/pkg/php/php.go @@ -91,8 +91,17 @@ post_max_size = 32M // CustomNginxConfig is an environment variable to pass a custom nginx configuration. CustomNginxConfig = "GOOGLE_CUSTOM_NGINX_CONFIG" - // NginxServesStaticFiles is an environment variable to configure Nginx to serve static files. + // NginxServesStaticFiles is an environment variable to configure nginx to serve static files. NginxServesStaticFiles = "NGINX_SERVES_STATIC_FILES" + + // NginxDocumentRoot is an environment variable to configure the document root of nginx. + NginxDocumentRoot = "NGINX_DOCUMENT_ROOT" + + // PHPFPMDynamicWorkers is an environment variable to enable dynamic workers for php-fpm. + PHPFPMDynamicWorkers = "PHP_FPM_DYNAMIC_WORKERS" + + // PHPFPMWorkerCount is an environment variable to configure the number of php-fpm workers. + PHPFPMWorkerCount = "PHP_FPM_WORKER_COUNT" ) type composerScriptsJSON struct { diff --git a/pkg/webconfig/BUILD.bazel b/pkg/webconfig/BUILD.bazel index 32fb0a09c..7a8a2bbac 100644 --- a/pkg/webconfig/BUILD.bazel +++ b/pkg/webconfig/BUILD.bazel @@ -9,6 +9,7 @@ go_library( ], deps = [ "//pkg/appyaml", + "//pkg/env", "//pkg/gcpbuildpack", "//pkg/php", "@com_github_buildpacks_libcnb//:go_default_library", diff --git a/pkg/webconfig/webconfig.go b/pkg/webconfig/webconfig.go index 50f114fd9..d4bdb90a4 100644 --- a/pkg/webconfig/webconfig.go +++ b/pkg/webconfig/webconfig.go @@ -2,9 +2,12 @@ package webconfig import ( + "fmt" + "os" "path/filepath" "github.com/GoogleCloudPlatform/buildpacks/pkg/appyaml" + "github.com/GoogleCloudPlatform/buildpacks/pkg/env" gcp "github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack" "github.com/GoogleCloudPlatform/buildpacks/pkg/php" "github.com/buildpacks/libcnb" @@ -44,6 +47,10 @@ type OverrideProperties struct { NginxHTTPInclude bool // NginxHTTPIncludeFileName name of the partial nginx config to be included in the http section. NginxHTTPIncludeFileName string + // PHPFPMDynamicWorkers boolean to toggle dynamic workers in the php-fpm config file. + PHPFPMDynamicWorkers bool + // PHPFPMWorkers integer to specify the worker thread count in the php-fpm config file. + PHPFPMWorkers int // PHPFPMOverride boolean to check if user-provided php-fpm config exists. PHPFPMOverride bool // PHPFPMOverrideFileName name of the user-provided php-fpm config file. @@ -56,6 +63,44 @@ type OverrideProperties struct { NginxServesStaticFiles bool } +func (overrides *OverrideProperties) UpdateFromEnvironment(ctx *gcp.Context) error { + if customNginxConf, present := os.LookupEnv(php.CustomNginxConfig); present { + overrides.NginxConfOverride = true + overrides.NginxConfOverrideFileName = filepath.Join(defaultRoot, customNginxConf) + } + + nginxServesStaticFiles, err := env.IsPresentAndTrue(php.NginxServesStaticFiles) + if err != nil { + return err + } + overrides.NginxServesStaticFiles = nginxServesStaticFiles + + nginxDocumentRoot := os.Getenv(php.NginxDocumentRoot) + if nginxDocumentRoot != "" { + overrides.DocumentRoot = nginxDocumentRoot + } + + phpFPMDynamicWorkers, err := env.IsPresentAndTrue(php.PHPFPMDynamicWorkers) + if err != nil { + return err + } + overrides.PHPFPMDynamicWorkers = phpFPMDynamicWorkers + + phpFPMWorkerCountPresent, phpFPMWorkerCount, err := env.IsPresentAndInt(php.PHPFPMWorkerCount) + if err != nil { + return err + } + if phpFPMWorkerCountPresent { + if phpFPMWorkerCount > 0 { + overrides.PHPFPMWorkers = phpFPMWorkerCount + } else { + return fmt.Errorf("invalid %s value, must be greater than 0", php.PHPFPMWorkerCount) + } + } + + return nil +} + // OverriddenProperties returns whether the property has been overridden and the path to the file. func OverriddenProperties(ctx *gcp.Context, runtimeConfig appyaml.RuntimeConfig) OverrideProperties { phpIniOverride, phpIniOverrideFileName := overrideProperties(ctx, runtimeConfig.PHPIniOverride, defaultPHPIni)