Differenze e novità tra PHP5 e PHP7, con esempi di programmazione

Con la scusa di un po’ di esercizio vediamo anche quali sono le principali differenze tra (l’ormai vecchio) PHP 5 e il nuovo PHP 7 (che comunque è in circolazione già da un bel po’).

1. Sintassi flessibile per Heredoc e Newdoc
2. Operatore Spaceship
3. Null Coalesce Operator
4. Virgola finale negli argomenti di funzione
5. Migrazione a PCRE2
6. Unicode Codepoint Escape Syntax
7. Chiamata di errore nella codifica JSON
8. Exceptions per i fatal errors
9. Classi anonime
10. Dichiarazione del tipo di dato restituito
11. Metodo is_countable()
12. Primi e ultimi valori e chiavi di un array
13. Il metodo Closure->call
14. Prestazioni migliorate

1. Sintassi flessibile per Heredoc e Newdoc

Nell’ultimo aggiornamento del PHP 7.3 viene introdotta la possibilità di terminare il heredoc con UNA indentazione. Sottolineo UNA perché la cosa è un po’ comica, visto che una doppia indentazione (due tabulazioni) oppure degli spazi producono comunque errore.

In sostanza prima si doveva scrivere così:

Adesso è possibile scrivere così:

Questa singola tabulazione certo non farà la felicità assoluta di nessuno, però permette di aggiungere un po’ di chiarezza al codice (sebbene personalmente continui a trovarla una novità abbastanza inutile, tale qual’è).

2. Operatore Spaceship

A differenza della precedente, questa è una novità effettivamente significativa.

Viene introdotto l’operatore <=>, chiamato spaceship, che fa le veci (quasi ma non del tutto) della funzione strcmp. Questo velocizza la scrittura e ottimizza la sintassi, riducendo così il numero di passaggi a volte necessari.

Avendo il seguente codice:

Avremo il seguente output:

a <=> b : -1
mario <=> maria : 1
marii <=> mario : -1
maria <=> mario : -1
mario <=> luigi : 1
1 <=> 2 : -1
2 <=> 1 : 1
3 <=> 3 : 0

Utilizzando invece strcmp avremmo avuto il seguente codice:

Ed il seguente output:

a <=> b : -1
mario <=> maria : 14
marii <=> mario : -6
maria <=> mario : -14
mario <=> luigi : 1
1 <=> 2 : -1
2 <=> 1 : 1
3 <=> 3 : 0

Come si può notare il comportamento non è identico, se consideriamo i valori numerici e non i segni.

Per riprodurre il comportamento dell’operatore spaceship in PHP 5 potremmo immaginare di scrivere una funzione simile a questa:

Applicandola all’elenco precedente:

Avremo gli stessi risultati di <=> in PHP 7:

a <=> b : 0
mario <=> maria : 1
marii <=> mario : -1
maria <=> mario : -1
mario <=> luigi : 1
1 <=> 2 : -1
2 <=> 1 : 1
3 <=> 3 : 0

Immaginiamo dunque di voler scrivere una funzione di ordinamento per un array. In PHP 5 avremmo potuto scrivere:

Mentre in PHP 7 possiamo sintetizzare con:

Entrambi i casi produrranno un output ordinato come il seguente:

Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 7 [5] => 8 )

Inutile dire che per entrambi gli esempi avremmo potuto incorporare direttamente una funzione anonima dentro ad usort in questo modo:

Infine nel PHP 5 l’uso dell’operatore spaceship produrrebbe un errore di questo tipo:

Parse error: syntax error, unexpected '>' in /index.php on line 16

3. Null Coalesce Operator

In PHP 7 viene introdotto il null coalescing operator ??, proseguendo così le implementazioni sull’if lineare, già introdotte nel PHP 5.3 con l’Elvis operator ?:.

In generale sappiamo che possiamo scrivere un’istruzione con if lineare (o operatore ternario) di questo tipo:

Questa istruzione equivale a scrivere:

In generale questa istruzione può essere utilizzata per riassegnare la medesima variabile in uno dei due casi. Per esempio immaginiamo di voler scrivere:

Con l’Elvis operator possiamo sintetizzare la scrittura nel modo seguente:

Quello che viene assegnato è l’argomento del test. Se l’argomento del test comprende operatori logici viene assegnato il risultato dell’operazione logica.

Per capire meglio immaginiamo di voler trovare il più grande valore tra quelli dati (ci sarebbero altri mille modi, ma per lo scopo dell’esercizio prendiamo questo):

Il risultato di questa operazione darà ovviamente come risultato 8.

Se lo riscrivessimo con l’Elvis operator potremmo avere qualcosa del genere:

