PHP 8 dovrebbe essere rilasciato a dicembre 2020 e ci porterà un sacco di fantastiche funzionalità e miglioramenti al linguaggio.

Molte RFC sono già stati approvati e implementati, quindi è giunto il momento per noi di immergerci nell’analisi di alcune delle novità più entusiasmanti che dovrebbero rendere PHP più veloce e affidabile.

Dato che PHP 8 è ancora in fase di sviluppo, potrebbero esservi diversi cambiamenti prima del rilascio finale. Terremo d’occhio tutte le novità e aggiorneremo questo post regolarmente, quindi assicuratevi di non perdere nulla di PHP 8 e tornate a dare un’occhiata a questo articolo di tanto in tanto.

Quindi, quali funzionalità e miglioramenti dobbiamo aspettarci con PHP 8? Qual è la cosa più importante in arrivo con PHP 8, la prossima major release del linguaggio?

Scopriamolo subito!

PHP JIT (Just in Time Compiler)

La funzionalità più acclamata in arrivo con PHP 8 è il Compiler Just-in-time (JIT). Ma cos’è JIT?

La proposta dell’RFC descrive JIT come segue:

“PHP JIT è implementato come parte quasi indipendente di OPcache. Può essere abilitato/disabilitato al momento della compilazione di PHP e al run-time. Quando è abilitato, il codice nativo dei file PHP è memorizzato in una regione aggiuntiva della memoria condivisa di OPcache e gli op_array→opcodes[].handler(s) mantengono i puntatori sugli entry points del codice JIT.”

Allora, come siamo arrivati a JIT e qual è la differenza tra JIT e OPcache?

Per capire meglio cos’è JIT per PHP, diamo una rapida occhiata a come viene eseguito PHP dal codice sorgente fino al risultato finale.

L’esecuzione di PHP consiste in una procedura in 4 fasi:

L’immagine che segue mostra una rappresentazione visuale della procedura di esecuzione di base di PHP.

Procedura di esecuzione di base di PHP

Procedura di esecuzione di base di PHP

Allora, in che modo OPcache rende PHP più veloce? E cosa cambia nella procedura di esecuzione con JIT?

L’Estensione OPcache

PHP è un linguaggio interpretato. Ciò significa che, quando viene eseguito uno script PHP, l’interprete analizza, compila ed esegue il codice più e più volte ad ogni richiesta. Ciò può comportare uno spreco di risorse della CPU e di tempo aggiuntivo.

Qui entra in gioco l’estensione OPcache:

“OPcache migliora le prestazioni di PHP memorizzando il bytecode degli script precompilati nella memoria condivisa, eliminando così la necessità per PHP di caricare e analizzare gli script ad ogni richiesta.”

Con OPcache abilitato, l’interprete PHP attraversa la procedura a 4 fasi di cui sopra solo la prima volta che lo script viene eseguito. Dato che i bytecode PHP sono memorizzati nella memoria condivisa, sono immediatamente disponibili come rappresentazione intermedia di basso livello e possono essere eseguiti subito sulla Zend VM.

Procedura di esecuzione PHP con OPcache abilitata

Procedura di esecuzione di PHP con OPcache abilitata

A partire da PHP 5.5, l’estensione Zend OPcache è disponibile di default e si può verificare se questa è configurata correttamente semplicemente invocando phpinfo() da uno script sul proprio server o controllando il file php.ini (si vedano le impostazioni di configurazione di OPcache).

Sezione Zend OPcache in una pagina phpinfo

Sezione Zend OPcache in una pagina phpinfo

Preloading

OPcache è stata recentemente migliorata con l’implementazione del preloading, una nuova funzionalità di OPcache aggiunta con PHP 7.4. Il preloading fornisce un modo per memorizzare un insieme specifico di script nella memoria di OPcache “prima che venga eseguito qualsiasi codice applicativo“, ma non porta un miglioramento tangibile delle prestazioni per le tipiche applicazioni basate sul web.

Potete leggere di più sul precaricamento nella nostra introduzione a PHP 7.4.

Con JIT, PHP fa un passo avanti.

JIT — Just in Time Compiler

