PHP 8 verrà ufficialmente rilasciato il 26 novembre 2020. Dato che si tratta di una major release avremo da un lato nuove funzionalità, dall’altro ci saranno dei cambiamenti che potrebbero rendere incompatibile il nostro vecchio codice. Ci sono, come detto, tante nuove funzionalità e soprattutto dei miglioramenti della velocità, che vedremo in seguito.
Attualmente lo sviluppo è molto vivo e la prima versione Alpha è già uscita il 25 giugno di quest’anno.
Date le novità e i cambiamenti introdotti, è possibile che dovremo modificare il nostro codice in alcuni punti, per essere compatibile appunto con PHP 8. Ma se ci siamo tenuti aggiornati alle ultime release non dovemmo sudare molto, visto che molte delle funzionalità abbandonate (deprecated) erano già tali dalla 7. Vedremo le deprecated in questo articolo.

Oltre tutto, come accennato, PHP 8 introdurrà un nuovo set di feature, incluso il nuovo compilatore JIT, l’unione dei tipi, attributi e altro.

Nuove funzionalità

Partiamo con le novità, tenendo presente che PHP 8 è attualmente in sviluppo e questa lista crescerà nel tempo.

Unioni di tipi (Union types)

Data la natura dinamica della tipizzazione dei dati in PHP, le union types potranno essere sicuramente utili in tanti casi. Sono l’unione di due o più tipi che indicano che almeno uno di essi può essere usato:

Da notare che il tipo void non può essere parte di una union, dato che significa “nessun valore”. Invece, le unioni nullable possono continuare ad esistere, sempre anteponendo il ? al tipo:

JIT compiler

Una delle novità più attese è il nuovo compilatore JIT – just in time – che promette un aumento delle prestazioni significativo, anche se non sempre nel contesto delle richieste web. Gli attuali benchmark su JIT in PHP 8 ci dicono che il guadagno di velocità è altalenante, attendiamo la versione finale per dare un giudizio complessivo.

Per approfondire questo argomento, leggete questo articolo dedicato su PHP 8 e JIT.

Annotations v2

Gli Attributes, chiamati in altri linguaggi annotations, offrono un modo veloce per aggiungere metadati alle classi, senza doversi occupare di fare il parse dei cosiddetti docblocks. Concetti molto simili esistono già in Java, C#, C++ e assomigliano ai Decorators di Python e Javascript.

Questo tipo di notazione non è mai stata ufficiale, e sarà molto familiare a chi utilizza le annotations in Doctrine. Essendo un argomento molto vasto vi rimando direttamente alla rfc

Diamo ora uno sguardo rapido alle nuove annotations:

Espressione Match

Il nuovo costrutto match è considerabile come il fratello maggiore di switch : match infatti può restituire i valori e non richiede l’uso del break. Si possono inoltre combinare le condizioni e usare la tipizzazione nei confronti:

Per approfondire l’utilizzo di match , date uno sguardo all’articolo su match vs switch in PHP 8!

Creazione di proprietà nel costruttore migliorata

Questa nuova RFC aggiungi una sintassi molto interessante: potremo infatti sfruttare un meccanismo per indicare esplicitamente la visibilità di una proprietà creata dinamicamente dal costruttore di una classe. Invece di fare questo:

Potremo fare questo:

Questo è solo uno sguardo generale, per approfondire c’è l’articolo dedicato.

Nuovo tipo static

Anche se era già possibile fare il return di tipo self , static  viene aggiunto con la versione 8 di PHP. Data la tipizzazione debole di PHP, potrebbe essere utile in varie situazioni.

Nuovo tipo mixed

Anche se lo abbiamo letto mille volte nel manuale ufficiale, adesso potremo utilizzarlo nel nostro codice. Sì, potrebbe non essere necessariamente una cosa positiva, ma tant’è. Principalmente potremmo utilizzarlo al posto di null , o più praticamente se ci aspettiamo che un metodo restituisca più tipi di valore (..ma sarebbe meglio di no) o in generale quando non possiamo fare un type-hinting convincente.

