Что означает «версия PHP»

Версия PHP — это не просто строка из php -v. В реальном проекте она задаёт доступный синтаксис, набор стандартных функций, поведение расширений, совместимость Composer-пакетов и окно security-фиксов. Код на PHP 8.4 может использовать возможности, которых нет в 8.1; пакет может требовать php: ^8.3; контейнер может запускать CLI 8.4, а веб-сервер — FPM 8.2. Поэтому версию всегда проверяют там, где код реально выполняется.

На 15 июня 2026 года официально поддерживаются ветки 8.2, 8.3, 8.4 и 8.5. PHP 8.2 и 8.3 уже находятся в режиме security fixes only; PHP 8.4 и 8.5 ещё получают active support. После EOL ветка больше не получает исправлений от проекта PHP, и это уже не вопрос вкуса: сервис остаётся с известными уязвимостями, даже если «всё работает». Подробная тема обновлений вынесена в Миграции между версиями PHP.

php -v
php --ini
php -i | grep "Server API"
php -r 'echo PHP_VERSION, "\n", PHP_VERSION_ID, "\n", PHP_SAPI, "\n";'

PHP_VERSION_ID удобен для машинных проверок: например, PHP 8.4.x кодируется как число вида 80400+. Это лучше, чем парсить строку версии вручную.

Быстрое повторение
Почему нельзя считать `php -v` в терминале достаточной проверкой версии PHP для веб-приложения?

SAPI: как PHP подключён к внешнему миру

SAPI — Server API, слой между Zend Engine и окружением, которое запускает PHP. Один и тот же файл может выполняться через разные SAPI и вести себя по-разному: в CLI есть $argv, в веб-запросе есть заголовки и суперглобалы, во встроенном сервере SAPI будет cli-server, в PHP-FPM — обычно fpm-fcgi.

flowchart TD A[Окружение запуска] --> B{SAPI} B --> C[CLI: команды, cron, миграции] B --> D[cli-server: локальная разработка] B --> E[PHP-FPM / FastCGI: веб-запросы] B --> F[Apache module: apache2handler] C --> G[Zend Engine выполняет PHP-код] D --> G E --> G F --> G H[php.ini и расширения] --> G G --> I[Результат: stdout, HTTP-ответ, exit code, логи]
flowchart TD
    A[Окружение запуска] --> B{SAPI}
    B --> C[CLI: команды, cron, миграции]
    B --> D[cli-server: локальная разработка]
    B --> E[PHP-FPM / FastCGI: веб-запросы]
    B --> F[Apache module: apache2handler]
    C --> G[Zend Engine выполняет PHP-код]
    D --> G
    E --> G
    F --> G
    H[php.ini и расширения] --> G
    G --> I[Результат: stdout, HTTP-ответ, exit code, логи]
SAPI определяет, кто запускает PHP и какой контекст получает один и тот же код.

Практически важные варианты:

  • cli — запуск из терминала: миграции, cron, консольные команды, скрипты обслуживания. Подробнее см. CLI и встроенный сервер.
  • cli-server — встроенный dev-сервер php -S. Полезен локально, но не предназначен для production.
  • fpm-fcgi — PHP-FPM через FastCGI, типичная связка с nginx или Apache. Смежная тема: PHP-FPM, RoadRunner и долгоживущие воркеры.
  • cgi-fcgi — CGI/FastCGI-варианты; в современных деплоях чаще встречается именно FPM.
  • apache2handler — PHP как модуль Apache.
  • phpdbg — отладочный SAPI для запуска и анализа кода.

Минимальная диагностика внутри скрипта:

<?php
printf("PHP: %s (%d)\n", PHP_VERSION, PHP_VERSION_ID);
printf("SAPI: %s\n", PHP_SAPI);
printf("Loaded php.ini: %s\n", php_ini_loaded_file() ?: 'none');

Не стоит строить бизнес-логику вокруг SAPI без нужды, но для загрузки bootstrap-файлов, выбора формата логов или запрета веб-запуска консольного скрипта это нормальная проверка.

Быстрое повторение
SAPI в PHP — это слой между чем и чем?

php.ini и разные конфиги для разных режимов