Anche se gli opcode sono sotto forma di rappresentazione intermedia di basso livello, devono comunque essere compilati in codice macchina. JIT “non introduce alcuna forma aggiuntiva di rappresentazione intermedia (IR – Intermediate Representation)”, ma utilizza DynASM (Dynamic Assembler for code generation engines) per generare codice nativo direttamente dal byte-code PHP.

In breve, JIT traduce le parti calde del codice intermedio in codice macchina. Bypassando la compilazione, è in grado di apportare notevoli miglioramenti nelle prestazioni e nell’utilizzo della memoria.

Zeev Surasky, co-autore della proposta di PHP JIT, mostra quanto sarebbero più veloci i calcoli con JIT:

Ma JIT può migliorare davvero le prestazioni di WordPress?

JIT per le Live Web Apps

Secondo l’RFC di JIT, l’implementazione del compilatore just in time dovrebbe migliorare le prestazioni di PHP. Ma sperimenteremo davvero tali miglioramenti in applicazioni reali come WordPress?

I primi test mostrano che JIT renderebbe molto più veloci i carichi di lavoro ad alta intensità di CPU, tuttavia, avverte l’RFC:

“… come i precedenti tentativi – attualmente non sembra migliorare significativamente le applicazioni reali come WordPress (con opcache.jit=1235 326 req/sec vs 315 req/sec).

Si prevede uno sforzo ulteriore per migliorare JIT per le applicazioni real-life, utilizzando profilazione e ottimizzazioni speculative”.

Con JIT abilitato, il codice non verrebbe eseguito dalla Zend VM, ma dalla CPU stessa, e questo migliorerebbe la velocità di calcolo. Le applicazioni web come WordPress si basano anche su altri fattori come TTFB, ottimizzazione del database, richieste HTTP, ecc.

Quindi, per quel che riguarda WordPress e applicazioni simili, non dovremmo aspettarci un grande incremento della velocità di esecuzione di PHP. Tuttavia, JIT potrebbe portare diversi benefici agli sviluppatori.

Secondo Nikita Popov:

“I vantaggi del compilatore JIT sono grossomodo (e come già delineato nell’RFC):

  • Prestazioni significativamente migliori per il codice numerico.
  • Prestazioni leggermente migliori per il “tipico” codice di applicazioni web PHP.
  • La possibilità di spostare più codice da C a PHP, perché PHP sarà ora sufficientemente veloce”.

Così, anche se JIT difficilmente porterà enormi miglioramenti alle prestazioni di WordPress, porterà PHP al livello successivo, permettendo di scrivere funzioni direttamente nel linguaggio.

Il lato negativo, tuttavia, sarebbe la maggiore complessità che potrebbe portare ad un aumento dei costi di manutenzione, stabilità e debugging. Secondo Dmitry Stogov:

“JIT è estremamente semplice, ma in ogni caso aumenta il livello generale di complessità di PHP, comporterà rischi di nuovi tipi di bug e costi di sviluppo e manutenzione”.

La proposta di includere JIT in PHP 8 è passata con 50 voti favorevoli e 2 contrari.

PHP 8 arriverà alla fine dell'anno. 🚀 Scopri le nuove funzionalità nella nostra analisi approfondita!Click to Tweet

PHP 8: Miglioramenti e Nuove Funzionalità

Oltre a JIT, con il rilascio di PHP 8 possiamo aspettarci molte altre funzionalità e miglioramenti. L’elenco che segue è la nostra selezione delle prossime aggiunte e modifiche che dovrebbero rendere PHP più affidabile ed efficiente.

Constructor Property Promotion

Come risultato di una discussione in corso su come migliorare l’ergonomia degli oggetti in PHP, la Constructor Property Promotion RFC propone una nuova e più concisa sintassi che semplifica la dichiarazione delle proprietà, rendendola più breve e meno ridondante.

Questa proposta si riferisce solo ai promoted parameters, cioè quei parametri di metodo preceduti dalle visibility keyword public, protected e private.

Attualmente, tutte le proprietà devono essere ripetute più volte (almeno quattro volte) prima di poter essere utilizzate con gli oggetti. Si consideri il seguente esempio riportato sull’RFC:

class Point {
    public int $x;
    public int $y;
    public int $z;

    public function __construct(
        int $x = 0,
        int $y = 0,
        int $z = 0,
    ) {
        $this->x = $x;
        $this->y = $y;
        $this->z = $z;
    }
}

