Kort gezegd: PHP is dynamisch getypeerd: het type van een waarde staat pas tijdens de uitvoering vast, en zonder verdere aanwijzingen zet PHP waarden indien nodig automatisch om. Sinds PHP 7 kunnen parameters, retourwaarden en eigenschappen echter van typen worden voorzien, en declare(strict_types=1) maakt de controle streng. Wie de regels kent en strikte vergelijkingen met === gebruikt, voorkomt een hele klasse van logica- en beveiligingsfouten.
Dynamisch getypeerd, maar niet typeloos
In PHP hoeft u bij $preis = 19.90; geen type op te geven. PHP herkent dat het om een kommagetal gaat. Wordt de waarde later met een tekenreeks gecombineerd, dan zet PHP hem om. Dit gedrag heet type juggling, dus automatische typeomzetting. Het maakt de start gemakkelijk, maar kan tot verrassende resultaten leiden.
Voor beslissers is dat relevant, omdat veel oudere PHP-toepassingen zonder typeaanduidingen zijn geschreven. Zulke code is moeilijker te controleren en te moderniseren. Nieuwere projecten gebruiken typen consequent, waardoor fouten eerder zichtbaar worden en de statische analyse met hulpmiddelen als PHPStan of Psalm pas echt effectief wordt.
De typen van PHP in een overzicht
De PHP-handleiding noemt negen interne typen die elke expressie kan hebben. Gewoonlijk worden ze in drie groepen ingedeeld:
| Groep | Typen | Voorbeeld |
|---|---|---|
| Scalaire typen | bool, int, float, string |
true, 42, 3.5, "Text" |
| Samengestelde typen | array, object, callable |
[1, 2], new DateTime() |
| Bijzondere typen | null, resource |
null, een geopend bestand |
Voor typecontroles raadt de handleiding de is_*()-functies aan, zoals is_int(). Om te onderzoeken is get_debug_type() geschikt, dat sinds PHP 8.0.0 de gebruikelijke typenamen levert, terwijl het oudere gettype() bijvoorbeeld "integer" en "double" teruggeeft.
Naast de automatische omzetting is er de uitdrukkelijke: met een cast zoals (int) $wert of met settype() bepaalt de code zelf welk type een waarde moet hebben. Dat is gebruikelijk bij invoer uit formulieren, want alles wat via $_GET of $_POST binnenkomt, is in eerste instantie een tekenreeks of een array. Een cast controleert echter niet of de waarde zinvol was: van "abc" wordt stilzwijgend 0. Voor invoer is een echte controle met filter_var() daarom meestal de betere keuze.
Klassen en interfaces zijn in PHP eveneens typen. Een functie kan dus eisen dat een parameter een object van een bepaalde klasse is of een bepaalde interface implementeert. Hoe klassen, interfaces en enums samenwerken, legt de pagina Objectoriëntatie in PHP uit.
Typedeclaraties: van PHP 7.1 tot 8.3
Typedeclaraties leggen vast welke waarden een functie accepteert en teruggeeft. Het typesysteem is over vele versies gegroeid. Volgens het wijzigingsoverzicht van de handleiding kwamen onder meer deze mogelijkheden erbij:
| Versie | Nieuw |
|---|---|
| 7.1.0 | nullable typen (?int), void, iterable |
| 7.2.0 | object |
| 7.4.0 | getypeerde klasse-eigenschappen |
| 8.0.0 | union-typen (meerdere toegestane typen in één aanduiding), mixed, retourtype static |
| 8.1.0 | intersection-typen (A&B), retourtype never |
| 8.2.0 | DNF-typen, literaltype true, null en false als zelfstandige typen |
| 8.3.0 | getypeerde constanten van klassen, interfaces, traits en enums |
DNF staat voor disjunctieve normaalvorm en maakt combinaties mogelijk zoals (A&B)|null. Wie PHP-versies plant, ziet aan deze tabel ook welke taalmiddelen een toepassing vereist.
strict_types: omzetting of TypeError
Zonder verdere aanduiding werkt PHP in de omzettende modus. Een functie met int-parameters accepteert dan ook 1.5 en maakt daar 1 van; sinds PHP 8.1 is deze verlieslatende omzetting als verouderd gemarkeerd. Met declare(strict_types=1) aan het begin van het bestand werpt PHP in plaats daarvan een 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, in plaats van stilzwijgend 3
Twee finesses zijn belangrijk. Ten eerste geldt strict_types voor aanroepen vanuit het bestand waarin het staat, niet voor de daar gedefinieerde functies. Roept een bestand zonder strict_types dezelfde functie aan, dan wordt er omgezet. Ten tweede is een geheel getal waar een kommagetal wordt verwacht ook in de strikte modus toegestaan.
Losse en strikte vergelijkingen
De operator == vergelijkt na typeomzetting, === vergelijkt waarde en type zonder omzetting. De handleiding raadt in de meeste gevallen === en !== aan, omdat de omzettingsregels niet altijd vanzelfsprekend zijn.
<?php
var_dump(0 == "a"); // PHP 7: true, PHP 8: false
var_dump("1" == "01"); // true, beide zijn numerieke tekenreeksen
var_dump("1" === "01");// false, hetzelfde type, maar een andere tekenreeks
var_dump(1 == true); // true
var_dump(1 === true); // false
Met PHP 8.0 werden vergelijkingen tussen getallen en niet-numerieke tekenreeksen verstandiger. Oudere toepassingen kunnen zich na de upgrade anders gedragen, bijvoorbeeld in switch-blokken. De eveneens met PHP 8.0 ingevoerde match-expressie vergelijkt altijd strikt. Hoe losse vergelijkingen tot beveiligingslekken leiden, bijvoorbeeld bij token- of wachtwoordcontroles, beschrijft het artikel Type juggling: losse vergelijkingen als beveiligingsrisico.
Typen, upgrades en PHP2Shell
Een strikt typesysteem maakt toepassingscode robuuster, maar beschermt niet tegen fouten in de interpreter zelf. De PHP2Shell-lekken CVE-2026-109618 en CVE-2026-109619 zitten in het geheugen- en verzoekbeheer van PHP. Ze zijn verholpen in PHP 8.5.12, 8.4.27, 8.3.36 en 8.2.35; daarna moeten alle PHP-processen opnieuw starten.
Het typesysteem speelt toch een rol, en wel bij de upgrade. Toepassingen die nog op PHP 8.1 of ouder vastzitten, krijgen van het PHP-project geen officiële updates meer. Goed getypeerde code is aanmerkelijk gemakkelijker naar een ondersteunde versie te tillen, omdat hulpmiddelen incompatibiliteiten vroeg vinden. Bij oudere projecten ondersteunt onze migratie naar een ondersteunde PHP-versie.
Activeer strict_types stapsgewijs in nieuwe en herziene bestanden. Zo groeit de typeveiligheid zonder dat oude code in één keer breekt.
Hoe helpt PHP SOS concreet?
- Wij controleren welke PHP-versie uw toepassing minimaal nodig heeft en vergelijken dat met de verholpen versies.
- Wij laten statische analyse over de code lopen en markeren plaatsen met losse vergelijkingen in beveiligingsrelevante controles.
- Wij plannen upgrades zo dat gewijzigde vergelijkingsregels vooraf worden getest.
- Wij voeren de PHP-update door, starten alle workers opnieuw en documenteren de status.
Veelgestelde vragen
Is PHP sterk of zwak getypeerd?
PHP is dynamisch getypeerd: het type van een variabele staat pas tijdens de uitvoering vast. Zonder strict_types zet PHP waarden indien nodig om. Met typedeclaraties en strict_types kunt u de code echter zeer streng maken.
Wat doet declare(strict_types=1)?
Het schakelt voor aanroepen vanuit het betreffende bestand de automatische omzetting van scalaire waarden uit. Past een waarde niet bij het gedeclareerde type, dan werpt PHP een TypeError. De enige uitzondering is een geheel getal waar een kommagetal wordt verwacht.
Waarom is 0 == "a" in PHP 8 onwaar, maar in PHP 7 waar?
Tot PHP 7 werd een niet-numerieke tekenreeks bij vergelijking met een getal in een getal omgezet; van "a" werd 0. Sinds PHP 8.0 vergelijkt PHP in dit geval niet meer numeriek en is het resultaat false.
Beschermen typen tegen beveiligingslekken in PHP zelf?
Nee. Typen voorkomen fouten in uw toepassingscode. Lekken in de interpreter zoals PHP2Shell worden alleen met een PHP-update gedicht. Moderne typefuncties veronderstellen echter actuele PHP-versies en zijn daarmee een goede aanleiding om te upgraden.