Это перевод с английского замечательной статьи Abhinav Singh.
Вам когда-нибудь было любопытно, как же PHP выводит для вас в браузере фразу “Hello World” с помощью echo? Например, у меня не было интереса, пока я не прочёл о внутренностях PHP и его расширениях. Мне подумалось, что, может, некоторым из вас будет занимательно открыть для себя другую сторону PHP, так что приступим. Здесь я коротко освещу тему «Как PHP штампует контент, запрошенный на веб-странице».
Общее описание
Вот что происходит по шагам:
- Мы никогда сами не запускаем PHP-демонов или что-то в этом роде. Когда мы запускаем Apache, то он также запускает PHP-интерпретатор (автор рассматривает среду с веб-сервером Apache, но многие вещи справедливы и для других конфигураций — прим. пер.).
- PHP связан с Apache (SAPI, серверным API) посредством модуля mod_php5.so.
- PHP в целом состоит из трех модулей (ядро PHP, движок Zend и уровень расширений).
- Ядро PHP — это модуль, который обрабатывает запросы, файловые потоки, имеет дело с ошибками и т.п.
- Движок Zend (ZE) преобразует человекочитаемый код в понятные машине лексемы/коды операций (op-codes). Затем он выполняет этот сгенерированный код в виртуальной машине.
- Расширения — это наборы функций, классов, потоков, доступных PHP-скриптам, которые можно использовать в конкретных задачах. Например, расширение mysql для подключения к базе данных MySQL при помощи PHP.
- Когда движок Zend (ZE) выполняет сгенерированный код, скрипт может затребовать доступ к некоторым расширениям. Тогда ZE передаёт управление модулю/слою расширения, который после выполнения задач передаёт управление обратно ZE.
- Наконец, движок Zend возвращает результат обратно ядру PHP, которое передаёт его уровню серверного API, которое в конце концов отображает его в вашем браузере.
На шаг глубже
Но подождите! Это ещё не всё. Выше была приведена лишь высокоуровневая блок-схема. Давайте копнём чуть глубже и посмотрим, что ещё происходит за кулисами:
- Когда мы запускаем Apache, тот также запускает интерпретатор PHP.
- Запуск PHP осуществляется за 2 шага.
- Первый шаг — выполнение начальной установки структур и значений, которые сохраняются всё время жизни SAPI.
- Второй шаг — для временных настроек, которые существуют только для запроса одной страницы.
Шаг 1 запуска PHP
Запутались в шагах 1 и 2? Нет причин для беспокойства, далее мы обсудим то же самое в деталях. Давайте сначала посмотрим на шаг 1, который обычно является основным. Помните, что шаг 1 происходит даже перед тем, как сделан какой-нибудь запрос страницы.
- Когда мы запускаем Apache, он запускает PHP интерпретатор
- PHP вызывает метод MINIT каждого подключённого расширения. Посмотрите на свой файл php.ini, чтобы увидеть модули, подключённые по умолчанию.
- MINIT — это модульная инициализация (Module Initialization). Каждый метод модульной инициализации инициализирует и определяет набор функций, классов, которые будут использованы в будущих запросах страниц.
Типичный MINIT-метод выглядит так:
PHP_MINIT_FUNCTION(extension_name) { /* Инициализация функций, классов и т.д. */ }
Шаг 2 запуска PHP
- Когда сделан запрос страницы, уровень SAPI передаёт управление уровню PHP. PHP затем настраивает среду для выполнения запрошенной PHP страницы. Он создаёт символьную таблицу, в которой будут храниться переменные, использующиеся при выполнении этой страницы.
- Затем PHP вызывает RINIT-метод каждого модуля. RINIT-модуль — это модуль инициализации запроса (Request Initialization Module). Классический пример реализации RINIT-модуля — модуль сессий. RINIT-метод модуля сессий, если подключен в php.ini, предзаполнит переменную $_SESSION и сохранит её в символьной таблице.
- О модуле RINIT можно думать, как о директиве auto_prepend_file, которая добавляется к началу каждого PHP-скрипта перед его выполнением.
Типичный RINIT-метод выглядит так:
PHP_RINIT_FUNCTION(extension_name) { /* Инициализация переменных сессии, предзаполнение переменных, переопределение глобальных переменных и т.д. */ }
Шаг 1 завершения работы PHP
Так же, как и запуск PHP, его остановка осуществляется за 2 шага.
- После завершения выполнения страницы по достижении конца скрипта или из-за вызова функции exit() или die() PHP начинает процесс очистки. Он вызывает метод RSHUTDOWN каждого расширения. RSHUTDOWN можно представить как директиву auto_append_file каждому PHP-скрипту, который будет выполнен при любых обстоятельствах.
- Метод RSHUTDOWN удаляет символьную таблицу (управление памятью) посредством вызова unset() для всех переменных в символьной таблице.
Типичный метод RSHUTDOWN выглядит так:
PHP_RSHUTDOWN_FUNCTION(extension_name) { /* Управление памятью, unset всех переменных, использованных в последнем вызове PHP и т.д. */ }
Шаг 2 завершения работы PHP
Наконец, когда все запросы сделаны, и SAPI готово к завершению работы, PHP приступает к второму шагу завершения работы.
- PHP вызывает метод MSHUTDOWN каждого расширения, что является последним шансом для каждого расширения отменить регистрацию обработчиков и высвободить всю память, выделенную во время MINIT-цикла.
Типичный метод MSHUTDOWN выглядит так:
PHP_MSHUTDOWN_FUNCTION(extension_name) { /* Free handlers and persistent memory etc */ }
И это приводит нас к концу того, что мы называем жизненным циклом PHP. Важно заметить, что шаг 1 запуска и шаг 2 завершения работы происходят, когда не осуществляется никаких запросов к веб-серверам.