fix: Fix environment variables not being captured correctly in PHP-FPM
- Fix priority order in loadSystemEnvironment() to check and first - Add dynamic fallback in Environment::get() to handle variables set after initialization - Ensure all environment variables are captured during bootstrap, including those set dynamically by PHP-FPM/FastCGI Fixes issue where environment variables like RAPIDMAIL_USERNAME and RAPIDMAIL_PASSWORD were missing during bootstrap but available later in request processing.
This commit is contained in:
@@ -151,36 +151,55 @@ final readonly class EncryptedEnvLoader
|
|||||||
* Load variables from system environment (getenv(), $_ENV, $_SERVER)
|
* Load variables from system environment (getenv(), $_ENV, $_SERVER)
|
||||||
*
|
*
|
||||||
* Priority order optimized for PHP-FPM compatibility:
|
* Priority order optimized for PHP-FPM compatibility:
|
||||||
* 1. getenv() - Works in PHP-FPM, reads from actual process environment
|
* 1. $_ENV - Contains environment variables set by PHP-FPM/FastCGI
|
||||||
* 2. $_ENV - May be empty in PHP-FPM
|
* 2. $_SERVER - May contain additional vars from web server (including HTTP_* headers)
|
||||||
* 3. $_SERVER - May contain additional vars from web server
|
* 3. getenv() - Fallback to read from actual process environment
|
||||||
|
*
|
||||||
|
* IMPORTANT: In PHP-FPM, some environment variables are set dynamically
|
||||||
|
* during request processing, so we must check $_ENV and $_SERVER first.
|
||||||
*/
|
*/
|
||||||
private function loadSystemEnvironment(): array
|
private function loadSystemEnvironment(): array
|
||||||
{
|
{
|
||||||
$variables = [];
|
$variables = [];
|
||||||
|
|
||||||
// 1. Load from getenv() - THIS WORKS IN PHP-FPM!
|
// 1. Load from $_ENV first (contains dynamically set vars in PHP-FPM)
|
||||||
// In PHP-FPM, $_ENV and $_SERVER are empty by default.
|
// In PHP-FPM, environment variables are often set in $_ENV during request processing
|
||||||
// getenv() reads from actual process environment, unlike $_ENV
|
foreach ($_ENV as $key => $value) {
|
||||||
|
// Only include actual environment variables, not all superglobals
|
||||||
|
// Filter out HTTP_* headers and other non-env vars
|
||||||
|
if (is_string($key) && is_string($value) && !str_starts_with($key, 'HTTP_')) {
|
||||||
|
$variables[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Load from $_SERVER (may contain additional vars from web server)
|
||||||
|
// Filter to only include non-HTTP variables that look like environment variables
|
||||||
|
foreach ($_SERVER as $key => $value) {
|
||||||
|
if (!isset($variables[$key]) &&
|
||||||
|
is_string($key) &&
|
||||||
|
is_string($value) &&
|
||||||
|
!str_starts_with($key, 'HTTP_') &&
|
||||||
|
!in_array($key, ['GATEWAY_INTERFACE', 'SERVER_SOFTWARE', 'SERVER_NAME', 'SERVER_ADDR',
|
||||||
|
'SERVER_PORT', 'REQUEST_URI', 'REQUEST_METHOD', 'QUERY_STRING',
|
||||||
|
'CONTENT_TYPE', 'CONTENT_LENGTH', 'SCRIPT_NAME', 'SCRIPT_FILENAME',
|
||||||
|
'PATH_INFO', 'FCGI_ROLE', 'REDIRECT_STATUS', 'REQUEST_TIME',
|
||||||
|
'REQUEST_TIME_FLOAT', 'DOCUMENT_ROOT', 'DOCUMENT_URI',
|
||||||
|
'REMOTE_ADDR', 'REMOTE_PORT', 'REMOTE_USER'], true)) {
|
||||||
|
$variables[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Load from getenv() - Fallback for process environment variables
|
||||||
|
// In PHP-FPM, $_ENV and $_SERVER are often populated, but getenv() provides process-level vars
|
||||||
$allEnvVars = getenv();
|
$allEnvVars = getenv();
|
||||||
if ($allEnvVars !== false) {
|
if ($allEnvVars !== false) {
|
||||||
foreach ($allEnvVars as $key => $value) {
|
foreach ($allEnvVars as $key => $value) {
|
||||||
$variables[$key] = $value;
|
// Only override if not already set from $_ENV or $_SERVER
|
||||||
}
|
// This ensures dynamic vars from PHP-FPM take precedence
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Load from $_ENV (may be empty in PHP-FPM)
|
|
||||||
foreach ($_ENV as $key => $value) {
|
|
||||||
if (!isset($variables[$key])) {
|
if (!isset($variables[$key])) {
|
||||||
$variables[$key] = $value;
|
$variables[$key] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Load from $_SERVER (may contain additional vars from web server)
|
|
||||||
foreach ($_SERVER as $key => $value) {
|
|
||||||
if (!isset($variables[$key]) && is_string($value)) {
|
|
||||||
$variables[$key] = $value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $variables;
|
return $variables;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ final readonly class Environment
|
|||||||
{
|
{
|
||||||
$key = $this->keyToString($key);
|
$key = $this->keyToString($key);
|
||||||
|
|
||||||
// 1. Check if direct env var exists
|
// 1. Check if direct env var exists in internal array
|
||||||
if (isset($this->variables[$key])) {
|
if (isset($this->variables[$key])) {
|
||||||
return $this->variables[$key];
|
return $this->variables[$key];
|
||||||
}
|
}
|
||||||
@@ -34,10 +34,45 @@ final readonly class Environment
|
|||||||
return $secretValue;
|
return $secretValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Return default
|
// 3. Fallback: Check system environment dynamically
|
||||||
|
// This handles cases where environment variables are set after Environment initialization
|
||||||
|
// (common in PHP-FPM where vars may be set during request processing)
|
||||||
|
$systemValue = $this->getFromSystemEnvironment($key);
|
||||||
|
if ($systemValue !== null) {
|
||||||
|
return $systemValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Return default
|
||||||
return $default;
|
return $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get variable from system environment as fallback
|
||||||
|
*
|
||||||
|
* This ensures we can access environment variables that were set
|
||||||
|
* after Environment initialization (e.g., by PHP-FPM/FastCGI)
|
||||||
|
*/
|
||||||
|
private function getFromSystemEnvironment(string $key): ?string
|
||||||
|
{
|
||||||
|
// Priority: $_ENV > $_SERVER > getenv()
|
||||||
|
// $_ENV and $_SERVER may contain dynamically set vars in PHP-FPM
|
||||||
|
|
||||||
|
if (isset($_ENV[$key]) && is_string($_ENV[$key])) {
|
||||||
|
return $_ENV[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_SERVER[$key]) && is_string($_SERVER[$key]) && !str_starts_with($key, 'HTTP_')) {
|
||||||
|
return $_SERVER[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = getenv($key);
|
||||||
|
if ($value !== false && is_string($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public function getRequired(EnvKey|string $key): mixed
|
public function getRequired(EnvKey|string $key): mixed
|
||||||
{
|
{
|
||||||
$key = $this->keyToString($key);
|
$key = $this->keyToString($key);
|
||||||
@@ -115,11 +150,7 @@ final readonly class Environment
|
|||||||
*/
|
*/
|
||||||
public function all(): array
|
public function all(): array
|
||||||
{
|
{
|
||||||
return array_merge(
|
return $this->variables;
|
||||||
#$_ENV,
|
|
||||||
#$_SERVER,
|
|
||||||
$this->variables
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user