PHP 8.3 is volgens plan uitgebracht op 23 november en bevat veel nieuwe features en verbeteringen sinds de lancering van PHP 8.2. Hoewel het officieel wordt beschouwd als een kleine (minor) release, kunnen sommige wijzigingen in 8.3 direct van invloed zijn op hoe je werkt met PHP – bijvoorbeeld doordat je sneller kan coderen en minder bugs tegenkomt.

Laten we eens kijken naar de grote – en soms minder grote – veranderingen die deze laatste release met zich meebrengt.

Nieuwe features en verbeteringen in PHP 8.3

Laten we beginnen met de PHP 8.3 features die het meest in het oog springen.

Typed class constants

De mogelijkheid om types te declaren voor class properties is al beschikbaar sinds PHP 7.4. Maar ondanks talloze aanpassingen aan PHP typing door de jaren heen, is het niet uitgebreid naar constants – tot nu dan.

Class constants – waaronder interface-, trait- en enum constants – kunnen in PHP 8.3 worden getyped, waardoor het minder waarschijnlijk is dat developers afwijken van de bedoeling achter de oorspronkelijke declaratie van een constant.

Hier is een eenvoudig voorbeeld waarbij een interface wordt gebruikt:

// Legal:
interface ConstTest {
    // Declared type and value are both strings
    const string VERSION = "PHP 8.3";
}

// Illegal:
interface ConstTest {
    // Type and value mismatch in this initial declaration
    const float VERSION = "PHP 8.3";
}

De echte waarde van deze typed class constants wordt duidelijk wanneer je werkt in classes die zijn afgeleid van de basisdeclaraties. Hoewel een child class vaak een nieuwe waarde kan toekennen aan een constant, kan PHP 8.3 helpen voorkomen dat het type per ongeluk wordt gewijzigd zodat het incompatibel wordt met de oorspronkelijke declaratie:

class ConstTest {
    const string VERSION = "PHP 8.2";
}

class MyConstTest extends ConstTest {

    // Legal:
    // It's OK to change the value of VERSION here
    const string VERSION = "PHP 8.3";

    // Illegal:
    // Type must be declared if it was specified in the base class
    const VERSION = "PHP 8.3";

    // Illegal:
    // In this case, we can't change the type declared in the 
    // base class, even if the new type and its value are compatible.
    const float VERSION = 8.3;
}

Houd er rekening mee dat het type dat wordt toegewezen aan een class constant kan variëren als je meerdere types “narrowt” of een anders compatibel type gebruikt:

class ConstTest {
    const string|float VERSION = "PHP 8.2";
}

class MyConstTest extends ConstTest {

    // Legal:
    // Here, it's OK to narrow the type declaration to string or float
    const string VERSION = "PHP 8.3";
    const float VERSION = 8.3;

    // Legal:
    // Value could be an int, but it's compatible with float 
    const float VERSION = 8;

    // Illegal:
    // We can't widen the type options here to include int
    const string|float|int VERSION = 8;
}

Twee types die worden ondersteund voor andere properties bij het valideren van retourwaarden – void en never – worden niet ondersteund als class constant types.

Een nieuwe json_validate() functie

Wanneer je met JSON gecodeerde gegevens werkt, is het prettig om te weten of de payload syntactisch geldig is voordat je er iets mee probeert te doen.

In vorige versies van PHP gebruikten developers de functie json_decode() en controleerden ze op fouten terwijl die functie probeerde JSON gegevens om te zetten in associatieve arrays of objecten. De nieuwe json_validate() functie controleert op fouten zonder al het geheugen te gebruiken dat nodig is om die array- of objectstructuren te bouwen.

In het verleden zou je dus een JSON payload als volgt kunnen valideren:

$obj = json_decode($maybeJSON);

if (json_last_error() === JSON_ERROR_NONE) {
    // Probably do something with $obj   
}

Als je niet meteen iets gaat doen met $obj in het voorbeeld hierboven, dan zijn dat veel resources die verbruikt worden om alleen maar de geldigheid van de oorspronkelijke JSON payload te bevestigen. In PHP 8.3 kun je het volgende doen en wat geheugen besparen:

if (json_validate($maybeJSON)) {
    // Do something with $maybeJSON   
}

Opmerking: Het heeft niet veel zin om json_validate() te gebruiken en dan meteen de gegevens door json_decode() te halen, waarbij toch al de geheugenresources van decode worden gebruikt. Het is waarschijnlijker dat je de nieuwe functie gebruikt om de JSON te valideren voordat je het ergens opslaat of aflevert als respons op een verzoek.

