PHP 8.3 è stato rilasciato puntualmente il 23 novembre e contiene molte nuove funzionalità e miglioramenti rispetto al lancio di PHP 8.2. Anche se è ufficialmente considerata una versione minore, alcune delle modifiche apportate dalla versione 8.3 potrebbero avere un impatto diretto sul vostro lavoro con PHP, aiutandovi a codificare più velocemente e con meno bug.

Vediamo i cambiamenti più importanti, e anche quelli meno importanti, apportati da quest’ultima versione.

Nuove funzionalità e miglioramenti in PHP 8.3

Iniziamo con l’esplorare le caratteristiche di PHP 8.3 che sono state messe sotto i riflettori.

Costanti di classe tipizzate

La possibilità di dichiarare i tipi per le proprietà delle classi è disponibile fin da PHP 7.4. Tuttavia, nonostante le numerose modifiche apportate alla tipizzazione di PHP nel corso degli anni, non era mai stata estesa alle costanti, almeno fino a oggi.

Le costanti di classe – che includono anche le costanti interface, trait ed enum – possono essere tipizzate in PHP 8.3, riducendo la probabilità che gli sviluppatori si allontanino dall’intenzione che sta dietro alla dichiarazione iniziale di una costante.

Ecco un semplice esempio che utilizza un’interfaccia:

// 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";
}

Il vero valore di queste costanti di classe tipizzate si rivela quando si lavora in classi derivate dalle dichiarazioni di base. Sebbene una classe figlia possa spesso assegnare un nuovo valore a una costante, PHP 8.3 può aiutare a evitare di cambiare accidentalmente il suo tipo in modo che diventi incompatibile con la dichiarazione iniziale:

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;
}

Tenete presente che il tipo assegnato a una costante di classe può variare quando si “restringono” più tipi o si utilizza un tipo altrimenti compatibile:

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;
}

Due tipi supportati per altre proprietà quando si convalidano i valori di ritorno – void e never – non sono supportati come tipi di costanti di classe.

Una nuova funzione json_validate()

Quando si lavora con dati codificati in JSON, è utile sapere se il payload è sintatticamente valido prima di tentare di utilizzarlo.

Nelle versioni precedenti di PHP, gli sviluppatori utilizzavano la funzione json_decode() per verificare la presenza di errori durante i tentativi di trasformare i dati JSON in array associativi o oggetti. La nuova funzione di PHP 8.3 json_validate() esegue il controllo degli errori senza utilizzare tutta la memoria necessaria per costruire queste strutture di array o oggetti.

Quindi, in passato, potreste aver convalidato un payload JSON come questo:

$obj = json_decode($maybeJSON);

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

Se non avete intenzione di fare subito qualcosa con $obj nell’esempio precedente, sono molte le risorse utilizzate solo per confermare la validità del payload JSON originale. In PHP 8.3, potreste fare qualcosa di simile e risparmiare memoria:

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

Nota: non ha molto senso utilizzare json_validate() e poi passare immediatamente i dati attraverso json_decode(), utilizzando comunque le risorse di memoria di decode. Sarebbe più sensato utilizzare la nuova funzione per convalidare il JSON prima di memorizzarlo da qualche parte o consegnarlo come risposta a una richiesta.

Deep cloning delle proprietà di readonly

La possibilità di dichiarare le proprietà di singole classi come readonly è apparsa in PHP 8.1. PHP 8.2 ha introdotto la possibilità di assegnare questo attributo a un’intera classe. Tuttavia, molti sviluppatori ritenevano che i vincoli imposti quando si lavorava con classi contenenti tali proprietà ostacolassero una programmazione utile.

Una RFC per modificare il comportamento di readonly ha avanzato due proposte:

  1. Permettere alle classi che non sono readonly di estendere le classi che lo sono
  2. Permettere alle proprietà di readonly di essere reinizializzate durante la clonazione

La seconda proposta è stata inserita in PHP 8.3. Il nuovo approccio consente alle istanze di una classe con proprietà readonly di essere reinizializzate all’interno del metodo magico __clone (anche tramite funzioni invocate da __clone).

Questo esempio di codice tratto dalla RFC ne mostra il funzionamento:

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;

Nuovo attributo #[\Override]

Quando si implementano le interfacce in PHP, i programmatori forniscono funzionalità dettagliate per i metodi denominati in tali interfacce. Quando si crea un’istanza di una classe, i programmatori possono sovrascrivere un metodo parent creando una versione alternativa con lo stesso nome e una firma compatibile nel child.

Un problema è che i programmatori potrebbero pensare di implementare un metodo dell’interfaccia o di sovrascrivere un metodo parent quando non è così. Potrebbero creare qualcosa completamente separato a causa di un errore di battitura nel nome del metodo della classe child o perché i metodi sono stati rimossi o rinominati nel codice del parent.

PHP 8.3 introduce l’attributo #[\Override] per aiutare i programmatori a chiarire che un metodo deve avere una certa discendenza all’interno del codice.

Ecco un esempio di base:

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 {}
}

Recupero dinamico di costanti di classe e membri di enum

A differenza di altre proprietà nel codice PHP, la ricerca di costanti di classe e membri di Enum con nomi di variabili è stata un po’ complicata. Prima di PHP 8.3, si poteva fare utilizzando la funzione constant() come in questo caso:

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;

Ora, utilizzando le stesse definizioni di classe ed Enum di cui sopra, si può ottenere lo stesso risultato con il recupero dinamico delle costanti di PHP 8.3 in questo modo:

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

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

Nuovo metodo getBytesFromString()

Avete mai desiderato generare stringhe casuali utilizzando una raccolta di caratteri già approvata? Ora potete farlo facilmente grazie al metodo getBytesFromString() che è stato aggiunto all’estensione Random di PHP 8.3.

Questo nuovo metodo è semplice: si passa una stringa di caratteri come materiale di partenza e si specifica quanti se ne vogliono utilizzare. Il metodo selezionerà quindi dei byte dalla stringa in modo casuale fino a raggiungere la lunghezza specificata.

Ecco un semplice esempio:

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

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

È possibile che la lunghezza richiesta dell’output casuale abbia più byte della stringa in ingresso:

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

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

Con una stringa di input composta da caratteri unici, ognuno di essi ha le stesse probabilità di essere selezionato per il risultato casuale. Tuttavia, i caratteri possono essere ponderati perché appaiono più spesso di altri nell’input. Ad esempio:

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

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

Nuovi metodi getFloat() e nextFloat()

Ampliando l’estensione Random, PHP 8.3 introduce due nuovi metodi per generare valori casuali float: getFloat() e nextFloat().

Ecco un esempio:

$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

Il metodo getFloat() accetta anche un terzo parametro dopo i valori minimo e massimo. Utilizzando un Enum Random\IntervalBoundary è possibile determinare se i valori minimi e massimi possono essere restituiti dalla funzione.

Ecco le regole:

  • IntervalBoundary::ClosedOpen: il valore minimo può essere restituito, ma non quello massimo
  • IntervalloBoundary::ClosedClosed: possono essere restituiti sia il minimo che il massimo
  • IntervalBoundary::OpenClosed: il valore minimo non può essere restituito, quello massimo sì
  • IntervalBoundary::OpenOpen: non possono essere restituiti né il minimo né il massimo

Quando si utilizza getFloat() senza specificare l’Enum come terzo parametro, il valore predefinito è IntervalBoundary::ClosedOpen.

Un esempio utile fornito dalla documentazione della nuova funzione genera coordinate casuali di longitudine e latitudine, dove la latitudine può includere -90 e 90, ma la longitudine non può includere sia -180 che 180 (poiché sono uguali):

$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),
);

Il nuovo metodo nextFloat() è essenzialmente uguale all’utilizzo di getFloat() per richiedere un valore casuale che va da 0 a meno di 1:

$rando = new Random\Randomizer();

$rando->nextFloat(); // 0.3767414902847

Altri cambiamenti minori in PHP 8.3

PHP 8.3 include anche una serie di altre nuove funzioni e modifiche minori. Le citiamo di seguito con i link alle risorse aggiuntive (se disponibili):

Deprecazioni in PHP 8.3

Con ogni nuova versione di PHP, alcune funzioni e impostazioni vengono segnalate per essere rimosse. Una volta deprecate, queste funzioni non sono raccomandate per un uso continuativo e genereranno degli avvisi in molti log quando appariranno nel codice in esecuzione.

Ecco un elenco di deprecazioni in PHP 8.3, con link a ulteriori informazioni:

Riepilogo

Abbiamo esaminato i cambiamenti più significativi apportati a PHP 8.3. Per un elenco dettagliato di tutti gli aggiornamenti di questa versione, potete consultare il changelog ufficiale del rilascio. Se avete intenzione di spostare il codice su una piattaforma con PHP più recente, la Guida alla migrazione da 8.2 a 8.3 può aiutarvi a farlo senza problemi.

Se è vostro compito installare PHP sui server di sviluppo o di produzione, la versione 8.3 è pronta per essere scaricata.

Se siete clienti Kinsta, penseremo noi a rendere disponibile questa release sui server dei vostri progetti sull’Hosting WordPress gestito o sull’Hosting di Applicazioni.

Steve Bonisteel Kinsta

Steve Bonisteel is a Technical Editor at Kinsta who began his writing career as a print journalist, chasing ambulances and fire trucks. He has been covering Internet-related technology since the late 1990s.