nginx / unit

NGINX Unit - universal web app server - a lightweight and versatile open source server that simplifies the application stack by natively executing application code across eight different programming language runtimes.
https://unit.nginx.org
Apache License 2.0
5.37k stars 323 forks source link

[PHP] Allow additional php.ini files instead of overwriting via options.file #969

Closed ruudk closed 12 months ago

ruudk commented 1 year ago

Currently, there is no way to load additional php.ini files.

When you specify options.file, you override the default loaded php.ini completely.

{
  "applications": {
    "hello-world": {
      "type": "php",
      "root": "/www/public",
      "script": "index.php",
      "options": {
        "file": "some-tweaks.ini"
      }
    }
  }
}

When reading the code, it seems this is how it was designed:

https://github.com/nginx/unit/blob/c905d0d393378de2fa44d3e21d2bf317a6c3245c/src/nxt_php_sapi.c#L409-L422

https://github.com/nginx/unit/blob/c905d0d393378de2fa44d3e21d2bf317a6c3245c/src/nxt_php_sapi.c#L671

But, if you want to enable (zend) extensions on a per application basis, this becomes annoying.

You now have to copy the default php.ini, store it in a new file and then append your tweaks to it.

It would be easier if I could do this:

{
  "applications": {
    "hello-world": {
      "type": "php",
      "root": "/www/public",
      "script": "index.php",
      "options": {
        "additional-files": ["some-tweaks.ini"]
      }
    }
  }
}

Or, specify the (additional) extensions that I want to load and let me provide extra config via admin and user:

{
  "applications": {
    "hello-world": {
      "type": "php",
      "root": "/www/public",
      "script": "index.php",
      "options": {
        "extensions": [],
        "zend_extensions": ["xdebug.so"],
        "admin": {
          "xdebug.mode": "debug",
          "xdebug.start_with_request": "trigger",
          "xdebug.client_host": "127.0.0.1",
          "xdebug.idekey": "PHPSTORM"
        }
      }
    }
  }
}
lcrilly commented 12 months ago

Thanks for sharing this use case. If the options/file value was an array of filenames, would that meet your needs? Something like this:

{
  "applications": {
    "hello-world": {
      "type": "php",
      "root": "/www/public",
      "script": "index.php",
      "options": {
        "file": [
            "php.ini",
            "some-tweaks.ini"
        ]
      }
    }
  }
}
ruudk commented 12 months ago

That would be perfect!

ac000 commented 12 months ago

I'm not sure that we can make options.file an array as we can only really set nxt_php_sapi_module.php_ini_path_override once.

nxt_php_sapi_module aka sapi_module_struct is a structure used to pass various bits of config to PHP and php_ini_path_override simply points to a file or directory path.

@ruudk

AFAICT you have two options

1) Put any additional ini files you wanted loaded in the PHP_CONFIG_FILE_SCAN_DIR/--with-config-file-scan-dir directory, e.g /etc/php.d

2) Set the PHP_INI_SCAN_DIR environment variable to point to an alternate directory to scan for ini files (it will still load /etc/php.ini) . E.g

{
    "applications": {
        "hello-world": {
            "type": "php",
            "root": "/www/public",
            "script": "index.php",
            "environment": {
                "PHP_INI_SCAN_DIR": "/tmp/php.inis"
            }
        }
    }
}
tippexs commented 12 months ago

I think the environment variable is a good solution. The tweaked php.ini files can be placed somewhere and loaded dynamically. After checking the docs again, this option can even be used to set multiple directories separated by comma. I will give it a try but from what it looks like this should be something we put in our PHP docs to share this option with others.

ruudk commented 12 months ago

Thanks for the suggestions. I didn't think about using the PHP_INI_SCAN_DIR here!

It works!

{
  "applications": {
    "app": {
      "type": "php",
      "root": "public",
      "script": "index.php",
      "environment": {
          "PHP_INI_SCAN_DIR": ":config/"
      }
    }
}

Because PHP_INI_SCAN_DIR starts with :, this loads the default php.ini and the default scanned files, plus loads additional php.ini in the config directory. The directory can be a relative or absolute path.

If you want to dynamically load Xdebug depending on the ?XDEBUG_TRIGGER=1 value, you can do this:

{
  "settings": {
    "http": {
      "log_route": true
    }
  },
  "listeners": {
    "*:443": {
      "pass": "routes/https",
      "tls": {
        "certificate": "bundle"
      }
    }
  },
  "routes": {
    "https": [
      {
        "match": {
          "arguments": {
              "XDEBUG_TRIGGER": "1"
          }
        },
        "action": {
            "pass": "applications/app-xdebug",
            "response_headers": {
                "With-Xdebug": "true"
            }
        }
      },
      {
        "action": {
          "pass": "applications/app",
            "response_headers": {
                "With-Xdebug": "false"
            }
        }
      }
    ]
  },
  "applications": {
    "app": {
      "type": "php",
      "root": "public",
      "script": "index.php"
    },
    "app-xdebug": {
      "type": "php",
      "root": "public",
      "script": "index.php",
      "environment": {
        "PHP_INI_SCAN_DIR": ":config/directory-that-has-an-ini-file-that-enables-xdebug/"
      }
    }
  }
}

Thanks all!