# Zend Engine: PHP:n sydän

> Zend Engine selitettynä: kääntäjä, opcodet, virtuaalikone, zval ja muistinhallinta PHP:n ytimessä, sen versiot ja merkitys tietoturvapäivityksille.

Lähde: https://phpsos.de/fi/php/zend-engine/  
Tilanne: 25.10.2026  
Palveluntarjoaja: PHP SOS, PHP-hätäapu ja palvelimen tietoturva. Pikaapu WhatsAppilla: https://wa.me/491736494914?text=Hei%20PHP%20SOS%2C%20tarvitsemme%20apua%20PHP-palvelimemme%20kanssa.%20Yritys%3A%20%E2%80%A6%20Verkkosivusto%3A%20%E2%80%A6

**Lyhyesti:** Zend Engine on PHP:n ydin. Se kääntää PHP-koodin opcode-käskyiksi, suorittaa ne virtuaalikoneessa ja hallinnoi samalla muuttujia ja muistia. PHP 4:stä lähtien se on ollut jokaisessa PHP-asennuksessa. Koska PHP2Shell-haavoittuvuudet ovat tässä ytimessä, lisäosat tai CMS-päivitykset eivät auta, vaan ainoastaan päivitetty PHP ja uudelleenkäynnistys.

## Mikä Zend Engine on?

Zend Engine on se PHP:n osa, joka tekee varsinaisen työn. Kaikki muu, esimerkiksi yhteys verkkopalvelimeen tai tietokantalaajennukset, rakentuu sen päälle. Sitä voi verrata auton moottoriin: korit ja varusteet vaihtelevat, mutta moottori pyörittää kaikkea.

PHP-käsikirjan mukaan nimi on muodostettu kehittäjien Zeev Suraskin ja Andi Gutmansin etunimistä. Moottori esiteltiin vuoden 1999 puolivälissä, ja se oli PHP:n ydin PHP 4.0:sta lähtien, joka ilmestyi toukokuussa 2000. Sen jälkeen sitä on uudistettu perusteellisesti useita kertoja.

| PHP-versio | Moottori | Tärkein muutos käsikirjan ja RFC:iden mukaan |
|---|---|---|
| PHP 4.0 (2000) | Zend Engine | ensimmäinen yhteinen ydin, parempi suorituskyky |
| PHP 5.0 (2004) | Zend Engine 2.0 | uusi olimalli |
| PHP 7.0 (2015) | Zend Engine 3.0 | PHPNG: tiiviimmät tietorakenteet, AST |
| PHP 8.0 (2020) | jatkokehitys | JIT-kääntäjä osana OPcachea |

## Osat: kääntäjä, virtuaalikone, muisti

Moottori työskentelee selvästi erotettujen vaiheiden kautta. PHP 7:stä lähtien abstraktia syntaksipuuta koskevan RFC:n mukaan tavukoodiin on kolme vaihetta:

1. **Lexer:** pilkkoo lähdekoodin tokeneiksi, esimerkiksi avainsanoiksi, muuttujien nimiksi ja merkkijonoiksi.
2. **Parser:** rakentaa tokeneista abstraktin syntaksipuun (AST), joka kuvaa ohjelman rakenteen.
3. **Kääntäjä:** tuottaa AST:stä op-taulukoita eli opcode-luetteloita, yhden kutakin tiedostoa ja funktiota kohden.
4. **Suorittaja (Zend VM):** käy opcodet läpi. JIT-RFC:n mukaan jokaiseen opcodeen kuuluu käsittelijäfunktio, joka suorittaa sen.
5. **Muistinhallinta:** varaa ja vapauttaa muistia jokaiselle pyynnölle ja valvoo samalla `memory_limit`-asetuksen rajaa.

Komentosarjan tokenit voi tehdä näkyviksi Tokenizer-laajennuksella. Se on harmitonta ja opettavaista:

```php
<?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";
    }
}
```

Tuloste näyttää rivejä kuten `T_VARIABLE: $summe` tai `T_LNUMBER: 2`. Juuri näillä rakennuspalikoilla jäsennin jatkaa työtä.

## Miten Zend Engine hallinnoi muuttujia?

Käsikirjan mukaan jokainen PHP-muuttuja on säiliössä nimeltä zval. Se sisältää tyypin ja arvon sekä hallintatietoja, muun muassa viitelaskurin (refcount). Laskuri kertoo, kuinka moni muuttujan nimi osoittaa samaan säiliöön.

```php
<?php
$a = str_repeat('x', 10);
$b = $a;        // ei kopiointia, molemmat osoittavat samaan arvoon
$b .= 'y';      // vasta nyt kopioidaan (copy on write)
unset($a);      // laskuri pienenee, muisti vapautuu, kun se saavuttaa arvon 0
```