Secondo Nikita Popov, l’autore della RFC, dobbiamo scrivere il nome della proprietà almeno quattro volte in tre posti diversi: la dichiarazione della proprietà, i parametri del costruttore e l’assegnazione della proprietà. Questa sintassi non è particolarmente usabile, soprattutto nelle classi con un buon numero di proprietà e nomi più descrittivi.

Questa RFC propone di unire il costruttore e la definizione dei parametri. Così, a partire da PHP 8, abbiamo un modo più usabile di dichiarare i parametri e il codice visto sopra può cambiare come mostrato di seguito:

class Point {
    public function __construct(
        public int $x = 0,
        public int $y = 0,
        public int $z = 0,
    ) {}
}

E questo è tutto. Così abbiamo un nuovo modo di promuovere le proprietà che è più breve, più leggibile e meno soggetto a errori. Secondo Nikita:

È una semplice trasformazione sintattica che stiamo facendo. Ma questo riduce la quantità di codice boilerplate che si deve scrivere, in particolare per gli oggetti di valore…

La dichiarazione di proprietà viene trasformata come se avessimo dichiarato esplicitamente tali proprietà e possiamo utilizzare la Reflection API per effettuare un’analisi introspettiva delle definizioni delle proprietà prima dell’esecuzione (si veda Desugaring):

La Reflection (e altri meccanismi di introspezione) osserverà lo stato dopo il desugaring. Ciò significa che le proprietà promosse appariranno allo stesso modo delle proprietà dichiarate esplicitamente, e gli argomenti del costruttore promosso appariranno come argomenti ordinari del costruttore.

// before desugaring
class Point {
    public function __construct(public int $x = 0) {}
}

// after desugaring
class Point {
    public int $x;

    public function __construct(int $x = 0) {
        $this->x = $x;
    }
}

Ereditarietà

Non abbiamo alcuna limitazione nell’utilizzo dell’ereditarietà con la promozione dei parametri. In ogni caso, non c’è una relazione particolare tra i costruttori di classi genitori e classi figlie. Secondo Nikita:

Di solito, diciamo che i metodi devono sempre essere compatibili con il metodo della classe genitore. […] ma questa regola non vale per il costruttore. Quindi il costruttore appartiene realmente ad una singola classe, e non deve esserci in alcun modo compatibilità tra i costruttori della classe genitore e della classe figlia.

Ecco un esempio:

class Test {
    public function __construct(
        public int $x = 0
    ) {}
}

class Child extends Test {
    public function __construct(
        $x, 
        public int $y = 0,
        public int $z = 0,
    ) {
        parent::__construct($x);
    }
}

Cosa Non È Consentito con la Promozione delle Proprietà

Le proprietà promosse sono consentite nei costruttori non astratti e nei trait, ma ci sono diverse limitazioni che vale la pena menzionare.

Costruttori Astratti

Le proprietà promosse non sono ammesse nelle classi astratte e nelle interfacce:

abstract class Test {
    // Error: Abstract constructor.
    abstract public function __construct(private $x);
}
 
interface Test {
    // Error: Abstract constructor.
    public function __construct(private $x);
}
Nullability

Uno dei vincoli maggiori è legato alla nullability. In precedenza, quando si utilizzava un tipo che non era esplicitamente nullable, ma con un valore predefinito nullo, il tipo era implicitamente nullable. Ma con i property type, non abbiamo questo comportamento implicito perché i parametri promossi richiedono una dichiarazione di proprietà, e il tipo nullable deve essere dichiarato esplicitamente. Si veda l’esempio seguente riportato nella RFC:

class Test {
    // Error: Using null default on non-nullable property
    public function __construct(public Type $prop = null) {}

    // Correct: Make the type explicitly nullable instead
    public function __construct(public ?Type $prop = null) {}
}
Tipo Callable

Dato che callable non è un tipo supportato per le proprietà, non ci è permesso utilizzare il tipo callable nella promozione dei parametri:

class Test {
    // Error: Callable type not supported for properties.
    public function __construct(public callable $callback) {}
}
La Keyword var Non È Ammessa

Solo una visibility keyword può essere utilizzata con i parametri promossi, quindi non è consentito dichiarare le proprietà del costruttore con la parola chiave var (si veda il seguente esempio della RFC):

