Различие между PHP getenv() и $_ENV

Источник: «Difference between PHP getenv and $_ENV: beware of the subtleties!»
Переменные среды играют важную роль в современной разработке, в частности, для управления конфиденциальными данными, такими как ключи API или специфические конфигурации среды. В PHP существует два основных способа доступа к этим переменным: функция getenv и суперглобальная переменная $_ENV. Хотя они могут показаться взаимозаменяемыми, но ведут себя по-разному и могут создавать проблемы при неправильном использовании.

Различие между getenv и $_ENV

getenv

Функция getenv напрямую обращается к системным переменным среды. Это означает, что она взаимодействует с окружением, в котором выполняется PHP процесс, независимо от конфигураций, специфичных для PHP.

putenv('MY_SECRET_KEY=MY_VALUE');
echo getenv('MY_SECRET_KEY'); // Выводит: MY_VALUE

$_ENV

С другой стороны, суперглобальная переменная $_ENV зависит от конфигурации php.ini файла. В частности, она заполняется только в том случае, если директива variables_order содержит букву E. В некоторых средах (например, в конфигурациях по умолчанию на некоторых серверах) $_ENV может быть пустой.

putenv('MY_SECRET_KEY=MY_VALUE');
echo $_ENV['MY_SECRET_KEY']; // Может не работать

Требуется настройка в файле php.ini:

variables_order = "GPCS" # Добавьте "E" для включения $_ENV

Директива variables_order

Директива variables_order в php.ini управляет тем, какие суперглобалы PHP инициализирует и в каком порядке. Она принимает строку букв, каждая из которых представляет категорию переменных:

Пример конфигурации

Полная конфигурация

variables_order = "GPCSE"

Частичная конфигурация

variables_order = "GPCS"

Реальный пример: Рабочие процессы GitHub и автоматизированные тесты

При использовании GitHub Actions для автоматизации проекта я столкнулся с проблемой во время выполнения тестов, требующих секретные ключи, настроенные в GitHub. Мой код попытался получить эти значения через $_ENV, но потерпел неудачу.

После изучения ситуации я понял, что $_ENV в этом окружении пуст. Однако использование getenv позволило получить доступ к переменным без проблем.

Это заставило меня углубиться в изучение того, как работают getenv и $_ENV.

Исходный (проблемный) код:

$secretKey = $_ENV['SECRET_KEY'] ?? null;
if (!$secretKey) {
throw new Exception('Secret key not found');
}

Рабочее решение:

$secretKey = getenv('SECRET_KEY');
if (!$secretKey) {
throw new Exception('Secret key not found');
}

Компонент vlucas/phpdotenv

Этот вопрос заставил задуматься, как популярные библиотеки вроде vlucas/phpdotenv работают с переменными среды. Любопытствуя, решил поглубже разобраться, как это работает.

Библиотека использует умный, многослойный подход для обеспечения максимальной совместимости с различными настройками PHP. При загрузке .env файла она не останавливается на одном способе сделать переменные доступными. Он напрямую заполняет суперглобальную переменную $_ENV, присваивая ей каждую пару ключ/значение из файла, а также использует встроенную функцию PHP putenv() для внедрения этих переменных в окружение системы. Это означает, что они будут мгновенно доступны и через getenv().

В зависимости от конфигурации phpdotenv может также заполнять $_SERVER, обеспечивая гибкость независимо от того, как именно код обращается к переменным среды. Такая конструкция делает его надёжным решением для сред, где конфигурация PHP вами не контролируется.

putenv() и потокобезопасность

При использовании putenv() следует помнить, что она изменяет глобальное окружение для всего процесса, что может привести к возникновению условий гонки в многопоточных приложениях.

Это не представляет проблемы в однопоточной среде, но в многопоточной среде может привести к непредсказуемому поведению.

Рекомендации

Заключение

Хотя getenv и $_ENV кажутся похожими, их различия могут привести к проблемам, особенно в таких контекстах, как конвейеры CI/CD или виртуальный хостинг. Понимая эти тонкости и следуя рекомендациям, можно избежать коварных багов и обеспечить лучшую переносимость кода.

Сталкивались ли вы с подобными проблемами или хотите поделиться советами?

Комментарии


Дополнительные материалы

Предыдущая Статья

Руководство по вебхукам в Laravel

Следующая Статья

Кэширование аутентифицированных пользователей в Laravel