Глава 4 · С нуля → диагностика
Конфигурация PHP: найти настройку, понять область и проверить результат
Активный php.ini и scan-ini, синтаксис и единицы, ini_set и режимы INI_*, CLI/FPM, ошибки разработки и сайта, .user.ini, .htaccess и условные секции.
Конфигурация: правила для запуска, а не исходный код
Одна машина — разные источники настроек
Сначала выбираем процесс, поведение которого проверяем. Путь к одному ini ещё не описывает весь результат.
- Команда CLI
Выбранный php.exe → его основной и дополнительные ini → параметры запуска → разрешённые изменения кода.
- Запрос сайта
Его веб-SAPI → собственные ini и настройки сервера/каталога → разрешённые изменения кода; admin-запреты сохраняются.
- Проверяем в том же процессе
PHP_SAPI и версия: кто работает. Загруженные ini: откуда настройки. ini_get: что действует сейчас.
Здесь сопоставлены контексты, а не задан универсальный порядок всех конфигурационных механизмов. .user.ini, .htaccess и FPM применяются при разных условиях.
PHP уже исполняет файл. Теперь нужно понять, почему локально видна ошибка, на сайте — пустой ответ, а изменение лимита будто игнорируется. Конфигурация задаёт правила для PHP: часовой пояс по умолчанию, показ ошибок, ограничения и загрузку расширений. Файл php.ini содержит такие правила в специальном текстовом формате.
Представьте рабочее место с листом настроек: какой исполнитель пришёл, тот и читает свой лист. CLI и веб-процесс могут читать разные листы. Аналогия ограничена: кроме главного файла есть дополнительные файлы, настройки SAPI, администратора и разрешённые временные изменения из программы.
| Вопрос | Наблюдаемый ответ |
|---|---|
| Какой PHP выполняет работу? | Путь запуска, версия, PHP_SAPI |
| Откуда пришли настройки? | Основной ini, дополнительные ini, настройки выбранного SAPI |
| Что действует сейчас? | ini_get нужной директивы в том же процессе |
| Можно ли изменить это здесь? | Режим INI_* и ограничения администратора |
Сначала прочитаем одну настройку, затем разберём её изменение. До работы с конфигурацией пройдите запуск конкретного PHP и hello.php. Для первых команд не нужны собственные функции, массивы или веб-сервер.
Найти действительно загруженный php.ini
Не выбирайте первый найденный поиском файл php.ini. На компьютере могут жить несколько версий PHP, шаблоны php.ini-development / php.ini-production и конфигурации отдельных SAPI. Файл шаблона не становится активным из-за наличия на диске.
- Полный путь к php.exe
- --ini сообщает основной и дополнительные файлы
- В выбранном процессе читаем нужную директиву
& 'C:\Tools\PHP\php.exe' -v
& 'C:\Tools\PHP\php.exe' --ini
Предусловие: это подтверждённый путь к установленному PHP. В выводе --ini различайте Configuration File Path (каталог поиска), Loaded Configuration File (реально загруженный основной файл) и Additional .ini files parsed (дополнительные файлы). Пустой Loaded Configuration File или (none) не означает, что PHP неисправен. Конкретные пути зависят от установки.
Если основной файл отсутствует, PHP использует встроенные значения и применимые другие источники. Для собственного учебного окружения можно подготовить отдельный lesson.ini и выбрать его через -c, как ниже. Для рабочего сервера сначала установите владельца конфигурации и способ её применения.
Вопрос «какой php.ini используется?» встречается в этом обсуждении. Ответ должен содержать способ запуска: --ini описывает CLI, а не автоматически сайт.
& 'C:\Tools\PHP\php.exe' -n 'C:\PhpLessons\charset.php'
Предусловие: charset.php содержит следующий самостоятельный пример. -n отключает загрузку php.ini и дополнительных ini для этого запуска, не удаляя файлы. Поэтому могут исчезнуть подключаемые расширения. Это контрольный опыт для CLI, а не способ исправить конфигурацию сайта.
<?php
echo ini_get('default_charset'), "\n";
Ожидаемый результат
UTF-8
Ожидаемое значение относится к запуску PHP 8.2+ с -n, как в предыдущей команде. При обычном запуске настройка может быть переопределена. ini_get — готовая функция: ей передают имя директивы строкой, она возвращает текущее строковое значение либо false для неизвестной директивы. Это чтение, а не изменение кодировки исходного файла.
Имя default_charset заключено в кавычки, потому что передаётся как текст. Скобки означают вызов функции. echo выводит возвращённое значение и перевод строки. UTF-8 здесь ожидается благодаря явно указанному -n. Это проверка исходного состояния, после неё возвращайтесь к обычному запуску для диагностики своего окружения.
Синтаксис ini: имя, значение, комментарий и единица
Сохраните отдельный lesson.ini для учебных опытов. Каждая строка присваивает значение директиве. Имена здесь не придумывают: memory_limit и date.timezone уже определены PHP или расширением. PHP-код вроде echo в ini не выполняется.
| Фрагмент | Как читать |
|---|---|
| имя = значение | Одна директива; пробелы вокруг = для удобства |
| ; пояснение | Комментарий до конца строки |
| "UTC" | Строковое значение в кавычках |
| On / Off | Включено / выключено для логической директивы |
| 128M | Размер с единицей, если данная директива поддерживает размеры |
| [PHP] или [Date] | Обычная группировка для читателя; сама по себе не ограничивает действие настройкой модуля |
; Только отдельная учебная конфигурация.
default_charset = "UTF-8"
date.timezone = "UTC"
memory_limit = 128M
display_errors = On
log_errors = Off
Это синтаксический пример настроек, не целиком готовый профиль рабочего сайта. Слева точное имя директивы, справа значение; знака $ и PHP-тега нет. ; начинает комментарий вне кавычек. UTC — часовой пояс, 128M — ограничение памяти PHP, On/Off — логические значения для соответствующих директив. Смысл и возможность изменения проверяют у каждой директивы отдельно.
Не переносите в ini способы экранирования из PHP-строк. Кавычки помогают сохранить текстовые значения с особыми символами; обратная косая черта здесь не работает как универсальное PHP-экранирование. Для Windows-путей в примерах используем прямые слеши. Не меняйте кодировку исходного файла, думая, что default_charset перекодирует уже существующие байты.
<?php
echo ini_parse_quantity('8M'), "\n";
echo ini_parse_quantity('512K'), "\n";
Ожидаемый результат
8388608
524288
ini_parse_quantity существует с PHP 8.2. В синтаксисе размеров K, M, G используют множители 1024. Функция возвращает целое количество байтов; простое приведение строки "8M" к числу теряет единицу. Здесь только заранее корректные положительные значения. Специальное -1 имеет смысл лишь у тех директив, которые его разрешают; функция не решает эту семантику вместо документации.
Читать размер надо вместе с правилом конкретной директивы: memory_limit — ограничение выделяемой скриптом памяти PHP, а не объём всей оперативной памяти машины. Повышение лимита не ускоряет алгоритм автоматически. Причину роста расхода исследуют измерениями.
Основной файл, scan-ini и параметры одного запуска
При старте PHP ищет основной ini по правилам SAPI и платформы. Явный -c у CLI позволяет выбрать нужный файл. На поиск также влияют PHPRC, в Windows — документированные ключи реестра и пути, у сервера — его настройки. CLI не берёт php.ini из текущего рабочего каталога просто потому, что вы туда перешли.
Если в пути поиска имеется php-SAPI.ini, например php-cli.ini, он используется вместо php.ini для этого SAPI. После основного файла PHP может читать дополнительные .ini из каталога сканирования в алфавитном порядке. Поэтому значение в основном файле может не быть итоговым.
- lesson.ini: default_charset = UTF-8
- Запуск с -c выбирает этот основной файл; --ini показывает также scan-ini
- -d default_charset=ISO-8859-1 задаёт значение данному CLI-процессу
- charset.php читает текущее значение: ISO-8859-1
& 'C:\Tools\PHP\php.exe' -c 'C:\PhpLessons\lesson.ini' --ini
& 'C:\Tools\PHP\php.exe' -c 'C:\PhpLessons\lesson.ini' -d default_charset=ISO-8859-1 'C:\PhpLessons\charset.php'
Предусловия: lesson.ini существует и содержит предыдущий фрагмент, charset.php — пример чтения. -c выбирает основной файл, -d задаёт одну директиву для этого процесса; ожидаемая строка второй команды — ISO-8859-1. Дополнительные scan-ini всё ещё могут участвовать в запуске с -c: проверяйте --ini. Команды не редактируют общий php.ini. -n и -c здесь не совмещаются.
PHP_INI_SCAN_DIR меняет каталоги сканирования для процесса; разделитель каталогов — ; на Windows и : на Unix. Пустой элемент списка может вернуть также каталог, заданный при сборке. Это причина внимательно читать полный вывод --ini, а не только Loaded Configuration File. В этой главе переменные системы и реестр не меняются.
Модуль PHP читает ini при запуске веб-сервера; CLI и CGI — при каждом запуске. После правки конфигурации долгоживущего сервиса нужен предусмотренный его окружением reload/restart и проверка новых процессов. Универсальная команда перезапуска всех серверов здесь была бы неверной.
Полный порядок поиска по платформам и реестру сохранён в официальном описании configuration.file; здесь освоен проверяемый выбор CLI-файла.
Почему CLI и сайт показывают разные значения
php --ini выполняет CLI. Запрос сайта обслуживает другой процесс, иногда другой версии, с другими модулями и каталогами настроек. Даже одинаковый путь к основному php.ini не доказывает равенства итоговых настроек: остаются scan-ini и уровни сервера. Это предмет обсуждения о расхождении php -i и phpinfo.
| Нужно сравнить | CLI | Закрытая диагностика сайта |
|---|---|---|
| Версия и SAPI | php -v; PHP_SAPI | PHP_VERSION и PHP_SAPI в обслуживающем процессе |
| Прочитанные файлы | php --ini | php_ini_loaded_file и php_ini_scanned_files |
| Нужная настройка | ini_get в CLI-файле | ini_get той же директивы при HTTP-запросе |
Следующий код предполагает, что уже изучены переменная и присваивание и проверка условия. $loaded хранит результат чтения пути; === false отличает отсутствие основного ini от строки с путём. echo выводит plain text: HTML-разметка здесь не создаётся.
<?php
header('Content-Type: text/plain; charset=UTF-8');
echo "SAPI: ", PHP_SAPI, "\n";
echo "Version: ", PHP_VERSION, "\n";
$loaded = php_ini_loaded_file();
if ($loaded === false) {
echo "Main ini: none\n";
} else {
echo "Main ini: ", $loaded, "\n";
}
echo "Additional ini: ", php_ini_scanned_files(), "\n";
echo "Charset: ", ini_get('default_charset'), "\n";
Полный PHP-файл для закрытой локальной диагностики. Предусловия: уже настроенный нужный веб-SAPI и доступ только разработчику. Вывод содержит фактическую версию, SAPI и пути, поэтому точный текст зависит от окружения. Здесь выполнен lint; FPM/Apache не запускались. После диагностики файл удаляют из доступного по HTTP каталога. Не публикуйте phpinfo или полный набор путей/настроек.
Не пытайтесь заставить CLI «стать FPM», передав ему случайный серверный ini. Это не меняет его SAPI, права, окружение и обработчик HTTP. Сначала проверьте настройку в том процессе, поведение которого исправляете.
ini_set: временная настройка и явная проверка отказа
Иногда одному действию нужна разрешённая временная настройка. ini_set получает два аргумента: точное имя директивы и новое значение. Он возвращает старое значение при успехе или false при отказе. Изменение относится к текущему выполнению; файл php.ini на диске не редактируется.
$before — переменная со старым значением; if выбирает ветвь по результату. Перед этой ступенью прочитайте присваивание и if. Здесь эти конструкции используются, чтобы отличить изменение от отказа и не выдавать желаемое за действительное.
- ini_set получает имя и новую строку
- Если вернул false — сообщаем отказ
- При успехе читаем новое значение
- Возвращаем сохранённое старое значение
<?php
$before = ini_set('default_charset', 'ISO-8859-1');
if ($before === false) {
echo "Настройка не изменена\n";
} else {
echo "Временно: ", ini_get('default_charset'), "\n";
ini_set('default_charset', $before);
echo "Возврат: ", ini_get('default_charset'), "\n";
}
Ожидаемый результат
Временно: ISO-8859-1
Возврат: UTF-8
Проверяемый запуск с -n начинается с UTF-8. ini_set возвращает прежнюю строку либо false при отказе, поэтому сравнение строгое: === false. Нельзя путать отказ со старым значением "0". Здесь меняется только настройка процесса; сами байты исходника не перекодируются. В администраторски ограниченном окружении возможна ветвь отказа.
ini_restore возвращает значение из исходной конфигурации, а не обязательно то, которое установил внешний вызывающий код минуту назад. Для вложенной временной смены сохраняйте результат ini_set и восстанавливайте именно его. В долгоживущем CLI-процессе изменение способно затронуть следующие задания; завершение одной вашей функции не возвращает настройку автоматически.
Не подавляйте отказ через @. Проблема может быть в режиме директивы, неизвестном имени или запрете администратора. ini_get показывает принятое значение, но для некоторых настроек важны ещё ограничения и момент обработки запроса.
INI_USER, INI_PERDIR, INI_SYSTEM и INI_ALL
Режим изменения — разрешённый уровень настройки конкретной директивы. Это не пользователь Windows и не право редактирования файла. Если у вас есть доступ к php.ini, это ещё не значит, что любую настройку можно изменить из PHP-кода.
| Режим | Где предусмотрено изменение | Пример |
|---|---|---|
| INI_USER | Во время скрипта; также предусмотренные пользовательские механизмы | Уровень, включающий runtime-доступ |
| INI_PERDIR | php.ini и подходящие настройки каталога/сервера, .user.ini для CGI/FastCGI | upload_max_filesize: не через ini_set |
| INI_SYSTEM | php.ini и системная конфигурация сервера | allow_url_fopen: не через .user.ini или ini_set |
| INI_ALL | Все предусмотренные уровни, с учётом SAPI и admin-запретов | display_errors |
php.ini может задавать директивы этих режимов. Для INI_USER официальный список дополнительно включает Windows registry; это отдельный платформенный механизм. INI_ALL не делает .htaccess рабочим в Nginx и не отменяет php_admin_value. Проверяйте и режим, и применимость механизма.
- Код вызывает ini_set("upload_max_filesize", "20M")
- Режим INI_PERDIR запрещает runtime-изменение
- Функция возвращает false → выбираем настройку до запроса
<?php
$old = ini_set('upload_max_filesize', '20M');
if ($old === false) {
echo "Нужна настройка до выполнения скрипта\n";
} else {
echo "Настройка изменена\n";
}
Ожидаемый результат
Нужна настройка до выполнения скрипта
upload_max_filesize имеет режим INI_PERDIR. ini_set не вправе менять его во время выполнения — даже в CLI без загруженного php.ini. Для HTTP лимиты разбора запроса должны действовать ещё до начала вашего кода. Этот пример показывает отказ функции, а не испытывает настоящую загрузку файла.
Такая ошибка разбирается в вопросе про upload_max_filesize. Успешный вход в ваш скрипт ещё не означает, что PHP примет переопределение. Для загрузок согласуют также post_max_size и ограничения HTTP-сервера; подробности — начальный урок загрузки файлов.
Дополнительная ступень после массива с ключами: запросить метаданные директивы. Это помогает строить локальный диагностический отчёт, но не заменяет проверку фактического ini_set под администраторскими ограничениями.
<?php
$settings = ini_get_all(null, true);
$access = $settings['upload_max_filesize']['access'];
echo "Доступ из кода: ", ($access & INI_USER) ? 'да' : 'нет', "\n";
echo "Доступ уровня каталога: ", ($access & INI_PERDIR) ? 'да' : 'нет', "\n";
Ожидаемый результат
Доступ из кода: нет
Доступ уровня каталога: да
Поздняя диагностическая ступень после массивов: null выбирает все расширения, true просит подробности; две пары [] берут сведения сначала о директиве, затем о поле access. ini_get_all также возвращает global_value и local_value. access — числовая маска нескольких разрешённых уровней, а не само значение лимита. Оператор & проверяет присутствие флага; ненулевой результат выбирает «да» через условие ? : . Сравнивать всю маску на точное равенство INI_PERDIR нельзя: в ней может одновременно присутствовать INI_SYSTEM. Весь массив в браузер не выводится.
Разработка и рабочий сайт: куда попадают ошибки
У настройки ошибок три разных вопроса. error_reporting выбирает диагностические уровни. display_errors разрешает отправлять диагностику в вывод. log_errors включает запись в журнал. Пользователю сайта нужен понятный ответ; автору — технические сведения в контролируемом месте.
php.ini-development и php.ini-production — поставляемые шаблоны с разными намерениями, а не доказательство действующих настроек. Встроенные значения без ini могут отличаться от обоих шаблонов. Выберите свой файл сознательно и проверьте итог в нужном SAPI.
| Окружение | Ожидаемое поведение при диагностике | Что ещё проверить |
|---|---|---|
| Изолированная разработка | Автор видит техническое сообщение | В ответе могут появиться пути и детали; доступ только автору |
| Рабочий сайт | Посетитель не получает внутреннюю диагностику | Журнал реально пишется, защищён и не растёт без ограничения |
error_reporting = E_ALL
display_errors = On
display_startup_errors = On
log_errors = Off
Фрагмент для изолированного локального опыта. E_ALL — именованная маска уровней ошибок, поддерживаемая синтаксисом php.ini. Вывод ошибок удобен автору, но не подходит публичному ответу. Здесь логирование выключено лишь для этого короткого локального опыта; для длительной разработки можно включить его и назначить отдельный журнал.
error_reporting = E_ALL
display_errors = Off
display_startup_errors = Off
log_errors = On
Это фрагмент политики, не полный production php.ini. До применения администратор настраивает действующий журнал, права записи, доступ, ротацию и проверяет запись тестового сообщения в своём окружении. Конкретный error_log зависит от сервера. Отключение display_errors не исправляет ошибку и само по себе не гарантирует, что журнал существует.
ini_set("display_errors", "1") внутри файла не покажет синтаксическую ошибку, из-за которой этот же файл вообще не начал выполняться. Ошибки запуска PHP также возникают до ваших инструкций. Поэтому настраивать диагностику для таких случаев нужно до выполнения: через действующую конфигурацию или параметры контролируемого CLI-запуска.
Подавление вывода не создаёт правильный HTTP-код и пользовательскую страницу ошибки. Это работа приложения и сервера, описанная в разборе диагностики и исключений. Наличие log_errors=On не подтверждает успешную запись: важны путь, права и выбранный обработчик журнала.
.user.ini: настройки каталога под CGI/FastCGI
На хостинге часто нельзя править общий php.ini, но разрешена конфигурация каталога приложения. .user.ini — механизм PHP только для CGI/FastCGI. Он не обрабатывается обычным CLI, встроенным php -S или PHP-модулем Apache.
PHP ищет пользовательские ini от каталога запрошенного PHP-файла к document root. Если файл находится вне корня документов, проверяется только его каталог. Допускаются режимы INI_USER и INI_PERDIR, а также соответствующий INI_ALL. Имя задаёт user_ini.filename; пустое имя отключает механизм.
- Нужный CGI/FastCGI-процесс обслуживает файл приложения
- Читает разрешённые пользовательские ini для его каталога
- Использует кэш: user_ini.cache_ttl по умолчанию 300 секунд
- После применения проверяем текущее значение в этом HTTP-контексте
upload_max_filesize = 8M
post_max_size = 10M
display_errors = Off
Предусловия: сайт действительно работает через CGI/FastCGI, механизм per-user ini включён, имя файла по настройке user_ini.filename — .user.ini, сервер допускает эти директивы. Не относится к CLI или встроенному php -S. Значения — учебный пример: 10M больше 8M, чтобы оставить место остальным полям и обрамлению POST; полный маршрут проверки загрузки разобран в главе uploads.
Не переименовывайте .htaccess в .user.ini: у них разные синтаксис и обработчик. После изменения учитывайте TTL кэша, а также ограничения администратора. Ожидание пяти минут — лишь стандартное значение, не обещание хостинга. Точный срок берут из действующей user_ini.cache_ttl.
Потребность «локально лимит менялся, на хостинге получаю 500» описана в обсуждении смены лимитов. Современное решение начинается с выяснения SAPI и разрешённого механизма, а не с подавления ini_set через @.
.htaccess, конфигурация Apache и настройки пула FPM
Файл .htaccess читает Apache при разрешённых переопределениях. Директивы php_value и php_flag доступны для PHP как модуля Apache. При Apache, передающем запросы FPM, наличие самого Apache недостаточно. Nginx вообще не читает .htaccess.
Сначала уточните SAPI и правила сервера. Иначе добавление php_value способно дать HTTP 500 до выполнения вашего скрипта; это не ошибка тела PHP-файла. Такой случай — вопрос о post_max_size и Internal Server Error.
| Куда пишем | Кто понимает запись | Что нельзя переносить |
|---|---|---|
| php.ini / .user.ini | Парсер настроек PHP в соответствующем режиме | Не вставляем команды php_value с пробелами |
| .htaccess с mod_php | Apache и PHP-модуль | Не считаем этот путь совместимым с FPM |
| Конфигурация пула FPM | Менеджер FPM | Не подменяем её файлом .user.ini |
php_flag display_errors Off
php_value upload_max_filesize 8M
php_value post_max_size 10M
Предусловия: Apache действительно загружает PHP модулем, эти директивы разрешены администратором для .htaccess через AllowOverride (Options или All), место файла соответствует нужному каталогу. Здесь нет знака =. Для логического значения применён php_flag, для размера — php_value. При Apache→FPM этот механизм PHP не становится доступным автоматически; чужой сервер может вернуть HTTP 500.
В серверной конфигурации Apache существуют php_admin_value и php_admin_flag: они не разрешены в .htaccess, и программа не отменяет их через ini_set. В php_value числовые PHP-константы из кода, например E_ALL, не вычисляются как в php.ini. Не переносите пример без проверки формата.
php_admin_flag[display_errors] = off
php_admin_flag[log_errors] = on
Это строки для конфигурации выбранного пула, а не .user.ini или обычного php.ini; полного пула здесь нет. Предусловия: администратор знает активный FPM, пул и способ безопасного применения его конфигурации. php_admin_flag закрепляет настройку так, что ini_set её не переопределяет. В этой работе FPM и его конфигурация не менялись.
Для FPM есть отдельные php_value/php_flag и php_admin_value/php_admin_flag в синтаксисе пула. Admin-вариант нельзя переопределить ini_set. Приведённый фрагмент объясняет границу полномочий; создание пула, адрес FastCGI, управление процессами и безопасный reload требуют отдельного полного урока.
Обычные секции ini и условные HOST/PATH
Заголовки [PHP], [Date] помогают человеку группировать строки основного ini; они не создают отдельную область видимости PHP-кода. Особый случай — [PATH=...] и [HOST=...]. Эти условные секции применяются только в CGI/FastCGI к подходящему пути или хосту.
Это также не то же самое, что секция [pool-name] в конфигурации FPM: похожие квадратные скобки не означают одинаковое назначение. В условных секциях нельзя загружать extension и zend_extension.
| Заголовок | Область смысла |
|---|---|
| [Date] | Визуальная группа директив в основном ini |
| [PATH=/srv/lesson/public] | Настройки подходящего пути в CGI/FastCGI |
| [HOST=lesson.example.test] | Настройки указанного хоста в CGI/FastCGI |
[PATH=/srv/lesson/public]
display_errors = Off
[HOST=lesson.example.test]
display_errors = Off
Фрагмент применим только к CGI/FastCGI. PATH выбирает область по пути, HOST — по имени хоста. Это два независимых условных блока, не последовательность запросов. Путь и имя учебные: перенесённый буквально фрагмент не настроит ваш сайт. extension и zend_extension в таких секциях загружать нельзя.
Официальное описание этих секций находится в List of php.ini sections. Проверка простым php -r или запуском CLI-файла не доказывает их действие для HTTP-запроса. Пограничные случаи совпадений и применение на реальном сервере остаются для серверного практикума.
Значение из окружения процесса
При нескольких учебных окружениях бывает удобно оставить одинаковый ini, а одно значение задать окружением запуска. Запись ${NAME} подставляет переменную окружения. С PHP 8.3 форма ${NAME:-fallback} задаёт запасной вариант для незаданной переменной. Наличие переменной с пустой строкой — другой случай: запасной вариант не применяется.
| Что видит PHP в LESSON_MEMORY | Какое значение получает memory_limit |
|---|---|
| Непустая строка, например 256M | 256M из окружения процесса |
| Переменная не задана | Запасные 128M из ini |
| Переменная существует, но пуста | Пустое значение; для memory_limit это ошибка настройки, не выбор 128M |
memory_limit = ${LESSON_MEMORY:-128M}
Предусловие: PHP 8.3+ и контролируемое окружение запуска. Если LESSON_MEMORY не задана, используется 128M. Пустая, но существующая переменная не включает запасной вариант: не задавайте лимит пустой строкой. Это синтаксис ini, не PHP-переменная со знаком $. Переменная должна существовать в окружении самого PHP-процесса; её наличие в вашем терминале не доказывает наличие у службы FPM. Здесь переменные компьютера не изменяются.
Строки таблицы — альтернативные исходы. Не путайте это с массивом $_ENV и переменной $name в PHP-коде. Службы могут получать другое окружение; FPM по умолчанию очищает его, кроме явно разрешённых значений. Не используйте такие подстановки для незаметной передачи секретов в публичные диагностические отчёты.
Внутри действия: восстановить настройку даже при раннем возврате
Когда настройка нужна только одному отчёту, важно не изменить поведение следующей операции. После базовых функций и управления выходом и finally рассмотрите полный небольшой пример. function reportTime объявляет действие без входных аргументов; : string означает строковый результат. return передаёт строку месту вызова.
try обозначает участок работы, finally — действие при выходе из него, включая return. date("H:i", 0) форматирует заданный момент как часы и минуты в текущем часовом поясе. Настройка меняется до вызова date, а затем восстанавливается.
- Перед вызовом UTC
- reportTime временно выбирает Asia/Tokyo
- date для момента 0 возвращает 09:00
- finally возвращает UTC, затем echo получает результат
<?php
ini_set('date.timezone', 'UTC');
function reportTime(): string
{
$before = ini_set('date.timezone', 'Asia/Tokyo');
if ($before === false) {
return 'Не удалось настроить часовой пояс';
}
try {
return date('H:i', 0);
} finally {
ini_set('date.timezone', $before);
}
}
echo "В отчёте: ", reportTime(), "\n";
echo "После отчёта: ", ini_get('date.timezone'), "\n";
Ожидаемый результат
В отчёте: 09:00
После отчёта: UTC
Самостоятельный учебный CLI-запуск без административных ограничений. Время 0 — начало Unix-отсчёта, поэтому результат определён, а не зависит от текущих часов. finally выполняется и при return, возвращая прежнюю настройку. Для реального приложения предпочтительнее явный DateTimeZone у операции: глобальная настройка может влиять на соседний код. Этот пример нужен для понимания области действия ini_set и восстановления.
Установка UTC в начале сделана для самостоятельного воспроизводимого опыта. В библиотеке не нужно навязывать её всему приложению. Ещё лучше передавать часовой пояс конкретному объекту даты, как показано в главе дат, тогда глобальная настройка вообще не меняется.
Практика: проверить причину, а затем выбрать место изменения
Задание 1. Вы исправили C:\Tools\PHP\php.ini, но сайт по-прежнему показывает прежний лимит. Предложенное решение: сначала прочитать SAPI, основной и дополнительные ini у сайта, затем действующее значение. Не считать CLI-доказательство доказательством HTTP.
Задание 2. ini_set вернул false для upload_max_filesize. Решение: найти режим INI_PERDIR и выбрать разрешённый startup/per-directory механизм. Оператор @ скрывает диагностику, но не выдаёт права на изменение.
Задание 3. После временной настройки внешний код должен получить своё прежнее значение. Решение: сохранить возврат ini_set, отличить false, восстановить прежнюю строку при выходе. ini_restore может вернуть другое — исходное — значение.
Задание 4. В .user.ini записали 8M и сразу не увидели эффекта. Решение: проверить CGI/FastCGI, имя user_ini.filename, расположение относительно исполняемого файла, TTL и admin-ограничения. Если это CLI, ожидание TTL не поможет.
Задание 5. display_errors=Off убрал сообщение из страницы. Что ещё проверить? Решение: правильный HTTP-ответ приложения и фактическую запись диагностики в доступный только обслуживающим людям журнал. Молчание страницы не означает исправление ошибки.
Рабочий порядок: зафиксировать исходный путь и значение → определить разрешённый уровень → изменить одну настройку в контролируемом окружении → применить её способом выбранного SAPI → проверить результат → сохранить путь возврата. В этой главе освоены основы; полные серверные, платформенные и расширенные варианты остаются самостоятельными разделами официального руководства.
Откуда взялось значение: основной ini, scan-файлы и выбранный SAPI
Вы изменили memory_limit, а программа продолжает видеть другое значение. Прежде чем повторно редактировать файлы, составьте цепочку загрузки. Есть выбор основного ini, дополнительные scan-файлы и последующие переопределения. Наличие php.ini в каталоге проекта не означает, что CLI его читает.
Первый этап: установленный SAPI может задать путь явно — -c у CLI/CGI, PHPIniDir у Apache. Далее участвуют PHPRC, Windows-registry при соответствующей платформе и пути поиска сборки. Точное правило нужно сверять с выбранным SAPI; полный наблюдаемый результат показывает --ini для CLI.
| Кандидат основного ini по порядку PHP.net | Граница |
|---|---|
| SAPI-specific путь: -c / PHPIniDir | Должен относиться к реально запущенному исполнителю |
| PHPRC | Окружение именно процесса, а не соседнего терминала |
| Windows IniFilePath по версии x.y.z → x.y → x, затем общий ключ | 32/64-разрядное представление реестра зависит от PHP и ОС |
| Рабочий каталог | CLI пропускает этот кандидат |
| Каталог веб-сервера для SAPI-module либо PHP на Windows | Зависит от интерфейса |
| Windows-каталог либо Unix compile-time config path | Не причина создавать глобальный ini без необходимости |
Если найден php-SAPI.ini, он используется вместо общего php.ini: например, php-cli.ini для CLI. Не угадывайте имя другого SAPI по имени службы; смотрите его фактическое имя. На Unix изменение current directory сервером при старте также объясняет неожиданный путь.
Второй этап: после основного файла PHP читает *.ini из scan-каталогов. Внутри каждого каталога действует алфавитный порядок, а каталоги идут в заданном порядке. -c не отключает scan; -n отключает чтение ini. PHP_INI_SCAN_DIR меняет набор каталогов для процесса.
<?php
echo "main:", php_ini_loaded_file() === false ? 'none' : 'loaded', "\n";
echo "scan:", php_ini_scanned_files() === false ? 'none' : 'loaded', "\n";
Ожидаемый результат
main:none
scan:none
Показанный результат получается при запуске php -n: ini-файлы не читаются. При запуске с основным php.ini первая строка будет main:loaded; при наличии дополнительных файлов вторая будет scan:loaded. Функции возвращают путь/список либо false, а === false сравнивает именно отсутствие. Это диагностический пример с результатом, зависящим от окружения: общий прогон проверяет синтаксис, отдельная проверка запуска с -n подтвердила показанный вывод. Пример не раскрывает внутренние пути посетителю.
Unix: /opt/lessons/conf.d:/opt/lessons/overrides
Windows: C:\PHP Lessons\conf.d;C:\PHP Lessons\overrides
Это карта значения PHP_INI_SCAN_DIR, не команды оболочки. На Unix разделитель :; на Windows ;. Пустой компонент списка включает compile-time scan-каталог на этом месте; он не означает «ничего не читать». Поэтому двоеточие в начале/конце может добавить неожиданную группу ini.
Внутри ini можно использовать ${NAME}, с PHP8.3 — ${NAME:-fallback}. В проверенном PHP8.5 fallback относится к отсутствующей переменной; существующая пустая строка не подменяется запасным значением. Не считайте эту запись универсальной проверкой корректности числа. Подробный минимальный разбор есть в базовой главе конфигурации.
Упражнение: главный ini содержит128M, scan-файл90-memory.ini содержит256M, а команда вызвана с -d memory_limit=192M. Какое значение ожидается в новом CLI?192M при отсутствии дальнейшего изменения в коде. Проверка такого приоритета важнее, чем поиск «единственно правильного» php.ini на всём диске.
Где разрешено менять настройку: серверный администратор и приложение
Разработчик хочет временно изменить отображение ошибки; администратор хочет удержать обязательную политику. INI-моды описывают, где параметр допускает изменение. Они не являются значением самого параметра и не заменяют проверку результата ini_set.
INI_USER разрешает уровень скрипта, INI_PERDIR — уровень каталога/конфигурации до выполнения, INI_SYSTEM — системную настройку запуска, INI_ALL объединяет возможности. Отдельные директивы имеют комбинированные маски и специальные ограничения. Считать любое число access равным ровно одной константе недостаточно.
<?php
$before = ini_get('arg_separator.output');
$changed = ini_set('arg_separator.output', ';');
echo $changed === false ? "change:rejected\n" : "change:accepted\n";
echo "current:", ini_get('arg_separator.output'), "\n";
ini_set('arg_separator.output', $before);
echo ini_get('arg_separator.output') === $before ? "restored:yes\n" : "restored:no\n";
Ожидаемый результат
change:accepted
current:;
restored:yes
Самостоятельный безопасный CLI-опыт. $before сохраняет строку; ini_set возвращает прежнее значение или false. Сравнение === false не путает отказ с прежней пустой строкой. В конце возвращаем сохранённое значение. Это пример изменяемой INI_ALL-настройки; он не даёт право менять любую директиву.
При PHP как модуле Apache есть php_value для значений и php_flag для on/off. Они допускают INI_ALL/INI_PERDIR в применимом контексте. php_admin_value/php_admin_flag задаются в серверной конфигурации, не в .htaccess, и запрещают переопределение скриптом. Для FPM используются одноимённые записи с квадратными скобками в pool; эти два формата не надо смешивать.
php_flag display_errors Off
php_admin_flag log_errors On
php_admin_value memory_limit 128M
Apache-конфигурация с загруженным PHP-модулем. Для Apache+FPM эти директивы не являются способом изменить PHP: используйте pool/ini применимого SAPI. Даже изменяемую обычно memory_limit код не переопределит после административной установки. Пример не исполнялся.
PHP-константы вроде E_ALL разбираются в php.ini, но не обязаны существовать в конфигурации Apache или строках Windows-registry. Там числовая маска должна соответствовать целевой версии: не переносите число старого E_ALL без проверки. Для boolean в Apache используйте php_flag, а не php_value.
Windows PHP также имеет историческую per-directory настройку через HKLM\SOFTWARE\PHP\Per Directory Values и вложенные ключи пути. Значения строковые, работают для каталога и потомков, PHP-константы не вычисляются; здесь разрешены INI_USER, не INI_PERDIR. Это отдельный механизм от IniFilePath, который выбирает главный файл.
Для понимания существующей установки достаточно прочитать эти настройки с администратором и сопоставить с процессом. Учебный опыт не требует записи в HKLM. На64-битной Windows учитывайте представление реестра32-битного PHP; ключ, который видит одна программа, может отличаться от просматриваемого другой.
Упражнение: ini_set вернул false после php_admin_value, но та же директива обычно изменяема. Решение: найден дополнительный административный запрет; удаление проверки false лишь скроет проблему. Настройку меняет владелец серверной политики, если задача действительно этого требует.
.user.ini и условные секции: настройки до начала скрипта
Два сайта на одном исполнителе могут иметь разные ограничения. Для CGI/FastCGI PHP умеет искать .user.ini от каталога запрошенного PHP-файла вверх до document root. CLI и встроенный php -S этот механизм не применяют. Если файл вне document root, правила поиска отличаются: PHP рассматривает только его каталог.
В .user.ini доступны INI_USER/INI_PERDIR, имя файла выбирает user_ini.filename, пустое имя выключает механизм. user_ini.cache_ttl обычно300секунд: изменение не обязано появиться в следующем запросе. Настройки читаются до выполнения тела файла; их задача отличается от ini_set посередине программы.
upload_max_filesize = 2M
post_max_size = 3M
Файл в своём учебном каталоге под document root; SAPI должен быть CGI/FastCGI и разрешать механизм. Предел POST больше одиночного файла, потому что тело содержит дополнительные поля и multipart-разметку. Эти строки не включают обработку загрузки и не проверены через HTTP в этой партии.
Проверяйте ограничение настоящим запросом в отдельной среде после истечения TTL/обновления процессов. Один ini_get покажет настройку, но не докажет, что приложение корректно различает отказ сервера, пустой POST и ошибку конкретного файла. Код обработки загрузки относится к соответствующей веб-главе.
Другой механизм — секции [PATH=...] и [HOST=...] внутри главного ini. Они действуют для CGI/FastCGI, а не являются обычными декоративными [PHP]-заголовками. PATH выбирает путь, HOST — имя хоста; startup-директивы вроде extension и zend_extension такими секциями не управляются.
[PATH=/srv/php-lessons/public]
memory_limit = 96M
[HOST=lessons.example.test]
display_errors = Off
Контекст CGI/FastCGI с указанным document root и отдельно настроенным учебным именем хоста. .test — демонстрационное имя; DNS/hosts не настраивались. Для реального опыта нужно проверить каждый путь/host через HTTP и границы соседнего сайта. Значения условных секций защищены от переопределения пользовательским ini/кодом в применимом контексте.
| Задача | Подходящий механизм | Неподходящая подстановка |
|---|---|---|
| Локальная CLI-проверка другого ini | -c и при необходимости -n/-d | .user.ini рядом с файлом |
| Разрешённая настройка FastCGI-каталога | .user.ini с учётом TTL | php_value в .htaccess без mod_php |
| Административный предел FPM pool | php_admin_value[имя] | Надежда, что приложение не вызовет ini_set |
| Изменить список загружаемых расширений | Настройки запуска соответствующего PHP | extension в [PATH] или .user.ini |
Упражнение: рядом с CLI-файлом лежит .user.ini, но лимит не изменился. Решение: это ожидаемо для CLI; используйте явные параметры запуска. Упражнение: после правки FastCGI .user.ini часть запросов ещё старая. Решение: исследовать cache TTL и живые процессы, а не добавлять ту же директиву во все найденные файлы.
Полная эксплуатационная проверка этих правил требует отдельной CGI/FastCGI-среды, нескольких каталогов и запросов с разными host. В этой Windows CLI-партии выполнены самостоятельные языковые опыты, а такие HTTP-сценарии явно остаются непроверенными.
Windows INI: поиск версии и разрядности без редактирования реестра
На машине несколько PHP, а найденный php.ini лежит не рядом с ожидаемым php.exe. Помимо явного -c/PHPRC, Windows PHP умеет искать IniFilePath в реестре. Сначала проверяют вывод php --ini полного пути исполнителя: это наблюдение безопаснее попытки сразу исправить глобальный registry. Реестр — системная база настроек; ошибочная запись влияет на другие процессы.
Для совпадающей разрядности PHP/ОС используется HKLM\SOFTWARE\PHP, для 32-bit PHP на 64-bit Windows — соответствующее 32-bit view (WOW6432Node). Внутри рассматриваются более точная версия x.y.z, затем x.y, затем x и общая настройка. Явный SAPI-specific путь и PHPRC участвуют раньше. В тексте PHP.net встречается опечатка WOW6421Node; вручную создавать ключ с таким именем не надо.
- Полный путь php.exe и его разрядность
- Явный путь / PHPRC / соответствующий registry view
- Найденный основной ini
- --ini подтверждает результат поиска
reg.exe query HKLM\SOFTWARE\PHP /s /reg:64
reg.exe query HKLM\SOFTWARE\PHP /s /reg:32
Предусловие: 64-bit Windows, разрешено чтение HKLM. /reg выбирает view, /s показывает подветки. Если ключ отсутствует, reg сообщает об этом и возвращает ненулевой код; это нормальная причина не найти registry-конфигурацию, а не доказательство сломанного PHP. Команды ничего не записывают. Не публикуйте чужие локальные пути из отчёта без необходимости.
Per Directory Values — другой механизм: значения для c:\inetpub\wwwroot находятся под соответствующей иерархией c\inetpub\wwwroot и распространяются на подкаталоги. Это string values с именами директив, применимы лишь INI_USER-изменения; INI_PERDIR и PHP-константы так не задают. Наличие общей IniFilePath и per-directory значения имеет разный смысл. На управляемом IIS обычно понятнее явная конфигурация FastCGI/PHPRC и отдельный pool, чем скрытая новая registry-зависимость.
Практика: не меняя registry, составьте карту «полный php.exe → разрядность → main ini → scanned ini». Сравните с handler php-cgi.exe сайта. Работа CLI не подтверждает Per Directory Values/CGI-поведение: для этого нужен отдельный закрытый HTTP стенд.
Несколько INI: порядок чтения, текущие значения и условные секции
Настройка присутствует в двух файлах; нужно понять, какая применяется. В нормальном startup сначала читается основной ini, затем дополнительные .ini по порядку каталогов, внутри каталога — по алфавиту. PHP_INI_SCAN_DIR может перечислять несколько каталогов с PATH_SEPARATOR данной ОС; пустой элемент включает compiled-in scan directory. Это отличается от пустого списка дополнительных файлов и может неожиданно вернуть системные настройки.
INI умеет ссылаться на уже известное значение через ${имя}. Это не PHP-переменная с долларом и не shell-команда. Следующий пример читает только учебный текст; он не меняет ini работающего PHP. parse_ini_string возвращает массив прочитанных значений. INI_SCANNER_RAW сохраняет строки без вычисления специальных значений.
- Startup: основной ini
- Каталоги scan в заданном порядке
- Допустимые локальные/административные значения
- ini_get наблюдает этот процесс
<?php
$text = "[report]\nname=stock\nlimit=20\n";
$settings = parse_ini_string($text, true, INI_SCANNER_RAW);
echo $settings['report']['name'], ': ', $settings['report']['limit'], "\n";
Ожидаемый результат
stock: 20
Строка [report] создаёт группу данных при втором аргументе true. ['report']['name'] читает значение внутри группы. Эта произвольная секция не является условной секцией php.ini и ничего не настраивает в runtime. Здесь учимся отличать текст INI от его применения системой.
<?php
$text = "limit=20\nlimit=30\n";
$settings = parse_ini_string($text);
echo $settings['limit'], "\n";
Ожидаемый результат
30
В учебном массиве остаётся последнее значение одинакового ключа. limit — произвольное имя данных, не директива PHP. Этот пример не моделирует режимы INI_SYSTEM/административный запрет или загрузку extension: у настоящих директив есть дополнительные правила.
include_path = ${include_path} ":/srv/lesson/lib"
Фрагмент настоящего ini для собственного Unix PHP: ${include_path} ссылается на известное значение конфигурации, двоеточие разделяет пути. На Windows разделитель — точка с запятой. Не путайте это с произвольной переменной, объявленной предыдущей строкой в parse_ini_string: такой пользовательский ключ сам по себе не становится ini-переменной. Здесь изменение настройки не выполнялось.
| Механизм | Когда/где действует | Что не обещает |
|---|---|---|
| Основной php.ini и scanned .ini | Startup конкретного PHP/SAPI | Сохранение файла не обновляет уже работающих workers |
| .user.ini | CGI/FastCGI, разрешённые INI_USER/INI_PERDIR, cache TTL | CLI/php -S не доказывают её действие |
| [HOST=имя] / [PATH=путь] | Условные секции для CGI/FastCGI | Нельзя подключать extension/zend_extension в этих секциях |
| Apache php_admin_* / FPM php_admin_* | Административно закреплённые значения | ini_set не обходит запрет |
| ini_get / ini_get_all | Наблюдение текущего процесса | Другой SAPI может иметь иные значения/доступность |
Для вложенной .user.ini PHP идёт от каталога запрошенного файла к document root; файл вне document root требует особого учёта пути поиска. user_ini.cache_ttl объясняет задержку, пустое user_ini.filename выключает поиск. HOST/PATH секции — специальная конфигурация PHP, обычные [mail] или [report] в php.ini не создают такие правила. Указание display_errors=On в dev HOST применимо только если реальный сервер передаёт ожидаемые данные хоста; не полагайтесь на непроверенный заголовок клиента как на контроль доступа.
Практика: в отдельном CGI/FastCGI стенде сравните root/a.php и root/nested/b.php с разрешённой директивой, дождитесь TTL либо перезапустите test pool, затем проверьте admin override. Здесь выполнены только два изолированных разбора INI-текста; реальное HTTP-наследование остаётся отдельно отмеченным испытанием.
configure: выбрать свойства ещё не собранного PHP
Нужен PHP с определённым SAPI или отладочная сборка для разбора сбоя расширения. Настройки php.ini не могут добавить код, которого нет в бинарном файле. configure работает раньше компиляции: проверяет систему и записывает правила для make. ini задаёт поведение уже собранного PHP. Эти этапы отличаются даже когда оба называют «конфигурацией».
Перед этим уроком пройдите исходную сборку с отдельным prefix. Команды ниже относятся к Unix/macOS shell внутри проверенного source tree выбранной версии PHP. ./ означает файл в текущей папке, --имя выбирает опцию, = связывает её со значением, кавычки сохраняют путь как один аргумент. Сборку и системную установку здесь не выполняли.
- configure: есть ли библиотеки и какие возможности включить
- make: получить бинарные файлы
- make test: проверить код в этой сборке
- install: разместить выбранный prefix
- php.ini: настроить новый процесс
./configure --help
./configure --prefix="$HOME/php-lab85" --disable-all --enable-cli --disable-cgi --disable-phpdbg --without-pear
Предусловия: отдельное дерево исходников PHP 8.5, toolchain/зависимости из его README; если configure ещё нет, сначала buildconf с нужными генераторами. $HOME читается как существующая домашняя папка shell, не переназначается. --disable-all выключает по умолчанию включаемые расширения, но не удаляет обязательное ядро/JSON/OPcache 8.5. CLI явно оставлен; CGI/phpdbg/PEAR для этой задачи не нужны. Вторая команда создаёт файлы сборки в source tree, не устанавливает PHP.
Не вставляйте этот минимальный набор в существующую production-сборку: приложению могут быть нужны XML, mbstring, PDO и другие возможности. Отдельный prefix предотвращает прямую замену /usr/bin/php, но ещё не обеспечивает воспроизводимость. Сохраните tag/release, compiler, dependency versions, configure-команду, лог и результаты тестов. После сборки вызывайте полный путь нового php; PATH может по-прежнему выбирать старый.
Практика: укажите, какой из этапов исправляет каждую проблему: «не найден компилятор», «не собран mbstring», «memory_limit слишком мал», «терминал запускает старый PHP». Нельзя все четыре лечить изменением php.ini.
Пути, SAPI и библиотеки: осмысленно прочитать опции сборки
Установить две ветки PHP рядом полезно для проверки обновления. Для этого нужно развести бинарники, основной ini и каталог дополнительных ini. Параметры prefix/config-file-path/config-file-scan-dir решают разные части задачи: каталог установки не равен каталогу настроек. В готовом PHP смотрят php --ini и php-config, а не предполагают пути по имени архива.
- Отдельный prefix PHP
- Его bin и native-модули
- Его основной ini и conf.d
- Полный путь запуска выбирает этот комплект
| Текущая опция / группа | Практический смысл | Что проверить |
|---|---|---|
| --prefix=…; --with-layout=PHP|GNU | Корень и раскладка установленных файлов | Где получились bin, lib, man; не перезаписан системный PHP |
| --with-config-file-path=… | Сборочный путь поиска основного ini | php --ini в новом процессе |
| --with-config-file-scan-dir=… | Дополнительный каталог ini | Список parsed files и отсутствие модулей чужой ветки |
| --with-libdir=lib64; --disable-rpath | Каталог библиотек / отказ от добавочных runtime search paths | Зависимости линковщика; lib64 не универсально для всех ОС |
| --enable-cli / --disable-cli | Нужен ли командный исполнитель | bin/php -v; отключение CLI ограничивает и инструменты на PHP |
| --enable-cgi / --disable-cgi | CGI/FastCGI binary php-cgi | Не путать с FPM; --disable-cgi не включает FastCGI |
| --enable-fpm | Отдельный FPM SAPI для Unix | Pool/master конфигурация и listener после сборки |
| --with-apxs2=/путь/apxs | Apache 2 handler для выбранного Apache | Версия Apache, MPM/TS и установка модуля |
| --enable-embed=shared|static | Библиотека PHP для встраивания в программу на C | Host-приложение и его linking; это не web server |
| --disable-phpdbg; --enable-phpdbg-debug; --enable-phpdbg-readline | Состав/отладочные возможности SAPI phpdbg в 8.5 | Нужна ли задача отладки; phpdbg не заменяет обычный CLI |
| --with-pear=… / --without-pear | Установка инфраструктуры PEAR | С PHP 7.4 она не устанавливается по умолчанию; Composer/PIE — отдельные инструменты |
./configure --prefix="$HOME/php-lab85" --with-config-file-path="$HOME/php-lab85/etc" --with-config-file-scan-dir="$HOME/php-lab85/etc/conf.d" --disable-all --enable-cli --disable-cgi --disable-phpdbg --without-pear
Предусловия как в предыдущем разделе. Это альтернативный configure для отдельной чистой сборочной папки. После успешных make/test/install создайте собственную конфигурацию в её etc, а не копируйте весь conf.d системного PHP. Один и тот же старый extension=… может указывать на несовместимый модуль.
Параметры конкретных расширений читают в их установочных разделах и ./configure --help этой версии. --enable-… обычно переключает возможность, --with-… может ещё задавать библиотеку/путь; это соглашение, а не универсальная гарантия. Современные расширения часто находят зависимости через pkg-config; старый --with-библиотека=/путь может больше не поддерживаться.
Практика: запланируйте два prefix без общего extension_dir и conf.d. Объясните, какие файлы приложения можно использовать повторно, а какие native-модули нужно пересобрать. Проектный lock не решает ABI-совместимость.
Debug, ZTS, таймеры и диагностика сборки
При падении собственного C-расширения нужен отладочный PHP. --enable-debug меняет сборку и её проверки; это не то же, что display_errors=On. Для обычной разработки PHP-файлов достаточно привычного исполнителя и настроек ошибок. ZTS добавляет поддержку потокобезопасного runtime для соответствующего embedding/SAPI; он не запускает ваш foreach параллельно.
Ниже — карта специального инструментария PHP 8.5.11. Не включайте все опции одновременно: sanitizer/coverage/debug сборки имеют особые требования и стоимость. Для исходной диагностики меняют один параметр, сохраняют отчёт и воспроизводящий тест.
- Минимальный воспроизводящий C-сбой
- Отдельный debug PHP
- Согласованно собранное расширение
- Тест и отчёт без секретов
| Опция/семейство | Задача и граница |
|---|---|
| --enable-debug; --enable-debug-assertions | Debug build / дополнительные C assertions даже в release; отдельно от PHP assert() |
| --enable-zts | Потокобезопасная сборка; до PHP 8.0 на Unix использовалось имя --enable-maintainer-zts |
| --enable-zend-max-execution-timers | POSIX per-thread timers; PHP 8.1+, FreeBSD support 8.4+, default для ZTS с 8.3; нужны возможности ОС |
| --enable-sigchild | Собственный обработчик SIGCHLD; может влиять на наблюдение дочерних процессов |
| --with-valgrind; --enable-dmalloc | Интеграция диагностики памяти; нужны соответствующие инструменты/библиотеки |
| --enable-gcov | Покрытие исходников C; не готовый отчёт покрытия пользовательского PHP |
| --enable-address-sanitizer; --enable-undefined-sanitizer; --enable-memory-sanitizer | Отдельные compiler-инструменты для ошибок памяти/undefined behavior; memory sanitizer требует clang |
| --enable-werror | Сделать предупреждения compiler ошибками; новая версия compiler может остановить старый код |
| --enable-dtrace | Системные tracing probes при поддержке toolchain/ОС |
| --enable-rtld-now; --enable-libgcc | Режим динамической загрузки / явная связь с libgcc; специальные linking задачи |
| --enable-system-glob; --disable-ipv6; --enable-fd-setsize | Выбор системной glob, сетевых возможностей и descriptor set; меняют свойства runtime |
| --enable-re2c-cgoto; --disable-fiber-asm; --disable-gcc-global-regs; --disable-zend-signals | Низкоуровневые варианты реализации/переносимости; решаются по целевой платформе и исходникам |
| --disable-short-tags | Отключить по умолчанию короткий <?; обычный <?php и <?= не исчезают |
./configure --prefix="$HOME/php-debug85" --disable-all --enable-cli --disable-cgi --disable-phpdbg --without-pear --enable-debug
Предусловие: отдельное исходное дерево/сборочная папка с PHP 8.5 и compiler prerequisites. После make/test вызывают именно $HOME/php-debug85/bin/php. Release-модули не подставляют в debug runtime; требуемое расширение собирают согласованно. Здесь compiler не запускался.
Смысл max_execution_time зависит от платформы/сборки: обычный Unix ITIMER_PROF измеряет процессорное время, а POSIX per-thread timers считают прошедшее время, включая ожидание. По одной строке ini нельзя обещать одинаковое wall-clock завершение на всех PHP. Для внешнего процесса/HTTP отдельно задают соответствующий timeout; «30» не становится универсальным дедлайном всей системы.
Практика: подготовьте паспорт двух сборок — release и debug — и минимальный воспроизводящий PHP-файл. Отчёт об ошибке содержит версию, SAPI, compiler, расширения и фактический stderr, но не чужие секреты или полный дамп памяти с пользовательскими данными.
Исторические configure-флаги: сохранить смысл, проверить применимость
В официальном приложении configure.about перечислены и старые флаги, которых нет в проверенных core/SAPI исходниках PHP 8.5.11. Поэтому страница не является готовой командой для новой сборки. Ниже сохранены эти пункты дерева: читатель сможет распознать старый build script и выбрать современный путь. Сначала смотрят tag исходников и ./configure --help именно этой версии; warning unrecognized options нельзя принимать за успешное включение функции.
- Флаг из старого рецепта
- Задача, которую он решал
- Help конкретного PHP 8.5 source tree
- Поддерживаемый путь или явное отсутствие аналога
| Пункт официального списка | Как читать при сопровождении |
|---|---|
| --enable-php-streams | Исторический экспериментальный переключатель; streams давно являются частью современного PHP, включать этим флагом файловые функции не нужно |
| --with-zlib-dir | Старый способ указать каталог; для актуального zlib смотрят установочную страницу расширения и pkg-config/--with-zlib текущего source tree |
| --with-tsrm-pthreads | Исторический выбор threading; для современного PHP нужен согласованный ZTS/toolchain маршрут |
| --with-apxs; --with-apache; --with-mod_charset | Старые Apache 1.x/специальные варианты; современный Apache 2 handler использует --with-apxs2, а FPM вообще отдельный SAPI |
| --with-servlet | Историческое Java/servlet-встраивание; не обещается в PHP 8.5 |
| --enable-maintainer-mode; --enable-versioning | Пункты старого списка генерации/экспорта символов; не обнаружены в проверенных core/SAPI файлах 8.5.11, требуется проверка конкретного generated help |
| --enable-shared[=PKGS]; --enable-static[=PKGS]; --enable-fast-install[=PKGS] | Опции libtool для библиотек/пакетов; не означают «встроить все расширения» |
| --with-gnu-ld; --disable-libtool-lock; --with-pic | Выбор linker/locking/PIC в поддерживающем их build system; не runtime-настройки php.ini |
./configure --help > configure-help.txt
Предусловие: configure уже подготовлен в отдельном source tree. > записывает stdout в файл текущей папки и перезаписывает существующий configure-help.txt, поэтому выберите новый файл при необходимости. Содержимое зависит от тега, платформы и добавленных расширений. Здесь генератор/сборка не исполнялись.
Если старый recipe использует отсутствующую опцию, выясните желаемый результат: например, передача PHP-запросов Apache сейчас решается FPM или актуальным handler, а не оживлением Apache 1.x. Отсутствующая возможность может требовать архитектурной замены; не заменяйте имя флага на похожее наугад. Архив старого PHP нужен для анализа совместимости, а не для публичного сервера без поддержки.
Практика: возьмите один старый флаг из таблицы, найдите его задачу и запишите современный эквивалент либо отсутствие прямого эквивалента. Не считайте тишину configure доказательством наличия функции: после сборки нужна проверка binary и полезного действия.
Проверить сборку и спланировать обновление без замены рабочего PHP
Команда make завершилась, но это только компиляция. Перед установкой нужны тесты и повторяемый артефакт. Release архив получают с официальной страницы выбранного выпуска, сверяют опубликованную подпись/контрольную сумму и сохраняют имя исходника. Git tag/commit фиксирует иной путь подготовки; README выбранного тега определяет compiler и генераторы.
После неуспешного configure найдите первую полезную причину в config.log: ошибка compiler, development headers, pkg-config или неподдерживаемая опция. После неуспешного make изучите первую ошибку compiler, а не только финальное Error 2. Не смешивайте частично собранные объекты разных архитектур/веток в одной папке.
- Сохраняем рабочий prefix и его ini
- Собираем новый prefix отдельно
- Выполняем PHPT и тесты приложения
- Проверяем новый SAPI в тестовой среде
- Переключаем конкретный launcher/handler
- При отказе возвращаем прежний комплект
make test TESTS=tests/basic
Предусловие: сборка PHP в этом source tree успешно завершена; tests/basic — существующий каталог PHPT этого дерева. PHPT описывает вход и ожидаемый результат теста. Другие наборы могут требовать расширения/сервисы/переменные среды. Не отправляйте отчёт наружу автоматически: сначала исключите секреты и разберите FAIL/SKIP. Здесь make test не выполнялся.
SKIP означает непройденный сценарий из-за его условий, а не подтверждённую исправность. FAIL сравнивают с .out/.exp/.diff и воспроизводят минимально; flaky timing или locale проверяют отдельно, не удаляют тест ради зелёной строки. Сборка с критическим необъяснённым FAIL не становится рабочей только после make install.
На macOS сверяйте uname -m, выбранный SDK/compiler и архитектуру библиотек; смешение arm64/x86_64 часто проявляется при linking. На Solaris изучают требования конкретной ОС/compiler и поддерживаемые библиотеки: наличие старого Solaris пункта в PHP.net не доказывает наличие современного готового пакета. На Windows MSVC/SDK/build dependencies должны соответствовать целевому PHP. Этот урок даёт процедуру проверки; реальная сборка на этих ОС остаётся отдельным испытанием.
Практика: составьте migration record с предыдущим и новым prefix, конфигурацией, модульным составом, тестами и точкой возврата. Откат кода не откатывает изменённую схему базы автоматически: миграции данных согласуют отдельно.
Источники и обсуждения
- PHP: Файл конфигурации
- PHP: Способы изменения
- PHP: Режимы изменения
- PHP: Пользовательские ini
- PHP: Условные секции
- PHP: Основные директивы
- PHP: Настройка ошибок
- PHP: FPM: конфигурация
- PHP: ini_get
- PHP: ini_set
- PHP: ini_restore
- PHP: ini_get_all
- PHP: ini_parse_quantity
- PHP: php_ini_loaded_file
- PHP: php_ini_scanned_files
- PHP: date
- PHP 8.5.11: core configure source
- PHP 8.5.11: Zend configure source
- PHP 8.5.11: CGI configure
- PHP 8.5.11: CLI configure
- PHP 8.5.11: embed configure
- PHP 8.5.11: phpdbg configure
- PHP 8.5.11: Apache handler configure
- PHP 8.5.11: build requirements
- PHP: testing source builds
- PHP: configuration
- PHP: configure
- PHP: configure.about
- PHP: install.macosx.compile
- PHP: install.unix.solaris