php.ini — основной конфигурационный файл интерпретатора. В нём включают расширения, задают memory_limit, upload_max_filesize, error_reporting, display_errors, date.timezone, opcache.* и десятки других директив. При этом «один PHP» не означает «один php.ini»: CLI и FPM часто читают разные конфиги.

Проверяйте так:

php --ini
php -i | grep "Loaded Configuration File"

А для FPM — через phpinfo() в временном закрытом endpoint, через логи запуска FPM или через конфигурацию образа/пакета. В production phpinfo() нельзя оставлять публично: он раскрывает пути, расширения и настройки окружения.

Важная деталь: php.ini читается при старте PHP. Для CLI это каждый запуск команды. Для веб-рантаймов с долгоживущими процессами после изменения конфига обычно нужен reload/restart соответствующего сервиса: php-fpm, Apache, контейнера или supervisor-процесса. Это особенно важно для opcache, лимитов памяти и включения расширений.

Некоторые директивы меняются не везде. У каждой настройки есть режим изменяемости: INI_ALL, INI_PERDIR, INI_SYSTEM и т. п. Например, часть параметров можно задать через ini_set() в коде, а часть — только на уровне системного конфига. Поэтому «поставлю ini_set() в bootstrap» не является универсальным решением.

Быстрое повторение
Почему «один PHP» не гарантирует «один `php.ini`»?

CLI, FPM, CGI и встроенный сервер: один язык, разные сценарии

CLI-скрипт живёт в процессе команды: стартовал, выполнил код, завершился. Это удобно для задач, где состояние не должно переживать запуск. Веб-запрос через FPM обычно обслуживается worker-процессом, который переживает множество запросов. Суперглобалы вроде $_SERVER, $_GET, $_POST, $_COOKIE, $_FILES раскрываются подробно в SAPI и суперглобалы, но уже здесь важно помнить: их смысл зависит от режима выполнения.

Встроенный сервер запускается так:

php -S localhost:8000 -t public

Он удобен для локального просмотра простых приложений и примеров, но не заменяет nginx/Apache/FPM в production. Официальная документация прямо предупреждает, что built-in server предназначен для разработки. Если нужно понять production-модель с пулами воркеров, keep-alive, перезапусками и утечками состояния, это уже область PHP-FPM, RoadRunner и долгоживущие воркеры.

Почему lifecycle влияет на код

Устаревшая ветка PHP бьёт по проекту в трёх местах. Первое — безопасность: после EOL нет официальных исправлений для новых уязвимостей. Второе — зависимости: библиотеки постепенно поднимают минимальную версию PHP, и composer update начинает конфликтовать с рантаймом. Третье — поддерживаемость: команда не может использовать новые типы, синтаксис и стандартные API без оглядки на старый production.

В Composer это часто фиксируют явно:

{
  "require": {
    "php": "^8.3"
  },
  "config": {
    "platform": {
      "php": "8.3.0"
    }
  }
}

require.php говорит, на каких версиях пакет должен работать. config.platform.php заставляет Composer рассчитывать зависимости так, будто он работает на указанной версии, даже если локально у разработчика стоит более свежий PHP. Это полезно, но опасно как самообман: CI и production всё равно должны реально запускаться на заявленной версии.

Типичные ловушки

Самая частая ловушка — проверить php -v в терминале и решить, что веб-приложение работает на той же версии. На сервере это может быть CLI 8.4 и FPM 8.2. Вторая — поменять php.ini и забыть перезапустить долгоживущий runtime. Третья — считать minor-обновления «безопасными по определению»: PHP старается сохранять совместимость, но deprecations, изменения расширений и новые reserved words всё равно требуют прогонки тестов и чтения migration guide.

Ещё одна практичная привычка: в инцидентах логируйте не только версию приложения, но и PHP_VERSION, PHP_SAPI, путь к php.ini, список критичных расширений и режим opcache. Это резко сокращает время на вопросы вроде «почему в cron работает, а в вебе падает».

См. также

Источники

  1. PHP: Supported Versions
  2. PHP Manual: The configuration file
  3. PHP Manual: Built-in web server
  4. PHP Manual: php_sapi_name
  5. PHP Manual: Command line usage
  6. PHP Manual: List of php.ini directives