Kun laskuri saavuttaa nollan, moottori vapauttaa muistin. Olioiden välisiä kehäviittauksia varten on lisäksi roskienkerääjä (garbage collector). Lisää artikkelissa [Muistinhallinta PHP:ssä](https://phpsos.de/fi/php/speicherverwaltung/).

PHP 7:n PHPNG-uudistus iski juuri tähän. Dmitry Stogovin ja Zeev Suraskin RFC:n mukaan tavoitteena olivat tiiviimmät tietorakenteet ja älykkäämpi arvojen esitys. RFC mainitsi 20–110 prosentin suorituskykyhyödyt todellisissa sovelluksissa.

## OPcache ja JIT: moottorin laajennukset

Ilman välimuistia moottori kääntää jokaisen komentosarjan uudelleen jokaisella pyynnöllä. [OPcache](https://phpsos.de/fi/php/opcache/) pitää valmiit opcodet valmiina jaetussa muistissa. PHP 8.0:sta lähtien mukana on lisäksi JIT-kääntäjä. RFC:n mukaan se perustuu DynASM:iin, LuaJIT-projektin työkaluun, ja tuottaa konekoodia suoraan tavukoodista. Virtuaalikone pysyy samana; JIT korvaa vain käsittelijöiden sisääntulopisteet.

Ylläpitäjälle tämä tarkoittaa: OPcache ja JIT nopeuttavat moottoria, mutta eivät muuta sen käyttäytymistä virhetilanteissa. Ytimen haavoittuvuus säilyy myös aktiivisella välimuistilla.

## Miksi moottorin haavoittuvuudet painavat erityisen paljon

Kumpikin PHP2Shell-haavoittuvuus on PHP:n ytimessä. Haavoittuvuudessa CVE-2026-109618 Out-of-Memory-tila voi keskeyttää moottorin, kun pyyntötiedot on alustettu vasta osittain; siivouksessa virheellisiä tietoja käsitellään sitten kelvollisina. Haavoittuvuudessa CVE-2026-109619 sisäinen tila säilyy muistin vapauttamisen jälkeen, ja myöhempi pyyntö samassa worker-prosessissa voi käyttää sitä.

Korjatut versiot 8.5.12, 8.4.27, 8.3.36 ja 8.2.35 muuttavat PHP-käsikirjan mukaan myös muistirajan käyttäytymistä: `memory_limit` on niissä pehmeä raja, ja uusi asetus `memory_hard_limit_percent` määrittää kovan ylärajan. Käsikirja huomauttaa, että versio, joka ei tunne tätä asetusta, on haavoittuvuuksien piirissä. Backportteja käyttävissä jakeluissa ratkaisevaa on lisäksi paketin muutosloki.

> **Tärkeää:**
> Moottori on ladattuna jokaisessa PHP-prosessissa. Päivityksen jälkeen kaikki PHP-FPM-workerit, mod_php:tä käyttävät Apache-prosessit ja muut PHP-palvelut on siksi käynnistettävä uudelleen.

Mitä sisäisesti tapahtuu muistin loppuessa, kuvaa artikkeli [memory_limit ja Out-of-Memory](https://phpsos.de/fi/php/out-of-memory/). Itse haavoittuvuuden arviointi on sivulla [CVE-2026-109618](https://phpsos.de/fi/php2shell/cve-2026-109618/).

## Miten PHP SOS auttaa konkreettisesti?

- **Version ja pakettitilan tarkistus:** Selvitämme, mikä moottorin versio todella on käynnissä, jakelunne backportit mukaan lukien.
- **Päivitys ja uudelleenkäynnistys:** [PHP-päivityspalvelun](https://phpsos.de/fi/leistungen/php-patch-service/) kautta asennamme päivityksen ja varmistamme, ettei yksikään prosessi jää käyntiin vanhalla koodilla.
- **Muistirajojen järkevä asetus:** Sovitamme `memory_limit`-asetuksen, uuden kovan rajan ja cgroup-rajat sovellukseenne.
- **Poikkeamien selvitys:** Worker-prosessien kaatumiset tai prosessit, joiden poistumistila on 137, arvioimme ja tarkistamme, voiko niiden takana olla hyökkäys.


## Usein kysytyt kysymykset

**Mistä Zend Engine on saanut nimensä?**  
Nimi muodostuu kehittäjien etunimistä: Zeev Suraski ja Andi Gutmans. Moottori esiteltiin PHP-käsikirjan mukaan vuoden 1999 puolivälissä, ja se oli PHP:n ydin PHP 4.0:sta lähtien toukokuusta 2000.

**Onko Zend Engine erillinen tuote, joka minun on asennettava?**  
Ei. Zend Engine on kiinteä osa jokaista PHP-asennusta. Kun PHP päivitetään, myös moottori päivittyy automaattisesti. Tällä ei ole mitään tekemistä Zend-yhtiön kaupallisten tuotteiden kanssa.

**Mikä on opcode?**  
Opcode on yksittäinen käsky PHP:n virtuaalikoneelle, esimerkiksi yhteenlasku, funktiokutsu tai sijoitus. Kääntäjä muuntaa jokaisen komentosarjan tällaisten käskyjen jonoksi, jonka moottori sitten suorittaa.

**Mitä tekemistä Zend Enginellä on memory_limit-asetuksen kanssa?**  
Moottori hallinnoi jokaisen pyynnön muistia ja valvoo samalla memory_limit-asetuksen rajaa. Käsikirjan mukaan 21.10.2026 julkaistuista tietoturvajulkaisuista alkaen tämä raja on pehmeä raja, jota täydentää kova raja asetuksella memory_hard_limit_percent.

## Lähteet

- [PHP-käsikirja: PHP:n historia](https://www.php.net/manual/en/history.php.php)
- [PHP RFC: PHPNG](https://wiki.php.net/rfc/phpng)
- [PHP RFC: JIT](https://wiki.php.net/rfc/jit)
- [PHP-käsikirja: viitelaskenta](https://www.php.net/manual/en/features.gc.refcounting-basics.php)
- [PHP-käsikirja: ydinasetukset (memory_limit)](https://www.php.net/manual/en/ini.core.php)
