Cómo funciona Zend Engine
Siga PHP desde el código fuente, pasando por los opcodes, hasta la ejecución
Cómo funciona Zend Engine es una lección gratuita de PHP Academy en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de PHP Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de PHP Academy incluye 4 lecciones en total.
Dentro del motor
PHP no interpreta el código fuente línea por línea. El Zend Engine compila su script en una representación intermedia llamada opcodes y, después, una máquina virtual los ejecuta. Comprender esta canalización permite entender el rendimiento, OPcache y JIT.
En esta lección se recorre el flujo código fuente → tokens → AST → opcodes → ejecución en la VM.
La canalización
Cada solicitud a un archivo PHP pasa por cuatro fases:
- Análisis léxico — texto fuente → tokens (analizador basado en re2c)
- Análisis sintáctico — tokens → árbol de sintaxis abstracta (gramática de Bison)
- Compilación — AST → matriz de opcodes (op_array)
- Ejecución — la VM de Zend recorre los opcodes
Sin OPcache, las tres primeras fases se repiten en cada solicitud.
Tokenización
El analizador léxico convierte los caracteres en tokens como T_VARIABLE, T_ECHO y T_STRING. PHP expone esta fase mediante token_get_all() / PhpToken, que es exactamente lo que utilizan herramientas como PHP-CS-Fixer.
<?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));
}
?>El AST
Los tokens se analizan sintácticamente para formar un árbol de nodos: un nodo de asignación cuyos hijos son una variable y una expresión de operación binaria. PHP construye este AST internamente, y nikic/php-parser reconstruye uno equivalente en el espacio de usuario para herramientas de análisis estático como PHPStan.
<?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";
?>Opcodes
El compilador genera un op_array: una lista plana de opcodes. Cada opcode tiene un número de opcode (por ejemplo, ZEND_ADD, ZEND_ECHO y ZEND_ASSIGN) y hasta dos operandos, además de un resultado; cada uno puede ser una variable compilada (CV), un temporal (TMP) o una constante.
<?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";
?>Inspección de opcodes
Puede volcar los opcodes generados mediante la extensión VLD o la opción opcache.opt_debug_level de OPcache. Esto revela la evaluación anticipada de constantes, la eliminación de código inalcanzable y la forma en que el flujo de control se convierte en opcodes 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 optVariables compiladas (CV)
Las variables locales no se buscan en tablas hash en el código compilado; el compilador asigna a cada una una ranura CV numerada. Acceder a $x por segunda vez es un acceso a un índice de matriz, no una búsqueda en la tabla de símbolos. Esta es una de las principales razones por las que las variables locales son rápidas.
<?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;
?>El bucle de ejecución de la VM
El ejecutor (execute_ex) recorre el op_array. Cada opcode se corresponde con una función controladora; PHP puede implementar este despacho mediante un switch enorme, saltos calculados o un enfoque híbrido (la opción predeterminada y más rápida en los compiladores compatibles). El puntero opline avanza; los opcodes JMP lo desplazan para controlar el flujo.
<?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";
?>Dónde encaja OPcache
OPcache almacena el op_array compilado en memoria compartida, omitiendo el análisis léxico, el análisis sintáctico y la compilación en las solicitudes posteriores. También ejecuta una pasada de optimización (evaluación anticipada de constantes, eliminación de código inalcanzable y fusión de opcodes). La VM sigue ejecutando los opcodes almacenados en caché en cada solicitud; ahí es donde JIT resulta útil posteriormente.
Coste de las llamadas a funciones
Las llamadas añaden un nuevo marco a la pila de la VM: los opcodes INIT_FCALL, SEND_VAL/SEND_VAR para cada argumento y, después, DO_FCALL. Comprender esto explica por qué un exceso de llamadas a funciones pequeñas tiene un coste apreciable y por qué la inserción de código/JIT es importante en los bucles de ejecución frecuente.
<?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
?>Cierre de la solicitud
Después de la ejecución, PHP desmantela la solicitud: libera las variables activas, vacía la salida y, después, el motor restablece su arena de asignación. En un modelo FPM de recursos compartidos pero no compartidos entre solicitudes, cada solicitud comienza desde cero; por eso un error fatal en una solicitud no puede corromper otra. Los op_arrays compartidos de OPcache sobreviven entre solicitudes; el estado del ejecutor, no.
Comprobación rápida
¿Qué almacena exactamente OPcache para omitir la recompilación?
Resumen
La canalización de Zend es análisis léxico → análisis sintáctico → compilación → ejecución. El código fuente se convierte en tokens, después en un AST y, finalmente, en un op_array de opcodes que ejecuta el bucle del ejecutor de la VM; las variables locales se almacenan en ranuras CV numeradas y las llamadas añaden marcos a la pila. OPcache almacena el op_array (junto con una pasada de optimización), de modo que en cada solicitud solo se repite la ejecución.
Preguntas frecuentes
¿La lección «Cómo funciona Zend Engine» es gratis?
Sí — el texto completo de «Cómo funciona Zend Engine» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de PHP Academy, actualiza a CoddyKit PRO. El curso de PHP Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Cómo funciona Zend Engine»?
Siga PHP desde el código fuente, pasando por los opcodes, hasta la ejecución Practicas PHP Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar PHP Academy?
No se requiere experiencia previa. PHP Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «Cómo funciona Zend Engine»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de PHP Academy?
Sí. Cada lección de PHP Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Cómo funciona Zend Engine
- Gestión de memoria y recolección de basura
- OPcache y compilación JIT
- Escritura de una extensión básica de PHP en C