How do I use putenv() to update existing environment variable?
Asked Answered
K

2

18

Edit: as far as I can tell, my question is because of a defect in PHP. I've copied this question to the PHP bug tracker here: https://bugs.php.net/bug.php?id=74143 and plan to try and implement a fix.


The putenv function sets the value of an environment variable. According to the manual, putenv returns true on success, false on failure.

However, I'm finding that the putenv function sometimes returns true without updating the environment variable for the current session.

To reproduce this issue, set an environment variable in a webserver using PHP FPM, by using the fastcgi_param directive. This is incredibly useful, as it allows setting environment variables in isolation to other hosts on the same server.

Example nginx.conf:

location ~ \.php$ {
        fastcgi_pass    unix:/var/run/php/php7.0-fpm.sock;
        fastcgi_param   TESTVAR_ENV     old-value;
        include         fastcgi_params;
}

Example test.php:

var_dump(getenv("TESTVAR_ENV"));
var_dump(putenv("TESTVAR_ENV=new-value"));
var_dump(getenv("TESTVAR_ENV"));

Output of test.php:

string(12) "old-value"
bool(true)
string(12) "old-value"

As you can see:

  1. the existing value is read by getenv successfully,
  2. the putenv function returns true, indicating success,
  3. the new value is not actually set, which is incredibly confusing.

Am I misunderstanding what the purpose of the putenv function is? Is there some missing documentation on the setenv manual page? How do I use putenv() to update existing environment variable?

Kaltman answered 21/2, 2017 at 23:16 Comment(6)
I've not seen this behaviour myself. Have you tried removing the old value first? putenv("TESTVAR_ENV") should clear the value, or maybe try using $_SERVER instead?Versatile
Actually, just tested this with Nginx and PHP-FPM, instead of CLI and I'm seeing the same thing.Versatile
Do you think this is a bug with PHP, or PHP-FPM?Kaltman
Well, I just tried it in Apache with mod_php and get the same behaviour.Versatile
Using $_SERVER works as expected.Versatile
The php.net page says Returns TRUE on success or FALSE on failure. I can't convince myself that "failing" to change the value of an existing var is anything other than a failure... making this a php defect.Condition
A
9

This is interesting. After investigating i found that there's an undocumented parameter for getenv().

Calling putenv("TESTVAR_ENV=new-value") followed by getenv("TESTVAR_ENV", true) returns new-value as expected. However getenv("TESTVAR_ENV", true) returns false when called without explicitly setting the value first.

Reading from the source it seems that if local_only is set to false (the default), the value is fetched using sapi_getenv, whereas with local_only set to true the native getenv is used.

Furthermore, if sapi_getenv doesn't return a value, then getenv is called as a fallback. Meaning if you don't set TESTVAR_ENV in nginx/Apache configuration at all, putenv/getenv works as expected.

So to recap:

  • getenv(name) searches from SAPI's (php-fpm) internal environment table, and fallbacks to OS's environment if variable is not set.
  • getenv(name, true) searches only from OS's environment, which doesn't necessarily (depending on the SAPI) contain variables registered in the web server's configuration.
  • putenv() always only updates OS's environment.

I used the following to test this:

header("Content-Type: text/plain");

dump_env();
echo 'getenv("TESTVAR_ENV") => ' .
    var_export(getenv("TESTVAR_ENV"), true) . "\n";
echo 'getenv("TESTVAR_ENV", true) => ' .
    var_export(getenv("TESTVAR_ENV", true), true) . "\n";
echo "-----------\n";
echo 'putenv("TESTVAR_ENV=new-value") => ' . 
    var_export(putenv("TESTVAR_ENV=new-value"), true) . "\n";
dump_env();
echo 'getenv("TESTVAR_ENV") => ' .
    var_export(getenv("TESTVAR_ENV"), true) . "\n";
echo 'getenv("TESTVAR_ENV", true) => ' .
    var_export(getenv("TESTVAR_ENV", true), true) . "\n";

function dump_env() {
    echo "--- env ---\n" . `env` . "-----------\n";
}
Available answered 22/2, 2017 at 11:15 Comment(2)
Thanks for your input on this. I've updated the documentation on php.net to reflect this behaviour, although I still think the functionality could be improved somewhat.Kaltman
passing 'true' to getenv gives this error in apache/error_log: PHP Warning: getenv() expects exactly 1 parameter, 2 givenTravesty
I
0

You can set a new variable into $_ENV or $_SERVER directly:

/**
 * Return specified environment variable set in `.env` file
 */
function env(string $varname = '')
{
    return $varname ? $_ENV[$varname] : $_ENV;
}

/**
 * Set environment variable
 */
function setenv(string $key, string $value)
{
    return $_ENV[$key] = $value;
}

setenv("APP_ROOT_DIR", "YOUR_APPLICATION_ROOT_DIR"); // Sets the variable into $_ENV

echo env("APP_ROOT_DIR"); // Returns specified variable

print_r(env()); // Return the whole $_ENV array

Or:
You can even use this package: PHP Environment variables manager (php dotenv)

Inrush answered 15/8, 2021 at 13:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.