Tanto per ricordalo, mixed può essere uno di questi tipi:

  • array
  • bool
  • callable
  • int
  • float
  • null
  • object
  • resource
  • string

Ovviamente potremo utilizzare mixed  anche come tipo di proprietà o parametro. Prestiamo però attenzione, perché mixed  include null  e non è possibile renderlo “nullable”. Quindi questo tipo di sintassi ci porterà ad errore:

Throw è un espressione

throw  non è più classificato come statement, ma come expression, quindi potremo utilizzarlo in più contesti, come ad esempio:

Ereditarietà di metodi private

In precedenza, PHP applicava il controllo di visibilità sui metodi con visibilità public , protected  e private quando li ereditavamo. Questo faceva sì che i metodi private  seguissero le stesse regole dei protected  e public . Non aveva molto senso, in quanto i metodi private  non sono accessibili dalle classi che ereditano.

In PHP questa regola cambia, e i controlli sui metodi ereditati con visibilità private  non vengono eseguiti più. Inoltre, l’uso di final private function  non ha molto senso, e adesso genera un warning:

::class sugli oggetti

Una piccola ma utilissima modifica, permette adesso di utilizzare il ::class direttamente sugli oggetti, senza dover utilizzare get_class(). Funziona allo stesso modo:

Eccezioni senza variabile

Nel fare il catch  di una Exception  prima di PHP 8, dovevamo indicare una variabile nella quale mettere questa Exception , indipendentemente dal fatto che la usassimo o no. Con i non-capturing catch potremo ometterla. Invece di scrivere questo:

Potremo fare questo:

Ovviamente dovremo continuare a specificare un tipo di Exception , non potremo certo lasciare il catch vuoto. Se volessimo fare il catch  di tutte le Exception  e gli errori, dovremmo indicare Throwable  come tipo.

Ultima virgola nella lista parametri

Anche se già implementata nelle funzioni, adesso sarà possibile lasciare l’ultima virgola nella lista parametri di un metodo:

Nuova interface Stringable

Con PHP 8 viene introdotta una nuova interfaccia, Stringable , che può essere utilizzata come tipo su qualsiasi stringa o su una classe che implementi il metodo __toString() . Ancora meglio, qualsiasi classe implementi __toString()  implementerà automaticamente questa interfaccia, anche se non lo dichiariamo implicitamente.

Nuova funzione str_contains()

Ce l’abbiamo fatta. Non dovremo più utilizzare strpos()  per sapere se una stringa ne contiene un’altra. Anziché utilizzare questo:

Potremo semplicemente fare:

Nuove funzioni str_starts_with() e str_ends_with()

Quando pensavamo che le sorprese fossero finite, ecco che PHP 8 introduce queste due nuove funzioni per evitare di pasticciare con substr() e compagnia bella, semplicemente scrivendo:

Nuova funzione fdiv()

La nuova function fdiv()  si comporta in modo simile a fmod()  e intdiv() , che permettono la divisione by zero. Invece dell’errore riceveremo INF , -INF  o NAN  a seconda dei casi.

Nuova funzione get_resource_id()

Le variabili che puntano a una risorsa sono un caso speciale in PHP, perché fanno riferimento appunto a risorse esterne e non sono direttamente interpretabili. L’esempio più facile (e sicuramente diffuso) è quello di una connessione MySQL, o del puntatore di una risorsa file.

Ognuna di queste risorse è riconducibile ad un ID: in precedenza l’unico modo per leggere questo ID era fare il cast al tipo int:

PHP 8 aggiunge la funzione get_resource_id() , rendendo questa operazione type-safe:

 

Metodi astratti nei trait migliorati

I trait in PHP possono dichiarare dei metodi che possono essere implementati dalle classi che li utilizzano, ma c’è una limitazione: prima di PHP 8, infatti, essi subivano una validazione abbastanza permissiva, soprattutto riguardo al return type nell’implementazione da parte della classe del metodo astratto in questione. Questo codice, in PHP 7+ è infatti valido:

