Difference between PHP getenv and $_ENV: beware of the subtleties!
Environment variables play a crucial role in modern development, especially for managing sensitive data like API keys or environment-specific configurations. In PHP, there are two primary ways to access these variables: the getenv function and the $_ENV superglobal. While they may seem interchangeable, they behave differently and can cause issues if not used correctly.
Let me share what I’ve learned about the differences, illustrate a real-world example encountered with GitHub Workflows, and provide solutions to avoid potential pitfalls.
Understanding the differences between getenv and $_ENV
getenv
The getenv function accesses the system's environment variables directly. This means it interacts with the environment in which the PHP process is executed, independent of PHP-specific configurations.
Example:
putenv('MY_SECRET_KEY=MY_VALUE');
echo getenv('MY_SECRET_KEY'); // Outputs: MY_VALUE$_ENV
The $_ENV superglobal, on the other hand, depends on the configuration of the php.ini file. Specifically, it is populated only if the variables_order directive includes the letter "E". In some environments (like default configurations on certain servers), $_ENV may be empty.
Example:
putenv('MY_SECRET_KEY=MY_VALUE');
echo $_ENV['MY_SECRET_KEY']; // May not workRequired configuration in php.ini:
variables_order = "GPCS" # Add "E" to include $_ENVThe variables_order directive
The variables_order directive in php.ini controls which superglobals PHP initializes and in what order. It accepts a string of letters, each representing a category of variables:
- G: GET variables (e.g.,
$_GETfrom URLs). - P: POST variables (e.g.,
$_POSTfrom forms). - C: Cookie variables (e.g.,
$_COOKIE). - S: Session variables (e.g.,
$_SESSION). - E: Environment variables (e.g.,
$_ENV).
If the letter E is missing from the directive, PHP will not populate the $_ENV superglobal with environment variables, even if they exist in the system.
Example configurations
Complete configuration
variables_order = "GPCSE"
- All superglobals are initialized.
$_ENVwill be populated with system environment variables.
Partial configuration
variables_order = "GPCS"
- Only
$_GET,$_POST,$_COOKIE, and$_SESSIONare initialized. $_ENVwill remain empty.
Real-world example: GitHub Workflows and automated tests
While using GitHub Actions to automate a project, I encountered an issue during the execution of tests requiring secret keys configured in GitHub. My code attempted to retrieve these values via $_ENV but failed.
After some investigation, I realized that $_ENV was empty in this environment. However, using getenv allowed me to access the variables without any issues.
This got me digging into how getenv and $_ENV really work.
Original (problematic) code:
$secretKey = $_ENV['SECRET_KEY'] ?? null;
if (!$secretKey) {
throw new Exception('Secret key not found');
}Working solution:
$secretKey = getenv('SECRET_KEY');
if (!$secretKey) {
throw new Exception('Secret key not found');
}The case of the vlucas/phpdotenv component
This issue got me thinking about how popular libraries like vlucas/phpdotenv handle environment variables. Curious, I decided to dive deeper into how it works.
The library takes a clever, multi-layered approach to ensure maximum compatibility across different PHP setups. When a .env file is loaded, it doesn’t just stop at one method of making the variables available. It directly populates the $_ENV superglobal by assigning each key-value pair from the file, while also using PHP's native putenv() function to inject those variables into the system's environment. This means they’re instantly accessible through getenv() as well.
Depending on its configuration, phpdotenv can also populate $_SERVER, ensuring flexibility no matter how your code accesses environment variables. This design makes it a robust solution for environments where PHP’s configuration isn’t entirely within your control.
putenv() and thread safety
When using putenv(), keep in mind that it modifies the global environment for the entire process, which can lead to race conditions in multi-threaded applications.
This is not a problem in mono-thread environment, but in multi-threads environment, this might result in unpredictable behavior.
Recommendations
- Prefer getenv for accessing environment variables: Especially in environments where you don’t have full control over the PHP configuration (e.g., CI/CD, shared hosting, etc.).
- Test in the target environment: Differences between local and remote environments are often the source of hard-to-diagnose bugs.
- Using
putenv()is strongly discouraged due to the fact that these functions are not thread safe. - Check the php.ini configuration: If you must use
$_ENV, ensure thatvariables_orderincludes "E" or… - Manually populate $_ENV if needed: you can populate
$_ENVyourself like this:
foreach ($values as $key => $value) {
$_ENV[$key] = $value;
}Conclusion
While getenv and $_ENV may seem similar, their differences can lead to issues, especially in contexts like CI/CD pipelines or shared hosting. By understanding these subtleties and following best practices, you can avoid tricky bugs and ensure better portability for your code.
Have you encountered similar issues or have tips to share?
