So funktioniert die Zend Engine
PHP vom Quellcode über Opcodes bis zur Ausführung verfolgen
So funktioniert die Zend Engine ist eine kostenlose PHP Academy-Lektion auf CoddyKit. Dies ist Lektion 1 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des PHP Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.
Im Inneren der Engine
PHP wird nicht Zeile für Zeile aus dem Quelltext interpretiert. Die Zend Engine kompiliert Ihr Skript in eine Zwischendarstellung namens Opcodes, die anschließend von einer virtuellen Maschine ausgeführt wird. Das Verständnis dieser Pipeline erklärt die Performance, OPcache und JIT.
In dieser Lektion durchlaufen Sie den Weg von Quelltext → Tokens → AST → Opcodes → VM-Ausführung.
Die Pipeline
Jede Anfrage an eine PHP-Datei durchläuft vier Phasen:
- Lexing — Quelltext → Tokens (re2c-basierter Scanner)
- Parsing — Tokens → abstrakter Syntaxbaum (Bison-Grammatik)
- Kompilierung — AST → Opcode-Array (op_array)
- Ausführung — Die Zend VM durchläuft die Opcodes
Ohne OPcache werden die ersten drei Phasen bei jeder Anfrage erneut durchlaufen.
Tokenisierung
Der Lexer wandelt Zeichen in Tokens wie T_VARIABLE, T_ECHO und T_STRING um. PHP stellt diese Phase über token_get_all() / PhpToken bereit – genau das nutzen Tools wie 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));
}
?>Der AST
Tokens werden in einen Baum aus Knoten geparst – etwa einen Zuweisungsknoten, dessen Kinder eine Variable und ein Ausdruck mit binärem Operator sind. PHP erstellt diesen AST intern, und nikic/php-parser rekonstruiert im Userland eine äquivalente Struktur für statische Analysetools wie 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
Der Compiler erzeugt ein op_array: eine flache Liste von Opcodes. Jeder Opcode hat eine Opcode-Nummer (z. B. ZEND_ADD, ZEND_ECHO, ZEND_ASSIGN) und bis zu zwei Operanden sowie ein Ergebnis. Bei jedem davon handelt es sich um eine kompilierte Variable (CV), einen temporären Wert (TMP) oder eine Konstante.
<?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";
?>Opcodes untersuchen
Mit der Erweiterung VLD oder mit opcache.opt_debug_level von OPcache können Sie die erzeugten Opcodes ausgeben. Dadurch werden Constant Folding, Dead-Code-Eliminierung und die Umsetzung des Kontrollflusses in JMP-/JMPZ-Opcodes sichtbar.
# 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 optKompilierte Variablen (CVs)
Lokale Variablen werden in kompiliertem Code nicht über Hash-Tabellen-Lookups gesucht – der Compiler weist jeder einen nummerierten CV-Slot zu. Beim zweiten Zugriff auf $x handelt es sich um einen Array-Index, nicht um eine Suche in der Symboltabelle. Das ist ein wesentlicher Grund dafür, dass lokale Variablen schnell sind.
<?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;
?>Die Ausführungsschleife der VM
Der Executor (execute_ex) durchläuft das op_array. Jedem Opcode ist eine Handler-Funktion zugeordnet. PHP kann diesen Dispatch als riesige switch-Anweisung, als berechnete gotos oder als Hybrid implementieren (standardmäßig als Hybrid und auf unterstützenden Compilern am schnellsten). Der Zeiger opline wird weiterbewegt; JMP-Opcodes verschieben ihn für den Kontrollfluss.
<?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";
?>Die Rolle von OPcache
OPcache speichert das kompilierte op_array im gemeinsamen Speicher und überspringt bei nachfolgenden Anfragen Lexing, Parsing und Kompilierung. Außerdem führt OPcache einen Durchlauf des Optimierers aus (Constant Folding, Dead-Code-Entfernung, Opcode-Fusion). Die VM führt die gecachten Opcodes weiterhin bei jeder Anfrage aus – genau dort kommt später JIT ins Spiel.
Overhead von Funktionsaufrufen
Funktionsaufrufe legen einen neuen VM-Stack-Frame an: die Opcodes INIT_FCALL, SEND_VAL/SEND_VAR für jedes Argument und anschließend DO_FCALL. Das erklärt, warum übermäßig viele kleine Funktionsaufrufe messbare Kosten verursachen und warum Inlining und JIT in häufig ausgeführten Schleifen wichtig sind.
<?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
?>Beenden einer Anfrage
Nach der Ausführung räumt PHP die Anfrage auf: Laufzeitvariablen werden freigegeben, die Ausgabe wird geleert und anschließend setzt die Engine ihre Allocator-Arena zurück. In einem Shared-Nothing-FPM-Modell beginnt jede Anfrage mit einem sauberen Zustand – deshalb kann ein fataler Fehler in einer Anfrage keine andere beschädigen. Die gemeinsamen op_arrays von OPcache bleiben über Anfragen hinweg erhalten; der Zustand des Executors nicht.
Kurztest
Was speichert OPcache genau, um die Neukompilierung zu überspringen?
Zusammenfassung
Die Zend-Pipeline besteht aus Lexing → Parsing → Kompilierung → Ausführung. Aus Quelltext werden Tokens, daraus ein AST und anschließend ein op_array mit Opcodes, den die Ausführungsschleife der VM durchläuft. Lokale Variablen werden in nummerierten CV-Slots gespeichert, und Funktionsaufrufe legen Stack-Frames an. OPcache cached das op_array (zusammen mit einem Optimierungsdurchlauf), sodass sich pro Anfrage nur die Ausführung wiederholt.
Häufig gestellte Fragen
Ist die Lektion „So funktioniert die Zend Engine“ kostenlos?
Ja — der vollständige Text von „So funktioniert die Zend Engine“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des PHP Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „So funktioniert die Zend Engine“?
PHP vom Quellcode über Opcodes bis zur Ausführung verfolgen Du übst PHP Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um PHP Academy zu starten?
Keine Vorkenntnisse erforderlich. PHP Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 1 von 4.
Wie lange dauert die Lektion „So funktioniert die Zend Engine“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser PHP Academy-Lektion Code schreiben und ausführen?
Ja. Jede PHP Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- So funktioniert die Zend Engine
- Speicherverwaltung und Garbage Collection
- OPcache und JIT-Kompilierung
- Eine einfache PHP-Erweiterung in C schreiben