Purtroppo però il risultato non sarebbe più 8, bensì 1, e questo indipendentemente dal fatto che 1 esista nell’array. E’ 1 perché 1 è true, il risultato di una delle operazioni di test. (il penultimo massimo è 8, quindi 8 è maggiore di 2? sì, vero, quindi 1!)

Praticamente serve solo quando il risultato che vogliamo valutare è tra true o false, stringa nulla o meno, ecc.

A questo si aggiunge il null coalescing operator che integra il controllo della nullità della variabile, sostituendosi alla funzione isset. Immaginiamo di avere:

Questo può diventare ora:

Il comportamento è identico. Il risultato è che le seguenti due scritture sono fondamentalmente uguali, eccetto per un dettaglio:

La prima non produce una notice, mentre la seconda ci segnala qualcosa del tipo:

Notice: Undefined index: valore in index.php on line 9

Facciamo un altro esempio con entrambe:

La prima scrittura restituisce Mario senza ulteriori errori, l’oggetto non è dichiarato, quanto l’attributo; la seconda produce ancora due notice:

Notice: Undefined variable: studente in index.php on line 15

Notice: Trying to get property 'nome' of non-object in index.php on line 15

A questo punto potremmo anche scrivere:

Per dichiarare l’oggetto laddove inesistente, mentre la seguente scrittura produrrebbe sempre una notice:

A questo punto potremmo scrivere altre due equivalenze, dal punto di vista del risultato:

Ovviamente possiamo sfruttare l’operatore per altre aggregazioni del tipo:

Dove il risultato è 42, ovvero il primo valore non null della sequenza.

4. Virgola finale negli argomenti di funzione

Adesso è possibile dimenticarsi una virgola dopo l’ultimo argomento di una funzione, senza che il PHP si agiti per questo 🙄

Mentre sia nel PHP 5 che nel PHP 7 la seguente dicitura non provocava problemi:

Provare a fare lo stesso con una funzione, che accetti argomenti predefiniti o meno, provoca un errore:

Nel PHP 5 entrambe le funzioni avrebbero prodotto un errore del tipo:

Parse error: syntax error, unexpected ')' in index.php on line 12

Personalmente non la trovo un’innovazione strepitosa, ma va bene, adesso sappiamo che c’è.

5. Migrazione a PCRE2

Il PHP utilizza in generale PCRE per l’interpretazione delle espressioni regolari. Nel PHP 7.3 è stato finalmente introdotto l’utilizzo del PCRE2 che ha aspetti un po’ più stringenti e severi sulla sintassi. Facciamo un esempio, immaginando di avere una stringa dalla quale vogliamo trovare tutte le parole, compreso il simbolo (meno) e il . (punto); nel PHP 5 entrambe le seguenti scritture sarebbero state valide:

Soprassediamo sul fatto che i caratteri speciali verranno saltati e considerati alla stregua di spazi, ma il risultato sarà qualcosa di simile a:

 

Usando la prima espressione regolare all’interno di un programma come il Notepad++ avremmo ottenuto un errore:

Mentre la seconda è valida. Adesso anche nel PHP 7.3 la seconda espressione è l’unica valida, mentre la prima non funziona producendo un errore del tipo:

Warning: preg_match_all(): Compilation failed: invalid range in character class at offset 3 in index.php on line 10

Giusto per curiosità, se si volessero prendere anche i caratteri speciali potremmo utilizzare la seguente espressione regolare:

6. Unicode Codepoint Escape Syntax

Visto che si parla di stringhe facciamo un piccolo appunto a proposito dei riferimenti ai caratteri Unicode. Adesso è possibile specificarli direttamente nelle stringhe di testo con la sintassi \u{XXX}. Maggiori approfondimenti si possono trovare qui sul sito ufficiale. Questo significa che scrivendo:

Nel PHP 5 vedremo la stringa tale e quale:

\u{202E}Testo inverso

Mentre nel PHP 7 vedremo:

‮Testo inverso

Il carattere in questione è il U+202E RIGHT-TO-LEFT OVERRIDE

Si possono usare tutti i caratteri unicode, anche quelli associati agli smile, per cui echo "\u{1F602}"; produrrà 😂

7. Chiamata di errore nella codifica JSON

Adesso è finalmente possibile forzare il JSON a sollevare un errore in caso di stringhe non (de)codificabili. E’ possibile infatti scrivere nel modo seguente:

Senza l’aggiunta di JSON_THROW_ON_ERROR non ci sarebbe nessuna segnalazione di errore. Grazie a tale istruzione otteniamo invece il seguente errore fatale:

Fatal error: Uncaught JsonException: Syntax error in index.php:8 Stack trace: #0 index.php(8): json_decode('stringa JSON no...', NULL, 512, 4194304) #1 {main} thrown in index.php on line 8

