Trumpai: Zend Engine yra PHP branduolys. Ji paverčia PHP kodą operacijų kodais, vykdo juos virtualioje mašinoje ir tuo metu valdo kintamuosius bei atmintį. Nuo PHP 4 ji yra kiekviename PHP diegime. Kadangi PHP2Shell spragos slypi būtent šiame branduolyje, nepadeda nei papildiniai, nei CMS atnaujinimai, padeda tik atnaujintas PHP su paleidimu iš naujo.
Kas yra Zend Engine?
Zend Engine yra PHP dalis, kuri atlieka tikrąjį darbą. Viskas kita, pavyzdžiui, prijungimas prie žiniatinklio serverio ar duomenų bazių plėtiniai, remiasi ja. Ją galima palyginti su automobilio varikliu: kėbulas ir įranga skiriasi, o variklis viską varo.
Pasak PHP vadovo, pavadinimas sudarytas iš kūrėjų Zeev Suraski ir Andi Gutmans vardų. Variklis pristatytas 1999 m. viduryje ir nuo PHP 4.0, išleisto 2000 m. gegužę, buvo PHP branduolys. Nuo to laiko jis kelis kartus iš esmės pertvarkytas.
| PHP versija | Variklis | Svarbiausias pakeitimas pagal vadovą ir RFC |
|---|---|---|
| PHP 4.0 (2000) | Zend Engine | pirmasis bendras branduolys, geresnis našumas |
| PHP 5.0 (2004) | Zend Engine 2.0 | naujas objektų modelis |
| PHP 7.0 (2015) | Zend Engine 3.0 | PHPNG: kompaktiškesnės duomenų struktūros, AST |
| PHP 8.0 (2020) | tolesnis vystymas | JIT kompiliatorius kaip OPcache dalis |
Dalys: kompiliatorius, virtuali mašina, atmintis
Variklis veikia aiškiai atskirtais etapais. Nuo PHP 7, pasak RFC dėl abstraktaus sintaksės medžio, iki baitkodo yra trys fazės:
- Leksinis analizatorius (lexer): suskaido pirminį tekstą į elementus (tokens), pavyzdžiui, raktažodžius, kintamųjų pavadinimus ir eilutes.
- Sintaksinis analizatorius (parser): iš elementų sukuria abstraktųjį sintaksės medį (AST), kuris atspindi programos struktūrą.
- Kompiliatorius: iš AST sukuria op-masyvus, t. y. operacijų kodų sąrašus, po vieną kiekvienam failui ir funkcijai.
- Vykdytojas (Zend VM): apdoroja operacijų kodus. Pasak JIT RFC, kiekvienam operacijos kodui yra funkcija-tvarkyklė (handler), kuri jį vykdo.
- Atminties valdymas: rezervuoja ir atlaisvina atmintį kiekvienai užklausai ir tuo metu stebi
memory_limitribą.
Skripto elementus galima pamatyti su plėtiniu Tokenizer. Tai nekenksminga ir pamokoma:
<?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";
}
}
Išvestyje matysite įrašus, tokius kaip T_VARIABLE: $summe ar T_LNUMBER: 2. Būtent su šiais elementais toliau dirba sintaksinis analizatorius.
Kaip Zend Engine valdo kintamuosius?
Pasak vadovo, kiekvienas PHP kintamasis guli konteineryje, vadinamame zval. Jame yra tipas ir reikšmė bei valdymo duomenys, įskaitant nuorodų skaitiklį (refcount). Skaitiklis nurodo, kiek kintamųjų pavadinimų rodo į tą patį konteinerį.
<?php
$a = str_repeat('x', 10);
$b = $a; // nekopijuojama, abu rodo į tą pačią reikšmę
$b .= 'y'; // tik dabar nukopijuojama (copy on write)
unset($a); // skaitiklis mažėja, atmintis atlaisvinama pasiekus 0
Kai skaitiklis pasiekia nulį, variklis atlaisvina atmintį. Ciklinėms objektų nuorodoms papildomai yra šiukšlių surinkėjas (garbage collector). Daugiau straipsnyje Atminties valdymas PHP.
PHPNG pertvarka PHP 7 pradėta būtent čia. Pasak Dmitrijaus Stogovo ir Zeev Suraski RFC, tikslas buvo kompaktiškesnės duomenų struktūros ir gudresnis reikšmių pateikimas. RFC nurodė realių programų našumo padidėjimą nuo 20 iki 110 procentų.
OPcache ir JIT: variklio plėtiniai
Be talpyklos variklis kiekvieną skriptą per kiekvieną užklausą verčia iš naujo. OPcache laiko paruoštus operacijų kodus bendroje atmintyje. Nuo PHP 8.0 papildomai yra JIT kompiliatorius. Pasak RFC, jis remiasi DynASM, LuaJIT projekto įrankiu, ir kuria mašininį kodą tiesiogiai iš baitkodo. Virtuali mašina lieka nepakitusi; JIT pakeičia tik tvarkyklių įėjimo taškus.
Eksploatuotojams tai reiškia: OPcache ir JIT pagreitina variklį, bet nekeičia jo elgsenos klaidų atveju. Branduolio spraga lieka ir esant aktyviai talpyklai.
Kodėl variklio spragos ypač rimtos
Abi PHP2Shell spragos yra PHP branduolyje. Esant CVE-2026-109618, atminties trūkumo būsena gali pertraukti variklį, kai užklausos duomenys inicializuoti tik iš dalies; sutvarkymo metu neteisingi duomenys tuomet laikomi teisingais. Esant CVE-2026-109619, vidinė būsena išlieka atlaisvinus atmintį ir gali būti panaudota vėlesnės užklausos tame pačiame darbiniame procese (worker).
Pasak PHP vadovo, ištaisytos versijos 8.5.12, 8.4.27, 8.3.36 ir 8.2.35 keičia ir atminties ribos elgseną: memory_limit ten yra minkštoji riba, o naujas nustatymas memory_hard_limit_percent nustato kietąją viršutinę ribą. Vadovas nurodo, kad versijai, kuri šio nustatymo nepažįsta, spragos galioja. Distribucijose su backport papildomai svarbus paketo pakeitimų žurnalas.
Variklis įkeltas kiekviename PHP procese. Todėl po atnaujinimo reikia iš naujo paleisti visus PHP-FPM darbinius procesus, Apache procesus su mod_php ir kitas PHP tarnybas.
Kas viduje vyksta atminties stygiaus metu, aprašo straipsnis memory_limit ir atminties trūkumas. Pačios spragos įvertinimas pateiktas puslapyje CVE-2026-109618.
Kaip PHP SOS padeda konkrečiai?
- Versijos ir paketo būsenos patikra: nustatome, kokia variklio būsena iš tikrųjų veikia, įskaitant jūsų distribucijos backport.
- Atnaujinimas ir paleidimas iš naujo: per PHP pataisų paslaugą įdiegiame atnaujinimą ir tikriname, kad neliktų proceso su senu kodu.
- Prasmingai nustatyti atminties ribas: suderiname
memory_limit, naująją kietąją ribą ir cgroup ribas su jūsų programa. - Išnagrinėti neįprastumus: darbinių procesų strigtis arba procesus su išėjimo būsena 137 įvertiname ir tikriname, ar už jų gali slypėti ataka.
Dažni klausimai
Iš kur Zend Engine gavo savo pavadinimą?
Pavadinimas sudarytas iš jos kūrėjų vardų: Zeev Suraski ir Andi Gutmans. Pasak PHP vadovo, variklis pristatytas 1999 m. viduryje ir nuo PHP 4.0, 2000 m. gegužę, buvo PHP branduolys.
Ar Zend Engine yra atskiras produktas, kurį reikia įdiegti?
Ne. Zend Engine yra neatskiriama kiekvieno PHP diegimo dalis. Kas atnaujina PHP, automatiškai atnaujina ir variklį. Su komerciniais Zend bendrovės produktais tai nieko bendra neturi.
Kas yra operacijos kodas (opcode)?
Operacijos kodas yra viena PHP virtualios mašinos komanda, pavyzdžiui, sudėtis, funkcijos iškvietimas ar priskyrimas. Kompiliatorius paverčia kiekvieną skriptą tokių komandų seka, kurią variklis paskui vykdo.
Ką Zend Engine turi bendro su memory_limit?
Variklis valdo kiekvienos užklausos atmintį ir tuo metu laikosi memory_limit ribos. Nuo saugumo leidimų 2026-10-21, pasak vadovo, ši riba yra minkštoji, papildyta kietąja riba per memory_hard_limit_percent.