Deep cloning van readonly properties

De mogelijkheid om individuele properties van een class te declarer als readonly verscheen in PHP 8.1. PHP 8.2 introduceerde de mogelijkheid om die property toe te wijzen aan een hele class. Veel developers vonden echter dat de beperkingen die werden opgelegd bij het werken met classes die zulke properties bevatten, nuttig programmeren in de weg stonden.

Een RFC voor het wijzigen van readonly gedrag deed twee voorstellen:

  1. Sta classes die geen readonly zijn toe om classes uit te breiden die dat wel zijn
  2. Sta toe dat readonly properties opnieuw geïnitialiseerd worden bij het klonen

Het is het tweede voorstel dat is opgenomen in PHP 8.3. De nieuwe benadering staat toe dat instanties van een class met readonly properties opnieuw worden geïnitialiseerd binnen de __clone magic methode (inclusief via functies die worden gecallt vanuit __clone).

Dit codevoorbeeld uit de RFC laat zien hoe het werkt:

class Foo {
    public function __construct(
        public readonly DateTime $bar,
        public readonly DateTime $baz
    ) {}
 
    public function __clone() {
        // $bar will get a new DateTime when clone is invoked
        $this->bar = clone $this->bar; 

        // And this function will be called
        $this->cloneBaz();
    }
 
    private function cloneBaz() {
       // This is legal when called from within __clone
        unset($this->baz); 
    }
}
 
$foo = new Foo(new DateTime(), new DateTime());
$foo2 = clone $foo;

Nieuw #[\Override] attribuut

Bij het implementeren van interfaces in PHP, voorzien programmeurs gedetailleerde functionaliteit voor methoden die in die interfaces genoemd worden. Bij het maken van een instantie van een class kunnen programmeurs een methode van een parent overschrijven door een alternatieve versie met dezelfde naam en een compatibele handtekening te maken in de child.

Een probleem is dat programmeurs kunnen denken dat ze een interfacemethode implementeren of een parent methode overschrijven terwijl dat niet zo is. Ze maken in plaats daarvan misschien wel een compleet losstaande vanwege een typefout in de naam van de methode van de child class of omdat methoden zijn verwijderd of hernoemd in de code van de parent.

PHP 8.3 introduceert het #[\Override] attribuut om programmeurs te helpen duidelijk te maken dat een methode een bepaalde afkomst moet hebben binnen de code.

Hier is een eenvoudig voorbeeld:

class A {
    protected function ovrTest(): void {}
}

// This will work because ovrTest() 
// can be found in the parent class
class B extends A {
    #[\Override]
    public function ovrTest(): void {}
}

// This will fail because ovrBest() 
// (probably a typo) is not in the parent
class C extends A {
    #[\Override]
    public function ovrBest(): void {}
}

Dynamisch ophalen van class constants en enum members

In tegenstelling tot andere properties in PHP code, was het ophalen van class constants en Enum members met variabele namen wat ingewikkeld. Voor PHP 8.3 deed je dat misschien met de functie constant(), zoals dit:

class MyClass {
    public const THE_CONST = 9;
}

enum MyEnum int {
    case FirstMember = 9;
    case SecondMember = 9;
}

$constantName = 'THE_CONST';
$memberName = 'FirstMember';

echo constant('MyClass::' . $constantName);
echo constant('MyEnum::' . $memberName)->value;

Nu, met dezelfde class en Enum definities als hierboven, kun je hetzelfde resultaat bereiken met PHP 8.3’s dynamisch fetchen van constants zoals hieronder:

$constantName = 'THE_CONST';
$memberName = 'FirstMember';

echo MyClass::{$constantName};
echo MyEnum::{$memberName}->value;

Nieuwe methode getBytesFromString()

Heb je ooit willekeurige strings willen genereren met een vooraf goedgekeurde verzameling tekens? Dat kan nu heel eenvoudig met de getBytesFromString() methode die is toegevoegd aan de Random extensie in PHP 8.3.

Deze nieuwe methode is eenvoudig: je geeft een string door als bronmateriaal en geeft aan hoeveel je er wilt gebruiken. De methode selecteert dan willekeurig bytes uit de string totdat de opgegeven lengte is bereikt.

Hier is een eenvoudig voorbeeld:

$rando = new Random\Randomizer();
$alpha = 'ABCDEFGHJKMNPQRSTVWXYZ';

$rando->getBytesFromString($alpha, 6); //  "MBXGWL"
$rando->getBytesFromString($alpha, 6); //  "LESPMG"
$rando->getBytesFromString($alpha, 6); //  "NVHWXC"

