Als web developer kom ik het regelmatig tegen: een functie die variabelen van verschillende typen moet kunnen ontvangen, of juist teruggeven. Een enkel type is niet zo’n probleem. Stel dat ik de onderstaande klasse definieer. De waarde in de klasse is altijd een geheel getal. Dat kan ik afvangen door de variabele als ‘integer’ (int) te definiëren.

De setNumber() method accepteert hier een integer als input parameter. De method getNumber() geeft een integer terug als output.
Union Types voor PHP8
Voor de komst van PHP8 was er ook de mogelijkheid om een heel klein beetje union typing te doen. In plaats van alleen een integer, kon je door een vraagtegen toe te voegen ook een null-waarde accepteren. De klasse zou dan zo uit komen te zien:

Dit zorgt ervoor dat setNumber() een getal accepteert, of een lege (null) waarde. getNumber() geeft of een geheel getal terug, of een lege (null) waarde.
Union Types met PHPDoc annotations
Maar wat nu als je naast integers ook gebruik wilt maken van decimale getallen (floats)? Dat betekende, dat je terug moest vallen op PHPDoc annotations.

En eigenlijk was dit een beetje een ‘vieze’ workaround. Door de PHPDoc annotations kan je IDE je helpen bij het ontwikkelen. Deze leest die comments en helpt je als web developer te zorgen voor de juiste waarden.
Maar … het voorkomt niet dat je als web developer een fout maakt. Met de bovenstaande klasse kun je bijvoorbeeld prima zoiets doen:
$getal = new Getal();
$getal->setNumber("DIT IS NATUURLIJK GEEN GETAL");
$waarde = $getal->getNumber();
Bovenstaande code zal prima werken. De setNumber() method plaatst de opgegeven waarde, in dit geval een string, keurig in variable $getal. De getNumber() method geeft keurig die waarde terug. Stel je nu eens voor dat dit een klasse is die gebruikt wordt in een financiële applicatie!
Natuurlijk ben ik me er heel erg van bewust dat je in de methods zelf validatie doet op de waarde, maar daar gaat deze blogpost niet over.
PHP8 biedt echt Union Types
Dat we dit beter willen doen lijkt me een duidelijk verhaal. Gelukkig biedt PHP8 daarom Union Types. Hierdoor kun je meerdere types per parameter opgeven. Ieder type wordt gescheiden door een ‘pipe’: |
Het bovenstaande voorbeeld met Union Types int en float ziet er dan zo uit:

Wanneer ik nu de eerder beschreven voorbeeldcode uit zou voeren, krijg ik direct een foutmelding. De parameter van setNumber() mag enkel een int of een float zijn. De opgeven string voldoet daar niet aan. Ook de output types van getNumber() bieden zekerheid: getNumber() kan alleen een int of een float teruggeven en niets anders!
De grote voordelen
Zoals je in de voorbeelden ziet geven Union Types je meer zekerheid over het soort waarde dat je terugkrijgt en maakt het je code een stuk duidelijker. Daarnaast heeft het gebruik van Union Types nog een aantal andere voordelen:
- Types worden echt afgedwongen, een fout wordt daardoor sneller gevonden
- Omdat ze worden afgedwongen is het minder waarschijnlijk dat type informatie gedateerd of randgevallen gemist worden
- Types worden gecontroleerd bij toekenning, dit zorgt ervoor dat het Liskov Substitution Principle wordt opgevolgd
- Types zijn beschikbaar via ReflectionType
Scope
Je kunt Union Types voor alle ondersteunde PHP typen gebruiken met een paar uitzonderingen. Denk daarbij aan types als void, false pseudo-types enzovoort.