En resumen: la Zend Engine es el núcleo de PHP. Traduce el código PHP a opcodes, los ejecuta en una máquina virtual y gestiona para ello las variables y la memoria. Desde PHP 4 está presente en cada instalación de PHP. Como las vulnerabilidades de PHP2Shell están en este núcleo, no sirven los plugins ni las actualizaciones del CMS, sino solo un PHP actualizado con reinicio.
¿Qué es la Zend Engine?
La Zend Engine es la parte de PHP que hace el trabajo propiamente dicho. Todo lo demás, como la conexión con el servidor web o las extensiones para bases de datos, se apoya en ella. Se puede comparar con el motor de un coche: la carrocería y el equipamiento varían, pero el motor lo impulsa todo.
Según el manual de PHP, el nombre se forma con los nombres de pila de los desarrolladores Zeev Suraski y Andi Gutmans. El motor se presentó a mediados de 1999 y fue el núcleo de PHP desde PHP 4.0, aparecido en mayo de 2000. Desde entonces se ha revisado a fondo varias veces.
| Versión de PHP | Motor | Cambio principal según el manual y los RFC |
|---|---|---|
| PHP 4.0 (2000) | Zend Engine | primer núcleo común, mejor rendimiento |
| PHP 5.0 (2004) | Zend Engine 2.0 | nuevo modelo de objetos |
| PHP 7.0 (2015) | Zend Engine 3.0 | PHPNG: estructuras de datos más compactas, AST |
| PHP 8.0 (2020) | evolución | compilador JIT como parte de OPcache |
Los componentes: compilador, máquina virtual, memoria
El motor trabaja en etapas claramente separadas. Desde PHP 7, según el RFC sobre el árbol sintáctico abstracto, hay tres fases hasta el bytecode:
- Lexer: descompone el código fuente en tokens, por ejemplo palabras clave, nombres de variables y cadenas de caracteres.
- Parser: construye a partir de los tokens un árbol sintáctico abstracto (AST), que refleja la estructura del programa.
- Compilador: genera a partir del AST op-arrays, es decir, listas de opcodes, uno por archivo y función.
- Executor (Zend VM): procesa los opcodes. Según el RFC del JIT, cada opcode tiene una función manejadora que lo ejecuta.
- Gestión de memoria: reserva y libera memoria para cada petición y vigila para ello el límite de
memory_limit.
Los tokens de un script pueden hacerse visibles con la extensión Tokenizer. Es inofensivo y didáctico:
<?php
$code = '<?php $summe = 2 + 3; echo $summe;';
foreach (token_get_all($code) as $token) {
if (is_array($token)) {
echo token_name($token[0]), ': ', trim($token[1]), "\n";
} else {
echo 'Zeichen: ', $token, "\n";
}
}
La salida muestra entradas como T_VARIABLE: $summe o T_LNUMBER: 2. Con estos componentes sigue trabajando el parser.
¿Cómo gestiona la Zend Engine las variables?
Según el manual, cada variable de PHP reside en un contenedor llamado zval. Contiene el tipo y el valor, así como datos de gestión, entre ellos un contador de referencias (refcount). El contador indica cuántos nombres de variable apuntan al mismo contenedor.
<?php
$a = str_repeat('x', 10);
$b = $a; // no se copia, ambas apuntan al mismo valor
$b .= 'y'; // solo ahora se copia (copy on write)
unset($a); // el contador baja; se libera memoria cuando llega a 0
Cuando el contador llega a cero, el motor libera la memoria. Para las referencias circulares entre objetos existe además un recolector de basura (garbage collector). Más información en el artículo Gestión de memoria en PHP.
La revisión PHPNG para PHP 7 actuó justo aquí. Según el RFC de Dmitry Stogov y Zeev Suraski, se trataba de estructuras de datos más compactas y una representación más hábil de los valores. El RFC mencionaba mejoras de rendimiento del 20 al 110 por ciento en aplicaciones reales.
OPcache y JIT: ampliaciones del motor
Sin caché, el motor traduce cada script de nuevo en cada petición. OPcache mantiene los opcodes ya generados en la memoria compartida. Desde PHP 8.0 existe además un compilador JIT. Según el RFC, se basa en DynASM, una herramienta del proyecto LuaJIT, y genera código máquina directamente a partir del bytecode. La máquina virtual permanece sin cambios; el JIT solo sustituye los puntos de entrada de los manejadores.
Para los operadores esto significa: OPcache y JIT aceleran el motor, pero no cambian su comportamiento ante errores. Una vulnerabilidad en el núcleo persiste también con la caché activa.
Por qué las vulnerabilidades del motor son especialmente graves
Las dos vulnerabilidades de PHP2Shell están en el núcleo de PHP. En CVE-2026-109618, un estado de falta de memoria puede interrumpir el motor mientras los datos de la petición solo están inicializados en parte; en la limpieza, los datos no válidos se tratan entonces como válidos. En CVE-2026-109619, el estado interno persiste tras liberar la memoria y una petición posterior en el mismo worker puede utilizarlo.
Las versiones corregidas 8.5.12, 8.4.27, 8.3.36 y 8.2.35 cambian, según el manual de PHP, también el comportamiento del límite de memoria: memory_limit es allí un límite blando, y un nuevo ajuste, memory_hard_limit_percent, establece un tope duro. El manual advierte de que una versión que no conozca este ajuste está afectada por las vulnerabilidades. En las distribuciones con backports cuenta además el changelog del paquete.
El motor está cargado en cada proceso de PHP. Por eso, tras la actualización hay que reiniciar todos los workers de PHP-FPM, los procesos de Apache con mod_php y los demás servicios de PHP.
Qué ocurre en el interior ante una escasez de memoria lo describe el artículo memory_limit y falta de memoria. La valoración de la propia vulnerabilidad está en CVE-2026-109618.
¿Cómo ayuda PHP SOS en concreto?
- Comprobar la versión y el estado del paquete: averiguamos qué versión del motor se está ejecutando realmente, incluidos los backports de su distribución.
- Actualizar y reiniciar: mediante el servicio de parcheo de PHP instalamos la actualización y comprobamos que no queda ningún proceso con código antiguo.
- Fijar límites de memoria razonables: ajustamos
memory_limit, el nuevo límite duro y los límites de cgroup a su aplicación. - Investigar anomalías: valoramos los fallos de workers o los procesos con estado de salida 137 y comprobamos si puede haber un ataque detrás.
Preguntas frecuentes
¿De dónde viene el nombre Zend Engine?
El nombre se compone de los nombres de pila de sus desarrolladores: Zeev Suraski y Andi Gutmans. Según el manual de PHP, el motor se presentó a mediados de 1999 y fue el núcleo de PHP desde PHP 4.0, en mayo de 2000.
¿Es la Zend Engine un producto aparte que tenga que instalar?
No. La Zend Engine es parte integrante de cualquier instalación de PHP. Quien actualiza PHP actualiza automáticamente también el motor. No tiene nada que ver con los productos comerciales de la empresa Zend.
¿Qué es un opcode?
Un opcode es una instrucción individual para la máquina virtual de PHP, por ejemplo una suma, una llamada a una función o una asignación. El compilador traduce cada script a una secuencia de estas instrucciones, que el motor procesa después.
¿Qué tiene que ver la Zend Engine con memory_limit?
El motor gestiona la memoria de cada petición y vigila para ello el límite de memory_limit. Desde las versiones de seguridad del 21/10/2026, según el manual, este límite es un límite blando, complementado con un límite duro mediante memory_hard_limit_percent.