Naast optimalisaties en aanpassingen van de syntax, heeft PHP 8 weer een aantal nieuwe functionaliteiten die het werk van ontwikkelaars makkelijker kunnen maken. Hier volgt een overzicht van een aantal geaccepteerde RFC’s.
Attributes
Voor veel volgers van de ontwikkelingen van PHP 8 zijn attributes de feature waar het meest naar uitgekeken wordt. Dit is ook te zien aan het aantal RFC’s, want een groot deel van de RFC’s verwijzen direct of indirect naar attributes.
De eerste poging om attributes aan PHP toe te voegen stamt uit 2016, deze RFC haalde toen onvoldoende stemmen. Met PHP 8 worden attributes een onderdeel van de taal. De nieuwe RFC is inhoudelijk anders dan het aanvankelijke voorstel voor attributes, maar de belangrijkste reden dat deze nu wel is geaccepteerd, is waarschijnlijk dat de taal nu wel 'rijp' is voor deze toevoeging.
Het concept van attributes is in Java beter bekend als annotations en in Python als decorators. Het is een manier om metadata toe te kennen aan een klasse, interface, eigenschap, (anonieme) functie, methode, klasseconstante of hoedanigheid. Deze metadata kan vervolgens worden uitgelezen met de reflectionextensie.
Ondanks dat PHP geen officiële ondersteuning had voor attributes, werden door verschillende applicaties en frameworks alternatieven bedacht. Een veelgebruikt alternatief is het gebruik van annotations in doc-comments, waar een regel in de doc-comment begint met een apenstaartje gevolgd door een trefwoord dat een speciale betekenis heeft. Het nadeel van het gebruik van doc-comments is dat de applicatie of framework zelf de inhoud van een doc-comment moet parsen. Door attributes onderdeel te maken van de syntax van PHP kunnen deze attributes simpelweg worden uitgelezen met Tokenizer, een standaardonderdeel van PHP.
Een attribute wordt als een klasse geresolved en geïnitieerd, die zelf voorzien moet zijn van een attribute om aan te geven dat deze geschikt is als attribute. Een attribute kan optioneel argumenten bevatten, die in de constructor van deze klasse worden meegegeven. De RFC laat vrij wat een attribute precies doet. Een attribute kan bijvoorbeeld gebruikt worden door een framework om methodes in een klasse te markeren als een route, of door een ORM om variabelen te voorzien van de benodigde metadata voor het opslagtype.
<?php // PHP 7 class Product { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue */ protected $id; } // PHP 8 class Product { #[ORM\Id] #[ORM\Column(type="integer")] #[ORM\GeneratedValue] protected $id; }
De RFC stipt aan dat het gebruik van attributes daarnaast de weg vrij maakt voor PHP om deze attributes zelf te gaan gebruiken. Zo zouden attributes in de toekomst bijvoorbeeld gebruikt kunnen worden om PHP te helpen met het markeren van functies die wel of niet geschikt zijn voor JIT.
De RFC heeft een makkelijke overwinning behaald, maar er volgden nog enkele RFC’s over de implementatie. In de eerste geaccepteerde versie was er sprake van om << en >> te gebruiken als scheidingstekens voor attributes, maar als snel volgde er een verhitte discussie of dit leesbaar, makkelijk te schrijven is en niet verwarrend is voor de al bestaande bitwise operations. Daarna volgde een RFC om @@ te gebruiken en op de valreep een RFC om toch "#[" als start- en "]" als eindmarkering te gebruiken. Ook andere 'details', zoals het attribute dat een attribute zelf markeert en of een attribute als een enkele token of reeks tokens wordt gezien, zijn besproken in daaropvolgende RFC’s.
Union types
PHP 8 komt met union types ontwikkelaars tegemoet die type hinting willen gebruiken voor meerdere types. Met de union type kan een ontwikkelaar meerdere type hints voor dezelfde parameter specificeren, zonder hiervoor speciaal een Interface aan te maken. Naast meerdere klassen te specificeren, kunnen ook scalars in union types gebruikt worden.
Er is ook een speciaal type false dat in een union type kan worden aangegeven. In PHP zijn er een aantal functies die een waarde teruggeven, of in het geval dat er iets misgegaan is: false. Userlandfuncties kunnen dat met deze pseudo type ook aangeven. Dit is semantisch gezien correcter dan bool als return type, omdat dit impliceert dat er ook true teruggeven zou kunnen worden.
<?php class SimpleCache { private array $storage; public function setData(string $key, string|int|float $data): void { $this->storage[$key] = $data; } public function getData(string $key): string|int|float|false { if ( ! array_key_exists($key, $this->storage)) { return false; } return $this->storage[$key]; } }
Non-capturing catches
In 2013 was er een RFC ingediend voor een anonieme try-catchconstructie. Die RFC is toen gesneuveld voor de stemronde, maar een nieuwe RFC met een bijna identiek voorstel is met een overgrote meerderheid geaccepteerd voor PHP 8. Dankzij deze RFC is het in PHP 8 mogelijk om een try-catchconstructie te schrijven, zonder de exception aan een variabele toe te kennen. Het voorbeeld vanuit de RFC:
<?php try { changeImportantData(); } catch (PermissionException $ex) { echo "You don't have permission to do this"; }
De exception wordt aan de $ex-variabele toegekend, maar niet gebruikt. Dit kan intentioneel zijn, maar wellicht heeft de ontwikkelaar vergeten iets met de exception te doen. In PHP 8 is het mogelijk om dit als volgt te schrijven:
<?php try { changeImportantData(); } catch (PermissionException) { echo "You don't have permission to do this"; }
Hiermee is het voor de lezer duidelijk dat de ontwikkelaar de exception alleen wil afvangen, maar verder niet wil gebruiken.
Named arguments
Met named arguments is het in PHP 8 mogelijk om per argument op te geven welke parameter daar bij hoort. Zo kunnen optionele argumenten worden overgeslagen, en kan het de leesbaarheid vergroten als er meerdere argumenten zijn.
In het volgende voorbeeld wil een developer enkel de parameters string en double_encode doorgeven aan htmlspecialchars, maar omdat de double_encode de vierde parameter is, moet de developer ook parameters 2 en 3 opgeven met de juiste standaardwaarden. Ook is het niet duidelijk bij welke parameter false hoort. In PHP 8 kunnen de andere parameters overgeslagen worden, en is ook direct duidelijk dat false het argument voor double_encode is:
<?php // PHP 7 htmlspecialchars($string, ENT_COMPAT|ENT_HTML401, ini_get('default_charset'), false)); // PHP 8 htmlspecialchars($string, double_encode:false);
Match expression
PHP 8 introduceert de match expression: een variant op de switch statement die een strikte vergelijking doet, geen fallthrough heeft en direct aan een variabele kan worden toegekend.
<?php $winner = match (1337) { 1336 => 'Adriaan', 1337 => 'Henk', 1338 => 'Bassie', };
Nullsafe operator
De nullsafe operator maakt het eenvoudiger om methodes te chainen, zonder expliciet op null te hoeven testen. De nullsafe operator onderbreekt de chain zodra er een null-waarde in de chain optreedt.
<?php // PHP 7 $theAnswer = null; if ($earth !== null) { $human = $earth->extractHuman('Arthur Dent'); if ($human !== null) { $brain = $human->getBrain(); if ($brain !== null) { $theAnswer = $brain->getAnswer(); } } } // PHP 8 $theAnswer = $earth?->extractHuman('Arthur Dent')?->getBrain()?->getAnswer();
Standard PHP Library-aanpassingen
Buiten de engine beschikt PHP ook over een uitgebreide verzameling interne functies: Standard PHP Library, of SPL.
Philipp Tanlak, een PHP-gebruiker, heeft zijn eerste RFC ingediend voor str_contains, een functie die kijkt of een substring voorkomt in een string. PHP heeft al sinds versie 4 de strpos-functie, die de positie van een substring in een string aangeeft en begint bij 0. Een probleem doet zich volgens de RFC voor, wanneer de uitkomst van deze functie wordt vergeleken met een boolean. Het probleem ontstaat doordat een integer 0 naar boolean false wordt gecast als de substring in de string voorkomt op de eerste positie.
De nieuw voorgestelde functie str_contains zal altijd een boolean geven, waardoor dit veiliger is te gebruiken als deze met een boolean true of false wordt vergeleken.
Will Hudgins, eveneens een onbekende in de wereld van RFC’s, heeft voorgesteld om ook str_starts_with en str_ends_with-functies te introduceren, die net als str_contains een boolean teruggeven. Deze RFC is ook geaccepteerd en daarom zullen deze drie functies in PHP 8 beschikbaar zijn.
Deprecations
Ondanks dat RFC’s onder andere beoordeeld worden op backwards compatibility, is PHP 8 een nieuwe major versie en zullen er dus zaken worden verwijderd die niet meer ondersteund zijn.
PHP heeft de conventie om zaken die worden verwijderd, eerst als deprecated te markeren, voordat deze in een major versie worden verwijderd. Wanneer ontwikkelaars dus goed hun logbestanden in de gaten hebben gehouden, en geen minor versie overslaan bij het upgraden, zullen deze afschrijvingen niet onverwachts komen.
Tot slot
Om op de hoogte te blijven van alle veranderingen van PHP kun je de RFC's gaan lezen, je op de nieuwsgroep van PHP abonneren of een PHP-usergroep in de buurt opzoeken.
Voor een volledig overzicht kun je bij de release notes van PHP 8 kijken, en kom je beslagen ten ijs wanneer je gaat upgraden. Wat PHP 8.1 ons zal brengen, is nog niet duidelijk, maar de eerste RFC's zijn alvast geschreven.