Почему putenv() необходим для уже заданной переменной среды?
Когда php
используется как модуль apache
, переменная среды, исходящая из директивы apache SetEnv
, доступна для php getenv()
, но она не представляется доступной для C
расширений через stdlib getenv()
. По крайней мере, это происходит с модулем pgsql
.
Если переменная повторно установлена с помощью php-кода:
putenv("varname=".getenv("varname"));
тогда он становится доступным для кода расширения.
Вопрос: зачем нужна переустановка? Как основная среда php отличается от "стандартной" (stdlib
) среды?
Это происходит с помощью PHP Version 5.3.10-1ubuntu3.17
в Ubuntu 12.04, в качестве модуля apache. При запуске из командной строки это обходное решение не требуется.
Из этого другого вопроса: Использование .pgpass из Apache libphp5.so кажется, что это обходное решение также необходимо для php-5.4 под FreeBSD, поэтому это не просто Ubuntu или php-5.3.
Это не зависит от variables_order
, имеющего E
. Я пробовал как EGPCS
, так и GPCS
, а $_ENV
не заполняется, если E
не существует, как и ожидалось, но это не меняет результат getenv()
, так как документально или, по-видимому, результат stdlib getenv()
из внутренних расширений.
Демонстрация проблемы с помощью модуля pgsql
. Он построен поверх общей библиотеки libpq
, написанной в C
, которая вызывает getenv()
в нескольких необязательных переменных среды PG*
.
В файле конфигурации apache, под <VirtualHost>
, я устанавливаю это, чтобы попытки подключения не выполнялись:
SetEnv PGHOST doesnotexist
и не указывая хост в вызове pg_connect
, поэтому PGHOST
следует принимать, когда он присутствует.
Первая попытка:
$v=getenv("PGHOST");
echo "PGHOST=$v\n";
$cnx=pg_connect("user=daniel");
if ($cnx) {
echo "Connection is successful.";
}
Результат:
PGHOST=doesnotexist
Connection is successful.
Итак, PGHOST
игнорируется, несмотря на то, что он находится в среде.
Вторая попытка, теперь снова помещая PGHOST
в среду, даже если она уже существует:
$v=getenv("PGHOST");
echo "PGHOST=$v\n";
putenv("PGHOST=".getenv("PGHOST"));
$cnx=pg_connect("user=daniel");
if ($cnx) {
echo "Connection is successful.";
}
Результат (невозможность подключения к указанному хосту, как ожидалось):
PGHOST=doesnotexist
Warning: pg_connect(): Unable to connect to PostgreSQL server:
could not translate host name "doesnotexist" to address:
Name or service not known in /var/www/test/pgtest2.php on line 8
Ответы
Ответ 1
Причина такова:
Значения среды, которые вы получаете от getenv()[PHP]
(функция php), отличаются от среды, которую вы запрашиваете с помощью getenv()[C]
(функция C lib). Что getenv()[PHP]
делает, проверяет с зарегистрированным sapi для соответствия (http://lxr.php.net/xref/PHP_5_6/ext/standard/basic_functions.c#3999).
Apache2 sapi делает это через свой собственный контекст среды (http://lxr.php.net/xref/PHP_5_6/sapi/apache2handler/sapi_apache2.c#253), а не стандартную среду ОС из самого процесса apache.
ТОЛЬКО, когда найдено совпадение не найдено, оно будет проверяться в среде фактического процесса. Вот почему getenv()[PHP]
возвращает значение, но getenv()[C]
не делает.
Теперь "хак" также прост: putenv()[PHP]
хранит заданный ключ/значение в среде запущенного процесса, поэтому его можно найти позже getenv()[C]
.