Jak PHP kompiluje if/else, switch a match: analýza opcodes pro vývojáře
PHP-motor nevykonává původní kód přímo — nejprve jej kompiluje na sekvenci bajtkódu (opcodes), kterou pak vykonává Zend VM. Volba mezi if/else, switch a match ovlivňuje nejen čitelnost, ale i strukturu této sekvence: lineární procházení, hashování nebo inline návrat. Pro middle/senior vývojáře je pochopení těchto rozdílů klíčové při navrhování vysokonákladových větvících struktur, zejména v jádrech frameworků, routerech a business logice s desítkami podmínek.
Proč opcodes nejsou abstrakce, ale výkon
Opcodes jsou skutečné instrukce, které Zend VM zpracovává krok za krokem. Jejich počet, pořadí a typ určují CPU cykly, cache missy a předvídatelnost větvic. Například if/else s 10 větvemi v nejhorším případě provede 10 porovnání (IS_IDENTICAL) a až 10 podmíněných skoků (JMPZ). Každý takový skok může způsobit mispredikci v procesoru — zejména při nepravidelných vstupních datech. Naopak switch u řetězcových konstant používá SWITCH_STRING: jednu hash operaci a jeden bezpodmínečný skok (JMP) k potřebné větvi. Rozdíl není teoretický — měří se v taktách a projevuje se v mikrobenchmarcích při více než 10⁶ volání za sekundu.
Srovnání tří přístupů na úrovni instrukcí
Vezmeme si tři implementace stejného algoritmu — přiřazení slevy dle statusu klienta ('gold', 'silver', 'bronze', default). Všechny příklady jsou provedeny na PHP 8.1 s phpdbg -p, bez OPcache optimalizací — tedy ukazují „syrový“ výstup kompilátoru.
if/else: O(N) lineární hledání
Každá větev je samostatná dvojice porovnání a skoku. Není žádná optimalizace pořadí: kompilátor zachovává původní pořadí elseif. Při $status === 'bronze' proběhnou tři IS_IDENTICAL, tři JMPZ a jeden JMP k přiřazení. V nejhorším případě to znamená N porovnání a N−1 skoků.
switch: hybrid hash tabulky a fallback-procházení
Hlavní instrukce je SWITCH_STRING. Při kompilaci PHP vytvoří hash tabulku: každému řetězci ('gold', 'silver') je přiřazen adres a cílové instrukce. Při vykonávání se vypočítá hash $status a VM okamžitě přeskočí k potřebnému bloku. Složitost je amortizovaná O(1). Fallback blok (instrukce 0002–0008 v originále) se aktivuje pouze při kolizi hashu nebo nepravém porovnání — v typickém use-case se neprovede.
match: inline návrat bez skoků
match je výraz, nikoli operátor. Jeho opcodes neobsahují JMP ani JMPZ. Kompilátor generuje sekvenci IS_IDENTICAL + JMPNZ pouze pro kontrolu, ale výsledek je okamžitě přiřazen prostřednictvím ASSIGN do cílové proměnné — bez mezistupňových metek a přechodů. U tří větví to znamená 3 porovnání a 3 přiřazení do stohu, ale konečná hodnota se bere z prvního shodného =>. To dělá match semantičtěji předvídatelnější a bezpečnější (žádné fall-through), ale není rychlejší než switch při velkém počtu větví.
Klíčová omezení a háčky
switch(true)ničí optimalizaci: přicase $x > 100nemůže kompilátor vytvořit hash tabulku — vzniká lineární řetězecIS_SMALLER+JMPNZ, identický sif/else.- Typy klíčů jsou důležité:
SWITCH_LONGfunguje pro celá čísla,SWITCH_STRINGpro řetězce. Smíšené typy nebo objekty nutí motor použít fallback. breakjeJMP: každýbreakse kompiluje na bezpodmínečný skok (JMP → end). Absencebreakznamená, že VM bude pokračovat výkonu podle pořadí — odtud fall-through. To není „chování case“, ale vedlejší efekt absence instrukce.defaultnení zvláštní větev: je to jen metka umístěná v opcodes po všechcase. Pokudcase 'bronze'nemábreak, výkon „propadne“ dodefaultne kvůli logice, ale protože instrukce jdou po sobě.
Co je důležité
if/elsese škáluje lineárně: při 50 větvích v nejhorším případě to znamená 50 porovnání.switchu konstantních řetězců/celých čísel poskytuje O(1) hledání díky kompilaci hash tabulky.matchje bezpečnější a semantičtěji čistší, ale není rychlejší nežswitchpři více než 10 větvích — stále kontrolovalo hodnoty postupně.switch(true)s výrazy vcaseje antipattern: je toif/elses syntaktickým cukrem a dalším overheadem.- Vnořené
if/elseexponenciálně zvyšují počet skoků: 3 úrovně × 5 větví = až 15JMPZa 15JMP.
Pro technické specialisty: pokud váš kód obsahuje řetězce více než 7 elseif nebo se používá v hot path (routing, serializace, validace), nahrazení if/else switchem s konstantními klíči přináší měřitelný zisk v throughput. V PHP 8.0+ by se match měl používat tam, kde je důležitá bezpečnost a čitelnost, ne maximální rychlost.
Optimalizace by měla začínat analýzou opcodes — ne domněnkami. Spusťte phpdbg -p script.php na svém kritickém úseku a spočítejte počet IS_ a JMP. Pokud jich je více než 10 — je čas refactorovat.
— Editorial Team
Zatím žádné komentáře.