Как работает Zend Engine
Проследите путь PHP от исходного кода через опкоды до выполнения
«Как работает Zend Engine» — бесплатный урок PHP Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения PHP Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс PHP Academy содержит 4 уроков всего.
Внутри движка
PHP не интерпретируется построчно непосредственно из исходного кода. Движок Zend компилирует Ваш сценарий в промежуточное представление, называемое опкодами, а затем виртуальная машина выполняет их. Понимание этого конвейера объясняет производительность, кэширование опкодов и компиляцию точно вовремя.
В этом уроке рассматривается путь: исходный код → токены → AST → опкоды → выполнение в VM.
Конвейер
Каждый запрос к файлу PHP проходит через четыре этапа:
- Лексический анализ — исходный текст → токены (сканер на основе генератора лексеров)
- Синтаксический анализ — токены → абстрактное синтаксическое дерево (грамматика синтаксического анализатора)
- Компиляция — AST → массив опкодов (массив операций)
- Выполнение — Zend VM обходит опкоды
Без кэширования опкодов первые три этапа повторяются при каждом запросе.
Токенизация
Лексический анализатор превращает символы в такие токены, как T_VARIABLE, T_ECHO, T_STRING. PHP предоставляет доступ к этому этапу через token_get_all() и PhpToken — именно этим пользуются такие инструменты, как средства исправления кода PHP.
<?php
$src = '<?php $x = 1 + 2; echo $x;';
foreach (PhpToken::tokenize($src) as $tok) {
if ($tok->isIgnorable()) continue; // skip whitespace
printf("%-12s %s\n", $tok->getTokenName(), trim($tok->text));
}
?>AST
Токены разбираются в дерево узлов — например, узел присваивания, дочерними элементами которого являются переменная и выражение с бинарной операцией. PHP создаёт это AST внутри себя, а nikic/php-parser восстанавливает эквивалентное дерево в пользовательском коде для инструментов статического анализа, таких как средства анализа PHP.
<?php
// Conceptual AST for: $x = 1 + 2;
//
// AST_ASSIGN
// ├── AST_VAR ($x)
// └── AST_BINARY_OP (+)
// ├── 1
// └── 2
//
// At compile time PHP folds 1 + 2 into a literal 3
// (constant folding) before generating opcodes.
echo "AST drives opcode generation\n";
?>Опкоды
Компилятор формирует массив операций: плоский список опкодов. У каждого опкода есть номер опкода (например, ZEND_ADD, ZEND_ECHO, ZEND_ASSIGN) и до двух операндов, а также результат; каждый из них может быть скомпилированной переменной (CV), временным значением (TMP) или константой.
<?php
// Opcodes for: $x = 1 + 2; echo $x;
//
// line op operands result
// --- ------------ ----------------- -------
// 1 ADD 1, 2 ~0
// 1 ASSIGN $x, ~0
// 1 ECHO $x
// 1 RETURN 1
//
// ~0 is a temporary; $x is a compiled variable (CV).
echo "op_array is what OPcache stores\n";
?>Просмотр опкодов
Сгенерированные опкоды можно вывести с помощью расширения VLD или параметра opcache.opt_debug_level. Это показывает свёртку констант, удаление мёртвого кода и то, как поток управления превращается в опкоды JMP/JMPZ.
# Dump opcodes with VLD
php -d vld.active=1 -d vld.execute=0 script.php
# Or via OPcache optimizer debug (pre/post optimization)
php -d opcache.opt_debug_level=0x10000 script.php # before opt
php -d opcache.opt_debug_level=0x20000 script.php # after optСкомпилированные переменные (CV)
Локальные переменные в скомпилированном коде не ищутся в хеш-таблице — компилятор назначает каждой из них пронумерованную ячейку CV. Повторный доступ к $x выполняется как обращение по индексу массива, а не как поиск в таблице символов. Это одна из главных причин, по которым локальные переменные работают быстро.
<?php
// Each named local gets a fixed CV slot at compile time:
// $a -> CV0 $b -> CV1 $sum -> CV2
function add(int $a, int $b): int {
$sum = $a + $b; // ADD CV0, CV1 -> CV2
return $sum; // RETURN CV2
}
echo add(2, 3) . PHP_EOL;
?>Цикл выполнения VM
Исполнитель (execute_ex) обходит массив операций. Каждому опкоду соответствует функция-обработчик; PHP может организовать эту диспетчеризацию с помощью огромного оператора выбора, вычисляемых переходов или гибридного варианта (по умолчанию используется самый быстрый вариант в поддерживающих его компиляторах). Указатель на текущую операцию продвигается вперёд; опкоды JMP перемещают его для реализации потока управления.
<?php
// if ($n > 0) echo 'pos'; compiles roughly to:
//
// IS_SMALLER 0, $n -> ~T
// JMPZ ~T, ->L1 ; if false, skip
// ECHO 'pos'
// L1:
// RETURN 1
//
// The VM follows opline; JMPZ rewrites it conditionally.
$n = 5;
if ($n > 0) echo "pos\n";
?>Где применяется кэширование опкодов
Кэширование опкодов сохраняет скомпилированный массив операций в общей памяти, пропуская лексический и синтаксический анализ, а также компиляцию при последующих запросах. Оно также выполняет проход оптимизации (свёртка констант, удаление мёртвого кода, объединение опкодов). VM по-прежнему выполняет кэшированные опкоды при каждом запросе — именно здесь позже помогает компиляция точно вовремя.
Накладные расходы на вызов функций
Вызовы создают новый кадр стека VM: опкоды INIT_FCALL, SEND_VAL/SEND_VAR для каждого аргумента, а затем DO_FCALL. Понимание этого объясняет, почему чрезмерное количество очень маленьких вызовов функций имеет измеримую стоимость и почему встраивание функций и компиляция точно вовремя важны в горячих циклах.
<?php
// square($x) compiles to a call sequence:
// INIT_FCALL 'square'
// SEND_VAR $x
// DO_FCALL -> ~R
// ASSIGN $y, ~R
function square(int $x): int { return $x * $x; }
$total = 0;
for ($i = 1; $i <= 5; $i++) {
$total += square($i); // one call sequence per iteration
}
echo $total . PHP_EOL; // 1+4+9+16+25 = 55
?>Завершение обработки запроса
После выполнения PHP завершает обработку запроса: освобождает переменные, сбрасывает буфер вывода, а затем сбрасывает арену своего распределителя памяти. В модели FPM с изолированными процессами каждый запрос начинается с чистого состояния — поэтому критическая ошибка в одном запросе не может повредить другой. Общие массивы операций кэширования сохраняются между запросами, а состояние исполнителя — нет.
Быстрая проверка
Что именно сохраняет кэш, чтобы не компилировать код повторно?
Повторение
Конвейер Zend выглядит так: лексический анализ → синтаксический анализ → компиляция → выполнение. Исходный код превращается в токены, затем в AST, а затем в массив операций с опкодами, который выполняет цикл исполнителя VM; локальные переменные хранятся в пронумерованных ячейках CV, а вызовы создают кадры стека. Кэширование сохраняет массив операций и результат оптимизации, поэтому при каждом запросе повторяется только выполнение.
Изучай PHP с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 49
- Уроки
- 195
Часто задаваемые вопросы
Урок «Как работает Zend Engine» бесплатный?
Да — полный текст урока «Как работает Zend Engine» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс PHP Academy, подпишись на CoddyKit PRO. Курс PHP Academy содержит 4 уроков всего.
Чему я научусь в уроке «Как работает Zend Engine»?
Проследите путь PHP от исходного кода через опкоды до выполнения Ты практикуешь PHP Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать PHP Academy?
Предыдущий опыт не требуется. PHP Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Как работает Zend Engine»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке PHP Academy?
Да. Каждый урок PHP Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Как работает Zend Engine
- Управление памятью и сборка мусора
- OPcache и JIT-компиляция
- Написание базового расширения PHP на C