class Test {
    // Error: "var" keyword is not supported.
    public function __construct(var $prop) {}
}
Non Sono Ammesse Duplicazioni

Possiamo combinare proprietà promosse e proprietà esplicite nella stessa classe, ma le proprietà non possono essere dichiarate due volte:

class Test {
    public string $prop;
    public int $explicitProp;

    // Correct
    public function __construct(public int $promotedProp, int $arg) {
        $this->explicitProp = $arg;
    }

    // Error: Redeclaration of property.
    public function __construct(public string $prop) {}
}
I Parametri Variadici Non Sono Ammessi

Il motivo è che il tipo dichiarato è diverso dal parametro variadico, che è in realtà un array:

class Test {
    // Error: Variadic parameter.
    public function __construct(public string ...$strings) {}
}

Ulteriori Letture

Per una discussione approfondita della Costructor Property Promotion, ascoltate questa intervista a Nikita Popov. Per una panoramica approfondita dell’ergonomia degli oggetti in PHP, si veda questo articolo e la successiva intervista a Larry Garfield.

Validazione per i Metodi Astratti dei Trait

I Trait sono definiti come “un meccanismo per il riutilizzo del codice in linguaggi a single inheritance come PHP”. Sono utilizzati per dichiarare metodi che possono essere usati in più classi.

Un trait può anche contenere metodi astratti. Questi metodi si limitano a dichiarare la firma del metodo, ma l’implementazione deve essere fatta all’interno della classe che utilizza il trait.

Secondo il manuale PHP,

“I trait supportano l’utilizzo di metodi astratti per imporre requisiti alla classe che li espone”.

Ciò significa anche che le firme dei metodi devono corrispondere. In altre parole, il tipo e il numero di argomenti richiesti devono essere gli stessi.

Ad ogni modo, secondo Nikita Popov, autore dell’RFC, la convalida della firma è attualmente applicata solo in modo irregolare:

Il seguente esempio di Nikita si riferisce al primo caso (firma non forzata):

trait T {
	abstract public function test(int $x);
}
 
class C {
	use T;

	// Allowed, but shouldn't be due to invalid type.
	public function test(string $x) {}
}

Detto questo, questa RFC propone di emettere sempre un errore fatale se il metodo che implementa non è compatibile con il metodo astratto del trait, indipendentemente dalla sua origine:

Fatal error: Declaration of C::test(string $x) must be compatible with T::test(int $x) in /path/to/your/test.php on line 10

Questa RFC è stato approvato all’unanimità.

Firme di Metodi Incompatibili

In PHP, gli errori di ereditarietà dovuti a firme di metodi incompatibili generano o un errore fatale o un warning a seconda di ciò che causa l’errore.

Se una classe sta implementando un’interfaccia, le firme dei metodi incompatibili generano un errore fatale. Secondo la documentazione delle Object Interface:

“La classe che implementa l’interfaccia deve utilizzare una firma di metodo compatibile con l’LSP (Principio di Sostituzione di Liskov). Se non lo fa, si otterrà un errore fatale.”

Ecco un esempio di errore di ereditarietà con un’interfaccia:

interface I {
	public function method(array $a);
}
class C implements I {
	public function method(int $a) {}
}

In PHP 7.4, il codice di cui sopra avrebbe generato il seguente errore:

Fatal error: Declaration of C::method(int $a) must be compatible with I::method(array $a) in /path/to/your/test.php on line 7

Una funzione in una child class con una firma incompatibile genererebbe un warning. Si consideri il seguente codice tratto dall’RFC:

class C1 {
	public function method(array $a) {}
}
class C2 extends C1 {
	public function method(int $a) {}
}

In PHP 7.4, il codice qui sopra non farebbe altro che generare un avviso:

Warning: Declaration of C2::method(int $a) should be compatible with C1::method(array $a) in /path/to/your/test.php on line 7

Ora, questa RFC propone di emettere sempre un errore fatale per le firme di metodo incompatibili. Con PHP 8, il codice che abbiamo visto in precedenza visualizzerebbe quanto segue:

Fatal error: Declaration of C2::method(int $a) must be compatible with C1::method(array $a) in /path/to/your/test.php on line 7

Array che Iniziano con un Indice Negativo

