Krótko: Zend Engine to rdzeń PHP. Tłumaczy kod PHP na opcody, wykonuje je w maszynie wirtualnej i zarządza przy tym zmiennymi oraz pamięcią. Od PHP 4 znajduje się w każdej instalacji PHP. Ponieważ luki PHP2Shell tkwią w tym rdzeniu, nie pomagają wtyczki ani aktualizacje CMS, lecz tylko zaktualizowane PHP z restartem.
Czym jest Zend Engine?
Zend Engine to ta część PHP, która wykonuje właściwą pracę. Wszystko inne, na przykład połączenie z serwerem WWW czy rozszerzenia do baz danych, opiera się na niej. Można ją porównać do silnika samochodu: nadwozie i wyposażenie się różnią, a silnik napędza wszystko.
Według podręcznika PHP nazwa powstała z imion twórców, Zeeva Suraskiego i Andiego Gutmansa. Silnik przedstawiono w połowie 1999 roku, a od PHP 4.0, wydanego w maju 2000 roku, był rdzeniem PHP. Od tego czasu był kilkakrotnie gruntownie przepracowywany.
| Wersja PHP | Silnik | Najważniejsza zmiana według podręcznika i RFC |
|---|---|---|
| PHP 4.0 (2000) | Zend Engine | pierwszy wspólny rdzeń, lepsza wydajność |
| PHP 5.0 (2004) | Zend Engine 2.0 | nowy model obiektowy |
| PHP 7.0 (2015) | Zend Engine 3.0 | PHPNG: bardziej zwarte struktury danych, AST |
| PHP 8.0 (2020) | dalszy rozwój | kompilator JIT jako część OPcache |
Elementy: kompilator, maszyna wirtualna, pamięć
Silnik pracuje w wyraźnie rozdzielonych etapach. Od PHP 7 według RFC o abstrakcyjnym drzewie składni są to trzy fazy do bajtkodu:
- Lexer: rozbija kod źródłowy na tokeny, na przykład słowa kluczowe, nazwy zmiennych i ciągi znaków.
- Parser: buduje z tokenów abstrakcyjne drzewo składni (AST), które odwzorowuje strukturę programu.
- Kompilator: tworzy z AST tablice op, czyli listy opcodów, po jednej dla każdego pliku i funkcji.
- Executor (Zend VM): przetwarza opcody. Według RFC dotyczącego JIT do każdego opcode należy funkcja obsługi, która go wykonuje.
- Zarządzanie pamięcią: rezerwuje i zwalnia pamięć dla każdego żądania, pilnując przy tym limitu z
memory_limit.
Tokeny skryptu można uwidocznić za pomocą rozszerzenia Tokenizer. To nieszkodliwe i pouczające:
<?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";
}
}
Wyjście pokazuje wpisy takie jak T_VARIABLE: $summe czy T_LNUMBER: 2. Z takimi właśnie elementami parser pracuje dalej.
Jak Zend Engine zarządza zmiennymi?
Według podręcznika każda zmienna PHP leży w kontenerze o nazwie zval. Zawiera typ i wartość oraz informacje zarządcze, w tym licznik referencji (refcount). Licznik wskazuje, ile nazw zmiennych wskazuje na ten sam kontener.
<?php
$a = str_repeat('x', 10);
$b = $a; // brak kopiowania, obie wskazują na tę samą wartość
$b .= 'y'; // dopiero teraz następuje kopiowanie (copy on write)
unset($a); // licznik maleje, pamięć zostaje zwolniona, gdy osiągnie 0
Gdy licznik osiąga zero, silnik zwalnia pamięć. Dla odwołań cyklicznych między obiektami istnieje dodatkowo garbage collector. Więcej w artykule Zarządzanie pamięcią w PHP.
Przepracowanie PHPNG dla PHP 7 dotyczyło właśnie tego miejsca. Według RFC autorstwa Dmitrego Stogova i Zeeva Suraskiego chodziło o bardziej zwarte struktury danych i zręczniejszą reprezentację wartości. RFC podawał wzrost wydajności od 20 do 110 procent w rzeczywistych aplikacjach.
OPcache i JIT: rozszerzenia silnika
Bez pamięci podręcznej silnik tłumaczy każdy skrypt od nowa przy każdym żądaniu. OPcache trzyma gotowe opcody we wspólnej pamięci. Od PHP 8.0 istnieje dodatkowo kompilator JIT. Według RFC opiera się na DynASM, narzędziu z projektu LuaJIT, i tworzy kod maszynowy bezpośrednio z bajtkodu. Maszyna wirtualna pozostaje przy tym niezmieniona; JIT zastępuje tylko punkty wejścia funkcji obsługi.
Dla operatorów oznacza to: OPcache i JIT przyspieszają silnik, ale nie zmieniają jego zachowania w razie błędów. Luka w rdzeniu pozostaje także przy aktywnej pamięci podręcznej.
Dlaczego luki w silniku są szczególnie poważne
Obie luki PHP2Shell tkwią w rdzeniu PHP. W przypadku CVE-2026-109618 stan braku pamięci (out of memory) może przerwać silnik, gdy dane żądania są dopiero częściowo zainicjowane; przy sprzątaniu nieprawidłowe dane są wtedy traktowane jako prawidłowe. W przypadku CVE-2026-109619 wewnętrzny stan pozostaje po zwolnieniu pamięci i może zostać użyty przez późniejsze żądanie w tym samym workerze.
Poprawione wersje 8.5.12, 8.4.27, 8.3.36 i 8.2.35 zmieniają według podręcznika PHP także zachowanie limitu pamięci: memory_limit jest tam limitem miękkim, a nowe ustawienie memory_hard_limit_percent określa twardą górną granicę. Podręcznik zwraca uwagę, że wersja, która tego ustawienia nie zna, jest dotknięta lukami. W dystrybucjach z backportami dodatkowo liczy się changelog pakietu.
Silnik jest załadowany w każdym procesie PHP. Po aktualizacji należy więc zrestartować wszystkie workery PHP-FPM, procesy Apache z mod_php i pozostałe usługi PHP.
Co dzieje się wewnątrz przy braku pamięci, opisuje artykuł memory_limit i out of memory. Ocenę samej luki podajemy na stronie CVE-2026-109618.
Jak konkretnie pomaga PHP SOS?
- Sprawdzenie wersji i stanu pakietów: Ustalamy, jaki stan silnika faktycznie działa, łącznie z backportami Państwa dystrybucji.
- Aktualizacja i restart: W ramach usługi PHP Patch Service instalujemy aktualizację i kontrolujemy, że nie został żaden proces ze starym kodem.
- Sensowne ustawienie limitów pamięci: Dopasowujemy
memory_limit, nowy limit twardy i limity cgroup do Państwa aplikacji. - Badanie nieprawidłowości: Awarie workerów lub procesy ze statusem wyjścia 137 oceniamy i sprawdzamy, czy może za nimi stać atak.
Częste pytania
Skąd Zend Engine ma swoją nazwę?
Nazwa składa się z imion jej twórców: Zeeva Suraskiego i Andiego Gutmansa. Według podręcznika PHP silnik przedstawiono w połowie 1999 roku, a od PHP 4.0 w maju 2000 roku był rdzeniem PHP.
Czy Zend Engine to odrębny produkt, który muszę zainstalować?
Nie. Zend Engine jest stałym składnikiem każdej instalacji PHP. Kto aktualizuje PHP, automatycznie aktualizuje także silnik. Z komercyjnymi produktami firmy Zend nie ma to nic wspólnego.
Czym jest opcode?
Opcode to pojedyncze polecenie dla maszyny wirtualnej PHP, na przykład dodawanie, wywołanie funkcji lub przypisanie. Kompilator tłumaczy każdy skrypt na ciąg takich poleceń, które silnik następnie wykonuje.
Co Zend Engine ma wspólnego z memory_limit?
Silnik zarządza pamięcią każdego żądania i pilnuje przy tym limitu z memory_limit. Od wydań bezpieczeństwa z 21.10.2026 limit ten jest według podręcznika limitem miękkim, uzupełnionym limitem twardym przez memory_hard_limit_percent.