Het is mogelijk dat de gevraagde lengte van de willekeurige uitvoer meer bytes heeft dan de input string:

$rando = new Random\Randomizer();
$nums = '123456';

$rando->getBytesFromString($nums, 10); //  "2526341615"

Met een input string van unieke tekens heeft elke teken evenveel kans om geselecteerd te worden voor het willekeurige resultaat. Karakters kunnen echter worden gewogen door ze vaker te laten voorkomen dan andere in de input. Bijvoorbeeld:

$rando = new Random\Randomizer();
$weighted = 'AAAAA12345';

$rando->getBytesFromString($weighted, 5); //  "1AA53"
$rando->getBytesFromString($weighted, 10); //  "42A5A1AA3A"

Nieuwe methoden getFloat() en nextFloat()

PHP 8.3 breidt ook de Random extensie uit en introduceert twee nieuwe methoden om willekeurige waarden te genereren: getFloat() en nextFloat().

Hier is een voorbeeld:

$rando = new Random\Randomizer();

// Generate a float value between a minimum 
//  value of 0 and a maximum value of 5
$rando->getFloat(0,5); // 2.3937446906217

De methode getFloat() accepteert ook een derde parameter na de minimum- en maximumwaarden. Met behulp van een Random\IntervalBoundary Enum kun je bepalen of de min- en max-waarden zelf kunnen worden geretourneerd door de functie.

Dit zijn de regels:

  • IntervalBoundary::ClosedOpen: min mag worden teruggegeven, maar max niet
  • IntervalBoundary::ClosedClosed: zowel min als max mogen worden teruggegeven
  • IntervalBoundary::OpenClosed: min mag niet worden teruggegeven, max wel
  • IntervalBoundary::OpenOpen: min noch max mogen worden teruggegeven

Wanneer getFloat() wordt gebruikt zonder de Enum als derde parameter te specificeren, is de standaardwaarde IntervalBoundary::ClosedOpen.

Een handig voorbeeld uit de documentatie voor de nieuwe functie genereert willekeurige lengte- en breedtecoördinaten, waarbij breedtegraden -90 en 90 kunnen bevatten, maar lengtegraden niet zowel -180 als 180 kunnen bevatten (omdat ze hetzelfde zijn):

$rando = new Random\Randomizer();

printf(
    "Lat: %+.6f Long: %+.6f",
    $rando->getFloat(-90, 90, \Random\IntervalBoundary::ClosedClosed),

    // -180 will not be used 
    $rando->getFloat(-180, 180, \Random\IntervalBoundary::OpenClosed),
);

De nieuwe methode nextFloat() is in wezen hetzelfde als het gebruik van getFloat() om een willekeurige waarde op te vragen die loopt van 0 tot minder dan 1:

$rando = new Random\Randomizer();

$rando->nextFloat(); // 0.3767414902847

Andere kleine veranderingen in PHP 8.3

PHP 8.3 bevat ook een aantal andere nieuwe functies en kleine wijzigingen. We noemen ze hieronder met links naar aanvullende bronnen (indien beschikbaar):

Afschrijvingen in PHP 8.3

Bij elke nieuwe release van PHP worden sommige functies en instellingen gemarkeerd om uiteindelijk verwijderd te worden. Als deze functies afgeschreven zijn, wordt afgeraden om ze te blijven gebruiken en zullen ze meldingen genereren in veel logs als ze verschijnen in uitvoerende code.

Hier is een lijst van afschrijvingen in PHP 8.3, met links naar aanvullende informatie:

Samenvatting

We hebben gekeken naar de belangrijkste veranderingen in PHP 8.3. Voor een gedetailleerde lijst van elke update in deze versie kun je de officiële changelog van de release bekijken. Als je van plan bent om je code te verplaatsen naar een platform met de nieuwste PHP, dan kan de 8.2-naar-8.3 migratiegids je helpen om uit de problemen te blijven.

Als het jouw taak is om PHP te installeren op je ontwikkel- of productieservers, dan kun je 8.3 downloaden.

Als je een klant van Kinsta bent, kun je erop vertrouwen dat we deze release beschikbaar maken op servers achter je Managed WordPress Hosting of Applicatie Hosting projecten.

Steve Bonisteel Kinsta

Steve Bonisteel is Technical Editor bij Kinsta. Hij begon zijn schrijverscarrière als verslaggever en achtervolgde ambulances en brandweerwagens. Sinds eind jaren negentig schrijft hij over internetgerelateerde technologie.