In PHP, se un array inizia con un indice negativo (start_index < 0), gli indici successivi partiranno da 0 (maggiori informazioni su questo punto nella documentazione di array_fill). Si consideri il seguente esempio:

$a = array_fill(-5, 4, true);
var_dump($a);

In PHP 7.4 il risultato sarebbe il seguente:

array(4) {
	[-5]=>
	bool(true)
	[0]=>
	bool(true)
	[1]=>
	bool(true)
	[2]=>
	bool(true)
}

Ora, questa RFC propone di cambiare le cose in modo che il secondo indice sia start_index + 1, qualunque sia il valore di start_index.

In PHP 8, il codice di cui sopra risulterebbe nel seguente array:

array(4) {
	[-5]=>
	bool(true)
	[-4]=>
	bool(true)
	[-3]=>
	bool(true)
	[-2]=>
	bool(true)
}

Dunque, con PHP 8, gli array che iniziano con un indice negativo cambiano il loro comportamento. Per saperne di più sulle retro-incompatibilità si legga la RFC.

Union Types 2.0

Gli Union Types accettano valori che possono essere di diversi tipi. Attualmente, PHP non fornisce supporto per gli Union Types, con l’eccezione della sintassi del tipo ?Type e del tipo speciale iterabile.

Prima di PHP 8, gli Union Types potevano essere specificati solo nelle annotazioni phpdoc, come mostrato nell’esempio seguente tratto dall’RFC:

class Number {
	/**
	 * @var int|float $number
	 */
	private $number;

	/**
	 * @param int|float $number
	 */
	public function setNumber($number) {
		$this->number = $number;
	}

	/**
	 * @return int|float
	 */
	public function getNumber() {
		return $this->number;
	}
}

La RFC Union Types 2.0 propone di aggiungere il supporto per gli Union Type nelle firme delle funzioni, in modo da non fare più affidamento sulla documentazione in linea, ma definisce invece gli Union Types con la sintassi T1|T2|....:

class Number {
	private int|float $number;

	public function setNumber(int|float $number): void {
		$this->number = $number;
	}

	public function getNumber(): int|float {
		return $this->number;
	}
}

Come spiegato da Nikita Popov nella RFC,

“Il supporto degli Union Types nel linguaggio ci permette di spostare più informazioni sui tipi da phpdoc delle firme delle funzioni, con i soliti vantaggi che questo comporta:

  • I tipi sono effettivamente applicati, quindi gli errori possono essere colti in anticipo.
  • Dato che sono imposte, è meno probabile che le informazioni sui tipi diventino obsolete o che non siano aggiornate.
  • I tipi sono controllati durante l’ereditarietà, facendo rispettare il Principio di Sostituzione di Liskov.
  • I tipi sono disponibili tramite Reflection.
  • La sintassi è molto meno da “boilerplate” rispetto a phpdoc”.

Gli Union Types supportano tutti i tipi disponibili, con alcune limitazioni:

Potete leggere di più sugli Union Types V2 nell’RFC.

Errori di Tipo Coerenti per le Funzioni Interne

Quando si passa un parametro di tipo illegale, le funzioni interne e quelle definite dall’utente si comportano in modo diverso.

Le funzioni definite dall’utente generano un TypeError, ma le funzioni interne si comportano in diversi modi, in base a diverse condizioni. In ogni caso, il comportamento tipico è quello di emettere un avvertimento e restituire null. Si consideri il seguente esempio in PHP 7.4:

var_dump(strlen(new stdClass));

Questo risulterebbe nel seguente avviso:

Warning: strlen() expects parameter 1 to be string, object given in /path/to/your/test.php on line 4
NULL

Il comportamento sarebbe diverso se strict_types è abilitato, o se le informazioni sugli argomenti specificano i tipi. In tali scenari, l’errore del tipo viene rilevato e risulta in un TypeError.

Questa situazione comporterebbe ad una serie di problemi ben illustrati nella sezione Issues della RFC.

Per rimuovere queste incongruenze, questa RFC propone far sì che le API interne di analisi dei parametri generino sempre un ThrowError in caso mancata corrispondenza dei tipi dei parametri.

In PHP 8, il codice sopra riportato genera il seguente errore:

Fatal error: Uncaught TypeError: strlen(): Argument #1 ($str) must be of type string, object given in /path/to/your/test.php:4
Stack trace:
#0 {main}
  thrown in /path/to/your/test.php on line 4

throw Come Espressione

In PHP, throw è una dichiarazione, quindi non è possibile utilizzarlo in situazioni in cui è consentita solo un’espressione.

Questa RFC propone di convertire l’istruzione throw in un’espressione in modo che possa essere utilizzata in qualsiasi contesto in cui sono consentite le espressioni. Ad esempio, arrow function, operatore null coalesce, operatori ternari ed elvis, ecc.

Si vedano i seguenti esempi tratti dall’RFC:

$callable = fn() => throw new Exception();

// $value is non-nullable.
$value = $nullableValue ?? throw new InvalidArgumentException();
 
// $value is truthy.
$value = $falsableValue ?: throw new InvalidArgumentException();

Weak Maps

Una weak map è una collezione di dati (oggetti) in cui le chiavi sono referenziate debolmente, il che significa che non viene impedito loro di eliminate (garbage collected).

Per i tuoi siti ti serve di un hosting veloce, sicuro e adatto agli sviluppatori? Kinsta è stato progettato per gli sviluppatori di WordPress e fornisce un sacco di strumenti e un cruscotto potente. Scopri i nostri piani

PHP 7.4 ha aggiunto il supporto per le weak reference come modo per mantenere un riferimento ad un oggetto che non impedisce all’oggetto stesso di essere distrutto. Come sottolineato da Nikita Popov,

“Le weak reference grezze da soli sono di limitata utilità e le weak map nella pratica sono utilizzate molto più spesso. Non è possibile implementare un’efficiente weak map al top delle weak reference di PHP perché non è prevista la possibilità di registrare una callback di distruzione”.

Ecco perché questa RFC introduce una classe WeakMap per creare oggetti da utilizzare come chiavi di weak map che possono essere distrutte e rimosse dalla weak map nel caso in cui non ci siano ulteriori riferimenti all’oggetto key.

Nei processi di lunga durata, ciò eviterebbe perdite di memoria e migliorerebbe le prestazioni. Si veda questo esempio tratto dall’RFC:

$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 42;
var_dump($map);

Con PHP 8, il codice di cui sopra produrrebbe il seguente risultato (vedere il codice in azione qui):

object(WeakMap)#1 (1) {
	[0]=>
	array(2) {
		["key"]=>
		object(stdClass)#2 (0) {
		}
		["value"]=>
		int(42)
	}
}

Se si deregistra l’oggetto, la chiave viene automaticamente rimossa dalla weak map:

unset($obj);
var_dump($map);

Adesso il risultato sarebbe il seguente:

object(WeakMap)#1 (0) {
}

Per una descrizione più dettagliata delle Weak map, si legga la RFC. La proposta è stata approvata all’unanimità.

Virgola Finale negli Elenchi di Parametri

Le virgole finali sono virgole aggiunte ad elenchi di elementi in diversi contesti. PHP 7.2 ha introdotto le virgole finali nella sintassi delle liste, PHP 7.3 ha introdotto le virgole finali nelle chiamate di funzione.

PHP 8 introduce adesso le virgole finali nelle liste di parametri in funzioni, metodi e closure, come mostrato nell’esempio che segue:

class Foo {
	public function __construct(
		string $x,
		int $y,
		float $z, // trailing comma
	) {
		// do something
	}
}

La proposta è passata con 58 voti favorevoli e 1 contrario.

Sintassi ::class Ammessa sugli Oggetti

Per ottenere il nome di una classe, possiamo utilizzare la sintassi Foo\Bar::class. Questa RFC propone di estendere la stessa sintassi agli oggetti, in modo che sia ora possibile recuperare il nome della classe di un dato oggetto, come mostrato nel seguente esempio:

$object = new stdClass;
var_dump($object::class); // "stdClass"
 
$object = null;
var_dump($object::class); // TypeError

A partire da PHP 8, $object::class fornisce lo stesso risultato di get_class($object). Se $object non è un oggetto, allora viene generata un’eccezione TypeError.

La proposta è stata approvata all’unanimità.

Attributi v2

Gli attributi, noti anche come annotazioni, sono una forma di metadati strutturati che possono essere utilizzati per specificare le proprietà di oggetti, elementi o file.

Fino a PHP 7.4, i doc-comment erano l’unico modo per aggiungere metadati alle dichiarazioni di classi, funzioni, ecc. Ora, la Attributes v2 RFC introduce gli attributi in PHP, definendoli come una forma di metadati sintattici strutturati che possono essere aggiunti alle dichiarazioni di classi, proprietà, funzioni, metodi, parametri e costanti.

Gli attributi vengono aggiunti prima delle dichiarazioni a cui si riferiscono. Si vedano i seguenti esempi tratti dall’RFC:

<<ExampleAttribute>>
class Foo
{
	<<ExampleAttribute>>
	public const FOO = 'foo';

	<<ExampleAttribute>>
	public $x;

	<<ExampleAttribute>>
	public function foo(<<ExampleAttribute>> $bar) { }
}

$object = new <<ExampleAttribute>> class () { };

<<ExampleAttribute>>
function f1() { }

$f2 = <<ExampleAttribute>> function () { };

$f3 = <<ExampleAttribute>> fn () => 1;

Gli attributi possono essere aggiunti sia prima che dopo un commento del doc-block:

<<ExampleAttribute>>
/** docblock */
<<AnotherExampleAttribute>>
function foo() {}

Ogni dichiarazione può avere uno o più attributi e ogni attributo può avere uno o più valori associati:

<<WithoutArgument>>
<<SingleArgument(0)>>
<<FewArguments('Hello', 'World')>>
function foo() {}

Si legga la RFC per una descrizione più approfondita degli attributi in PHP, dei casi d’uso e della sintassi alternativa.

Nuove Funzioni di PHP

PHP 8 introduce nel linguaggio diverse nuove funzioni:

str_contains

Prima di PHP 8, strstr e strpos erano le opzioni tipiche che gli sviluppatori potevano per cercare un needle all’interno di una data stringa. Il problema è che entrambe le funzioni non sono molto intuitive e il loro utilizzo può confondere gli sviluppatori PHP alle prime armi. Si consideri il seguente esempio:

$mystring = 'Managed WordPress Hosting';
$findme = 'WordPress';
$pos = strpos($mystring, $findme);

if ($pos !== false) {
	echo "The string has been found";
} else {
	echo "String not found";
}

In questo esempio abbiamo utilizzato l’operatore di confronto !==, che controlla anche se due valori sono dello stesso tipo. Questo ci evita di ricevere un errore se la posizione del needle è 0:

“Questa funzione può restituire il valore booleano FALSE, ma può anche restituire un valore non booleano che corrisponde a FALSE. […] Si utilizzi l’operatore ==== per testare il valore di ritorno di questa funzione”.

Inoltre, diversi framework forniscono funzioni helper per la ricerca di un valore all’interno di una determinata stringa (si veda come esempio la documentazione degli Helper di Laravel).

Ora, questa RFC propone l’introduzione di una nuova funzione che permette di cercare all’interno di una stringa: str_contains.

str_contains ( string $haystack , string $needle ) : bool

Il suo utilizzo è piuttosto semplice. str_contains verifica se $needle si trova in $haystack e restituisce true o false di conseguenza.

Quindi, grazie a str_contains, possiamo scrivere il seguente codice:

$mystring = 'Managed WordPress Hosting';
$findme   = 'WordPress';

if (str_contains($mystring, $findme)) {
	echo "The string has been found";
} else {
	echo "String not found";
}

Che è più leggibile e meno soggetto ad errori (si può vedere questo codice in azione qui).
Al momento in cui scriviamo, str_contains è case-sensitive, ma questa caratteristica potrebbe cambiare in futuro.

La proposta di str_contains è stata approvata con 43 voti favorevoli e 9 contrari.

str_starts_with() e str_ends_with()

Oltre alla funzione str_contains, due nuove funzioni permettono di cercare un needle (ago) all’interno di una determinata stringa: str_starts_with e str_ends_with.

Queste nuove funzioni verificano se una data stringa inizia o finisce con un’altra stringa:

str_starts_with (string $haystack , string $needle) : bool
str_ends_with (string $haystack , string $needle) : bool

Entrambe le funzioni restituiscono false se $needle è più lungo di $haystack.

Secondo Will Hudgins, l’autore di questa RFC,

“La funzionalità di str_starts_with e str_ends_with è così normalmente necessaria che è supportata da molti dei principali framework PHP, tra cui Symfony, Laravel, Yii, FuelPHP e Phalcon“.

Grazie a queste funzioni, da ora in poi ora potremo evitare di utilizzare funzioni non ottimali e meno intuitive come substr, strpos. Entrambe le funzioni sono case sensitive:

$str = "WordPress";
if (str_starts_with($str, "Word")) echo "Found!";

if (str_starts_with($str, "word")) echo "Not found!";

Potete vedere questo codice in azione qui.

Questa RFC è stata approvata con 51 voti favorevoli e 4 contrari.

get_debug_type

get_debug_type è una nuova funzione PHP che restituisce il tipo di una variabile. La nuova funzione lavora in modo molto simile alla funzione gettype, ma get_debug_type restituisce i nomi nativi dei tipi e risolve i nomi delle classi.

Si tratta di un buon miglioramento per il linguaggio, perché gettype() non è utile per la verifica dei tipi.

La RFC fornisce due utili esempi per comprendere meglio la differenza tra la nuova funzione get_debug_type() e gettype(). Il primo esempio mostra gettype al lavoro:

$bar = [1,2,3];

if (!($bar instanceof Foo)) { 
	throw new TypeError('Expected ' . Foo::class . ', got ' . (is_object($bar) ? get_class($bar) : gettype($bar)));
}

Da PHP 8 in poi, potremo invece utilizzare get_debug_type:

if (!($bar instanceof Foo)) { 
	throw new TypeError('Expected ' . Foo::class . ' got ' . get_debug_type($bar));
}

La seguente tabella mostra i valori restituiti da get_debug_type e gettype:

Valore gettype() get_debug_type()
1 integer int
0.1 double float
true boolean bool
false boolean bool
null NULL null
“WordPress” string string
[1,2,3] array array
Una classe con nome “Foo\Bar” object Foo\Bar
Una classe anonima object class@anonymous

Altre RFC

Al momento in cui si scrive, diverse RFC proposte per PHP 8 sono ancora in fase di bozza e/o in attesa di implementazione. Le aggiungeremo non appena il loro stato cambierà in “Implemented”.

Ecco un breve elenco di altre novità approvate che faranno parte di PHP 8:

  1. Interfaccia Stringable: questa RFC introduce un’interfaccia stringable che viene aggiunta automaticamente alle classi che implementano il metodo __to String(). Lo scopo principale è quello di utilizzare lo union type string|Stringable.
  2. Nuove API DOM Living Standard in ext/dom: questa RFC si propone di implementare l’attuale DOM Living Standard nell’estensione PHP DOM, introducendo nuove interfacce e proprietà pubbliche.
  3. Tipo di ritorno static: PHP 8 introduce il tipo static come tipo di ritorno accanto ai tipi self e parent.
  4. Variable Syntax Tweaks: questa RFC risolve alcune incongruenze residue nella sintassi delle variabili di PHP.
PHP 8 arriverà alla fine dell'anno e porterà molti cambiamenti e miglioramenti. 🚀 Ecco la nostra analisi approfondita delle nuove funzionalità!Click to Tweet

Riepilogo

Che viaggio! In questo post abbiamo trattato tutte le principali modifiche e i miglioramenti previsti con il rilascio di PHP 8. Il più atteso è sicuramente il compilatore Just in Time, ma c’è molto di più in arrivo con PHP 8.

Assicuratevi di aggiungere questo post tra i vostri bookmerk, perché aggiungeremo le nostre novità preferite alla lista non appena saranno approvate. 🤓

Ora tocca a voi: siete pronti a testare le prossime funzionalità di PHP? Qual è la vostra novità preferita? Lasciateci una riga nella sezione dei commenti qui sotto.


Se ti è piaciuto questo articolo, allora apprezzerai la piattaforma di hosting WordPress di Kinsta. Metti il turbo al tuo sito web e ricevi supporto 24×7 dal nostro team di veterani di WordPress. La nostra infrastruttura potenziata da Google Cloud è centrata su scaling automatico, performance e sicurezza. Permettici di mostrarti la differenza di Kinsta! Scopri i nostri piani