Stručně: PHP je typované dynamicky: typ hodnoty je znám až za běhu a bez dalších pokynů PHP hodnoty podle potřeby automaticky převádí. Od PHP 7 však lze parametry, návratové hodnoty i vlastnosti opatřit typy a declare(strict_types=1) kontrolu zpřísní. Kdo zná pravidla a používá striktní porovnávání pomocí ===, vyhne se celé třídě logických i bezpečnostních chyb.
Dynamicky typované, ale ne bez typů
V PHP není u $preis = 19.90; nutné uvádět typ. PHP pozná, že jde o číslo s plovoucí řádovou čárkou. Pokud se hodnota později spojí s řetězcem, PHP ji převede. Toto chování se nazývá type juggling, tedy automatický převod typů. Usnadňuje začátky, ale může vést k překvapivým výsledkům.
Pro rozhodovatele je to důležité, protože mnoho starších aplikací PHP bylo napsáno bez uvádění typů. Takový kód se hůře kontroluje a modernizuje. Novější projekty typy používají důsledně, takže chyby vyjdou najevo dříve a statická analýza s nástroji, jako jsou PHPStan nebo Psalm, se teprve naplno uplatní.
Přehled typů v PHP
Příručka PHP uvádí devět interních typů, které může mít každý výraz. Obvykle se dělí do tří skupin:
| Skupina | Typy | Příklad |
|---|---|---|
| Skalární typy | bool, int, float, string |
true, 42, 3.5, "Text" |
| Složené typy | array, object, callable |
[1, 2], new DateTime() |
| Zvláštní typy | null, resource |
null, otevřený soubor |
Ke kontrole typů příručka doporučuje funkce is_*(), například is_int(). Ke zkoumání se hodí get_debug_type(), které od PHP 8.0.0 vrací běžné názvy typů, zatímco starší gettype() vrací například "integer" a "double".
Vedle automatického převodu existuje i výslovný: pomocí castu, například (int) $wert, nebo funkcí settype() určuje kód sám, jaký typ má hodnota mít. U vstupů z formulářů je to běžné, protože vše, co přichází přes $_GET nebo $_POST, je zprvu řetězec nebo pole. Cast ale nekontroluje, zda byla hodnota smysluplná: z "abc" se tiše stane 0. U vstupů je proto většinou lepší volbou skutečná kontrola pomocí filter_var().
Třídy a rozhraní jsou v PHP také typy. Funkce tedy může požadovat, aby byl parametr objektem určité třídy nebo splňoval určité rozhraní. Jak spolu třídy, rozhraní a výčty (enums) spolupracují, vysvětluje stránka Objektově orientované programování v PHP.
Deklarace typů: od PHP 7.1 do 8.3
Deklarace typů určují, jaké hodnoty funkce přijímá a vrací. Typový systém rostl v průběhu mnoha verzí. Podle seznamu změn v příručce přibyly mimo jiné tyto možnosti:
| Verze | Novinka |
|---|---|
| 7.1.0 | nullable typy (?int), void, iterable |
| 7.2.0 | object |
| 7.4.0 | typované vlastnosti tříd |
| 8.0.0 | union typy (více povolených typů v jednom zápisu), mixed, návratový typ static |
| 8.1.0 | intersection typy (A&B), návratový typ never |
| 8.2.0 | typy DNF, literálový typ true, null a false jako samostatné typy |
| 8.3.0 | typované konstanty tříd, rozhraní, traitů a výčtů |
DNF znamená disjunktivní normální forma a umožňuje kombinace jako (A&B)|null. Kdo plánuje verze PHP, z této tabulky vidí také to, jaké jazykové prostředky aplikace vyžaduje.
strict_types: převod, nebo TypeError
Bez dalšího nastavení PHP pracuje v režimu s převodem. Funkce s parametry int pak přijme i 1.5 a udělá z toho 1; od PHP 8.1 je tento ztrátový převod označen jako zastaralý. S declare(strict_types=1) na začátku souboru PHP místo toho vyhodí TypeError.
<?php
declare(strict_types=1);
function summe(int $a, int $b): int
{
return $a + $b;
}
echo summe(1, 2); // 3
echo summe(1.5, 2.5); // TypeError, místo tiché 3
Důležité jsou dvě jemnosti. Zaprvé, strict_types platí pro volání ze souboru, ve kterém je uvedeno, nikoli pro tam definované funkce. Pokud tutéž funkci zavolá soubor bez strict_types, hodnoty se převedou. Zadruhé, celé číslo tam, kde se očekává číslo s plovoucí řádovou čárkou, je povoleno i ve striktním režimu.
Volná a striktní porovnání
Operátor == porovnává po převodu typů, === porovnává hodnotu a typ bez převodu. Příručka ve většině případů doporučuje === a !==, protože pravidla převodu nejsou vždy zřejmá.
<?php
var_dump(0 == "a"); // PHP 7: true, PHP 8: false
var_dump("1" == "01"); // true, obě jsou číselné řetězce
var_dump("1" === "01");// false, stejný typ, ale jiný řetězec
var_dump(1 == true); // true
var_dump(1 === true); // false
S PHP 8.0 se porovnávání čísel a nečíselných řetězců stalo rozumnějším. Starší aplikace se po upgradu mohou chovat jinak, například v blocích switch. Výraz match, který byl rovněž zaveden s PHP 8.0, porovnává vždy striktně. Jak volná porovnání vedou k bezpečnostním zranitelnostem, například při kontrole tokenů nebo hesel, popisuje článek Type juggling: volná porovnání jako bezpečnostní riziko.
Typy, upgrady a PHP2Shell
Přísný typový systém činí aplikační kód odolnějším, ale nechrání před chybami v samotném interpretu. Zranitelnosti PHP2Shell CVE-2026-109618 a CVE-2026-109619 leží ve správě paměti a požadavků v PHP. Opraveny jsou v PHP 8.5.12, 8.4.27, 8.3.36 a 8.2.35; poté musí všechny procesy PHP naběhnout znovu.
Typový systém přesto hraje roli, a to při upgradu. Aplikace, které stále uvázly na PHP 8.1 nebo starším, už od projektu PHP nedostávají oficiální aktualizace. Dobře otypovaný kód lze na podporovanou verzi povýšit výrazně snáze, protože nástroje nekompatibility najdou brzy. U starších projektů pomáhá naše migrace na podporovanou verzi PHP.
Zapínejte strict_types postupně v nových a přepracovaných souborech. Typová bezpečnost tak roste, aniž by se starý kód najednou rozbil.
Jak konkrétně pomáhá PHP SOS?
- Zjistíme, jakou minimální verzi PHP vaše aplikace potřebuje, a porovnáme ji s opravenými verzemi.
- Necháme nad kódem proběhnout statickou analýzu a označíme místa s volnými porovnáními v bezpečnostně relevantních kontrolách.
- Upgrady plánujeme tak, aby se změněná pravidla porovnávání předem otestovala.
- Nasadíme aktualizaci PHP, restartujeme všechny workery a zdokumentujeme stav.
Časté dotazy
Je PHP typované silně, nebo slabě?
PHP je typované dynamicky: typ proměnné je znám až za běhu. Bez strict_types PHP hodnoty podle potřeby převádí. S deklaracemi typů a strict_types lze ale kód nastavit velmi přísně.
Co způsobí declare(strict_types=1)?
Pro volání z daného souboru vypne automatický převod skalárních hodnot. Pokud hodnota neodpovídá deklarovanému typu, PHP vyhodí TypeError. Jedinou výjimkou je celé číslo tam, kde se očekává číslo s plovoucí řádovou čárkou.
Proč je 0 == „a“ v PHP 8 nepravda, v PHP 7 ale pravda?
Do PHP 7 se nečíselný řetězec při porovnání s číslem převedl na číslo, z „a“ se stala 0. Od PHP 8.0 PHP v tomto případě už neporovnává numericky a výsledek je false.
Chrání typy před bezpečnostními zranitelnostmi v samotném PHP?
Ne. Typy brání chybám ve vašem aplikačním kódu. Zranitelnosti v interpretu, jako je PHP2Shell, odstraní jen aktualizace PHP. Moderní typové funkce ale vyžadují aktuální verze PHP, a jsou tak dobrým důvodem k upgradu.