0Pricing
PHP Academy · Lekcja

Jak działa Zend Engine

Prześledź drogę PHP od kodu źródłowego przez opcode’y do wykonania

Jak działa Zend Engine to bezpłatna lekcja PHP Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej PHP Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs PHP Academy zawiera 4 lekcji w sumie.

Wewnątrz silnika

PHP nie interpretuje kodu źródłowego wiersz po wierszu. Zend Engine kompiluje skrypt do postaci pośredniej nazywanej kodami operacyjnymi, a następnie wykonuje je maszyna wirtualna. Zrozumienie tego potoku wyjaśnia kwestie wydajności, OPcache i JIT.

W tej lekcji omówiono drogę: kod źródłowy → tokeny → AST → kody operacyjne → wykonywanie przez maszynę wirtualną.

Potok przetwarzania

Każde żądanie pliku PHP przechodzi przez cztery fazy:

  • Analiza leksykalna — tekst źródłowy → tokeny (skaner oparty na re2c)
  • Analiza składniowa — tokeny → Abstract Syntax Tree (gramatyka Bison)
  • Kompilacja — AST → tablica kodów operacyjnych (op_array)
  • Wykonywanie — Zend VM przechodzi po kodach operacyjnych

Bez OPcache pierwsze trzy fazy są powtarzane przy każdym żądaniu.

Tokenizacja

Analizator leksykalny przekształca znaki w tokeny, takie jak T_VARIABLE, T_ECHO i T_STRING. PHP udostępnia ten etap za pośrednictwem token_get_all() / PhpToken — dokładnie z tego korzystają narzędzia takie jak 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));
}
?>

AST

Tokeny są analizowane składniowo i przekształcane w drzewo węzłów — na przykład węzeł przypisania, którego elementami potomnymi są zmienna i wyrażenie operacji binarnej. PHP buduje to AST wewnętrznie, a nikic/php-parser odtwarza równoważną strukturę w kodzie użytkownika na potrzeby narzędzi do analizy statycznej, takich jak 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";
?>

Kody operacyjne

Kompilator emituje op_array — płaską listę kodów operacyjnych. Każdy kod operacyjny ma numer kodu (np. ZEND_ADD, ZEND_ECHO, ZEND_ASSIGN) oraz maksymalnie dwa operandy i wynik, z których każdy jest zmienną skompilowaną (CV), wartością tymczasową (TMP) lub stałą.

<?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";
?>

Inspekcja kodów operacyjnych

Wygenerowane kody operacyjne można wyświetlić za pomocą rozszerzenia VLD lub ustawienia opcache.opt_debug_level w OPcache. Pozwala to zobaczyć składanie stałych, eliminację martwego kodu oraz sposób, w jaki przepływ sterowania zostaje przekształcony w kody operacyjne 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

Zmienne skompilowane (CV)

Zmienne lokalne w skompilowanym kodzie nie wymagają wyszukiwania w tablicy haszującej — kompilator przypisuje każdej z nich numerowane gniazdo CV. Drugie odwołanie do $x jest indeksem tablicy, a nie wyszukiwaniem w tablicy symboli. To jeden z głównych powodów, dla których zmienne lokalne są szybkie.

<?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;
?>

Pętla wykonywania maszyny wirtualnej

Wykonawca (execute_ex) przechodzi po op_array. Każdy kod operacyjny odpowiada funkcji obsługi; PHP może realizować to rozsyłanie za pomocą ogromnej instrukcji switch, obliczanych skoków lub rozwiązania hybrydowego (domyślnego i najszybszego w kompilatorach, które je obsługują). Wskaźnik opline przesuwa się naprzód, a kody operacyjne JMP zmieniają jego położenie na potrzeby sterowania przepływem.

<?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";
?>

Rola OPcache

OPcache buforuje op_array w pamięci współdzielonej, pomijając analizę leksykalną, składniową i kompilację przy kolejnych żądaniach. Wykonuje również etap optymalizacji (składanie stałych, usuwanie martwego kodu, łączenie kodów operacyjnych). Maszyna wirtualna nadal wykonuje buforowane kody operacyjne przy każdym żądaniu — i właśnie w tym pomaga później JIT.

Koszt wywołań funkcji

Wywołania umieszczają na stosie maszyny wirtualnej nową ramkę: kody operacyjne INIT_FCALL, SEND_VAL/SEND_VAR dla każdego argumentu, a następnie DO_FCALL. Pozwala to zrozumieć, dlaczego nadmiar wielu krótkich wywołań funkcji ma mierzalny koszt oraz dlaczego rozwijanie funkcji (inlining) i JIT mają znaczenie w gorących pętlach.

<?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
?>

Kończenie obsługi żądania

Po zakończeniu wykonywania PHP zamyka żądanie: zwalnia zmienne, opróżnia bufor wyjścia, a następnie resetuje arenę alokatora silnika. W modelu FPM typu shared-nothing każde żądanie rozpoczyna się w czystym środowisku — dlatego błąd krytyczny w jednym żądaniu nie może uszkodzić innego. Współdzielone przez OPcache op_arrays przetrwają między żądaniami, ale stan wykonawcy już nie.

Szybki test

Co dokładnie przechowuje OPcache, aby pominąć ponowną kompilację?

Podsumowanie

Potok Zend obejmuje analizę leksykalną → analizę składniową → kompilację → wykonywanie. Kod źródłowy staje się tokenami, następnie AST, a potem tablicą op_array zawierającą kody operacyjne, które wykonuje pętla wykonawcy maszyny wirtualnej. Zmienne lokalne są przechowywane w numerowanych gniazdach CV, a wywołania umieszczają na stosie ramki. OPcache buforuje op_array (wraz z etapem optymalizacji), dzięki czemu przy każdym żądaniu powtarza się tylko wykonywanie.

Często zadawane pytania

Czy lekcja „Jak działa Zend Engine” jest bezpłatna?

Tak — pełny tekst „Jak działa Zend Engine” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu PHP Academy, przejdź na CoddyKit PRO. Kurs PHP Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Jak działa Zend Engine”?

Prześledź drogę PHP od kodu źródłowego przez opcode’y do wykonania Ćwiczysz PHP Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć PHP Academy?

Nie wymagamy żadnego doświadczenia. PHP Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „Jak działa Zend Engine”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji PHP Academy?

Tak. Każda lekcja PHP Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Jak działa Zend Engine
  2. Zarządzanie pamięcią i garbage collection
  3. OPcache i kompilacja JIT
  4. Pisanie podstawowego rozszerzenia PHP w C
← Powrót do PHP Academy