8. Exceptions per i fatal errors

A proposito di errori, finalmente è stato migliorato il sistema di cattura delle eccezioni con try… catch. Primo era sostanzialmente, per farla breve, necessario chiamare throw new Exception(); per sollevare un’eccezione. Adesso è possibile intercettare anche le eccezioni di sistema. Nel caso precedente l’eccezione è intercettabile con:

Come altro esempio immaginiamo di chiamare una funzione non esistente:

Nel dobbiamo usare Error per intercettare l’errore, e mentre nel PHP 5 avremmo comunque avuto il seguente output:

Fatal error: Call to undefined function funzione() in index.php on line 9

Mentre nel PHP 7 avremmo il seguente valore, prodotto dal nostro echo:

errore: Call to undefined function funzione()

9. Classi anonime

Tra le varie novità vi è la possibilità di implementare classi anonime. Cominciamo dall’esempio più semplice:

Nel PHP 5 questo produrrebbe un errore del tipo:

Parse error: syntax error, unexpected 'class' (T_CLASS) in index.php on line 7

Alla classe anonima possono essere passati anche degli argomenti per il costruttore, nel modo seguente:

L’output invece che Mario sarà Luigi. Le classi anonime possono anche estendere altre classi, come ad esempio:

Oppure implementarle:

Senza implementare il metodo stampaNome() avremmo il seguente errore:

Fatal error: Class class@anonymous contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Studente::stampaNome) in index.php on line 11

10. Dichiarazione del tipo di dato restituito

Il PHP 7 introduce la possibilità di forzare il tipo di dato restituito da una funzione. Immaginiamo di voler scrivere una funzione somma che restituisca solamente valori interi. Nella maniera classica avremmo scritto:

Passando come argomento 2.5 e 3 il risultato sarebbe stato 5.5, un float. Per forzare l’output di un intero avremmo potuto scrivere:

In PHP 7 è possibile anche scrivere:

Sintassi che nel PHP 5 avrebbe prodotto il seguente errore:

Parse error: syntax error, unexpected ':', expecting '{' in index.php on line 7

11. Metodo is_countable()

In PHP 7 è possibile verificare se un oggetto sia “conteggiabile” ovvero sia del tipo Countable. Immaginiamo la seguente serie di istruzioni:

Essendo Studenti conteggiabile viene stampato il conteggio. Nel PHP 5 avremmo ovviamente avuto un errore del tipo:

Fatal error: Call to undefined function is_countable() in index.php on line 19

Facciamo un altro esempio di funzione implementabile su Countable e dove ci può tornare utile il nuovo metodo:

In questo caso non abbiamo bisogno di chiamare la funzione count() all’interno di public function count(), ma possiamo tenere traccia della quantità di voci aggiunte al vettore $studenti tramite un contatore interno.

12. Primi e ultimi valori e chiavi di un array

Immaginiamo di avere un array di questo tipo:

Alla maniera precedente avremmo potuto scrivere:

In questo modo avremmo ottenuto un output del genere:

primo valore: 1
prima chiave: a
ultimo valore: 100
ultima chiave: c

Questo implica anche che stiamo spostando il cursore nella posizione attuale dell’array. Se provassimo a scrivere:

Il risultato non sarebbe corretto e avremmo:

primo valore: 1
ultimo valore: 100
prima chiave: c
ultima chiave: c

Quindi questa scrittura sarebbe stata impossibile. Ora, con il PHP 7.3 è possibile scrivere nel modo seguente:

Le funzioni proposte sono attualmente: array_key_first(), array_key_last() and array_value_first(), array_value_last() anche se sono state introdotte, nel PHP 7.3 solamente quelle riguardanti le chiavi. Maggiori dettagli sulla questione qui: PHP RFC: array_key_first(), array_key_last() and array_value_first(), array_value_last()

13. Il metodo Closure->call

Nel PHP 7 viene introdotta una semplificazione nell’atto di binding delle closure.

 

Se sembra che tutti si stia dicendo cose a caso vediamo un esempio:

Notiamo che la funzione scritta su $closure va ad integrare la classe, potendo interagire con tutte le sue variabili interne anche se private.

14. Prestazioni migliorate

Infine il PHP 7 introduce notevoli implementazioni in termini di performance. Di seguito riporto i risultati di benchmark ottenuti grazie a benchmark-php su un medesimo server, ma con due diverse versioni di PHP. In particolare la versione PHP 7.3 introduce notevole stabilità nell’esecuzione e nel mantenimento delle performance.

PHP 7.3.5 PHP 5.6.40

 

Vedi articolo