PHP 8 controllerà invece il tipo dei metodi implementati. Dovremo scrivere questo:

Breaking change

Come vi dicevo: questa è una major release e pertanto ci saranno dei cambiamenti distruttivi (tradurlo fa sempre un certo effetto). Questo vuole dire che probabilmente dovremo lavorare un po’ sul nostro codice per renderlo compatibile con PHP 8, ma la verità è che molte delle modifiche che andremo a vedere erano già deprecate in PHP 7, quindi se avevamo fatto una revisione del codice ai tempi, dovremmo essere a posto. In ogni caso diamo uno sguardo a quali cambiamenti ci saranno:

I parametri di tipo diverso generano una Exception di tipo TypeError

Passando un parametro del tipo non corretto ad una funzione scritta dall’utente, viene generata una Exception  TypeError . Per le funzioni interne, quelle predefinite di PHP, invece, questo dipende, alcune restituiscono null , altre un warning. A partire da PHP 8, verrà sempre lanciata l’Exception TypeError  (così come ArgumentCountError , che è una sua discendente). Nell’esempio vediamo come, attualmente, ci sia un’inconsistenza in quanto strlen()  restituisce null :

Da PHP 8, lo stesso genererà una TypeError :

Nuova gestione degli errori

Ricollegandoci all’argomento precedente, molte funzioni che generano dei Warning o delle Notice, adesso avranno un comportamento più consistente:

  • Undefined variable: Exception Error  anziché una Notice
  • Undefined array index: Warning anziché Notice
  • Division by zero: invece di un Warning verrà lanciata l’Exception DivisionByZeroError
  • Attempt to increment/decrement property of non-object: Exception  Error  invece di un Warning
  • Attempt to modify property of non-object: Exception  Error  invece di un Warning
  • Attempt to assign property of non-object: Exception  Error  invece di un Warning
  • Creating default object from empty value: Exception  Error  invece di un Warning
  • Trying to get property of non-object: avremo un Warning invece di una Notice
  • Undefined property: avremo un Warning invece di una Notice
  • Cannot add element to the array as the next element is already occupied: Exception  Error  invece di un Warning
  • Cannot unset offset in a non-array variable: Exception  Error  invece di un Warning
  • Cannot use a scalar value as an array: Exception  Error  invece di un Warning
  • Invalid argument supplied for foreach(): Exception  TypeError  invece di un Warning
  • Illegal offset type: Exception  TypeError  invece di un Warning
  • Illegal offset type in isset or empty: Exception  TypeError  invece di un Warning
  • Illegal offset type in unset: Exception  TypeError  invece di un Warning
  • Supplied resource is not a valid stream resource: Exception  TypeError  invece di un Warning
  • Cannot assign an empty string to a string offset: Exception  Error  invece di un Warning

L’operatore @ non silenzia i fatal error

Facciamo attenzione, questo cambiamento potrebbe far venire fuori degli errori che in precedenza avevamo silenziato utilizzando l’operatore @  prima di PHP 8. Accertiamoci che sia impostato display_errors=off  in produzione.

Nuovo livello di default di error reporting

Il nuovo livello di default di Error reporting è E_ALL , quindi, come dicevamo nel paragrafo precedente, assicuriamoci che non saltino fuori errori in produzione.

PDO utilizzerà di default PDOException

PDO di default genera errori silenti. Questo significa che, ad esempio, quando avviene un errore SQL, non viene generato alcun errore o Warning, a meno che non avessimo implementato noi stessi un controllo dell’errore esplicito. Da PHP 8, PDO farà il throw  di una PDOException  quando uno di questi errori accade.

Precedenza nella concatenazione

Anche se già deprecato in PHP 7.4, questo cambia avverrà definitivamente con la release di PHP 8. In precedenza, se avessimo scritto questo:

PHP lo interpreterà così:

Da PHP 8 verrà invece interpretato così: