Perché PHP 7?
PHP 6 non vedrà mai la luce, a confermarlo vi è il recente rilascio della versione Alpha di PHP 7; l'ultimo aggiornamento stabile in ordine di tempo del noto linguaggio Open Source per lo scripting server side è stato PHP 5.6 seguito da alcuni upgrade minori, quindi quale è stato il motivo di questo passaggio diretto alla release numero 7? Le ragioni alla base di tale scelta andrebbero ricercate proprio nelle features che gli sviluppatori del progetto avevano intenzione di introdurre in PHP 6.
Tra di esse la più rilevante era sicuramente l'implementazione di un supporto nativo adeguato per la codifica standard Unicode, quest'ultima però si sarebbe rivelata da subito eccessivamente dispendiosa in termini di risorse, senza tener conto dell'effetto negativo a carico delle prestazioni registrato nel corso dei test; il progetto è stato quindi abortito e, chiamati a decidere riguardo al nome della nuova versione del linguaggio, i responsabili del progetto avrebbero optato in larga maggioranza per PHP 7, sottolineando le differenze tra il ramo mai nato e quello successivo.
Stesso discorso per quanto riguarda PHP 5.7; PHPNG (PHP New Generation), che è alla base di PHP 7, era stato interpretato inizialmente come un'alpha di quest'ultima, ma il lavoro svolto sia in termini di integrazione di features addizionali che per quanto riguarda le performances, avrebbe convinto gli sviluppatori a lanciare una nuova major release.
Lo Spaceship Operator
Lo Spaceship Operator, o operatore di confronto combinato, viene rappresentato da una sequenza composta da tre simboli, "<=>", e funziona in modo relativamente simile alla funzione nativa strcmp()
per il confronto case sensitive delle stringhe, ha però la particolarità di poter essere impiegato con qualsiasi tipo di dato supportato da PHP; a livello di costrutto esso richiede una sintassi identica a quella di un qualsiasi altro operatore di confronto e restituisce "0" nel caso in cui gli operandi siano uguali tra loro, "1" se l'operando di sinistra è il più grande e "-1" se ad essere maggiore è invece l'operando di destra.
Volendo fare un semplice esempio basato sui valori numerici decimali (float
), potremmo eseguire una verifica sul funzionamento di tale operatore basata su delle istruzioni per la stampa a video:
// restituisce 0
echo 7.3 <=> 7.3;
// restituisce 1
echo 7.3 <=> 3.7;
// restituisce -1
echo 3.7 <=> 7.3;
Quanto già detto può essere ripetuto seguendo la medesima logica anche per un tipo di dato differente da quello analizzato in precedenza, le stringhe, che rispettano una gerarchia non basata sull'entità di valori, ma su un ordine di priorità:
// restituisce 0
echo "x" <=> "x";
// restituisce 1
echo y <=> x;
// restituisce -1
echo x <=> y;
Dato che x
è uguale a sé stessa, la prima istruzione restituirà 0, restituirà invece 1 nel secondo caso in quanto y
è successiva rispetto a (e quindi più grande di) x
e -1 nel terzo caso perché x
è inferiore a y
; array e oggetti verranno gestiti nello stesso modo mostrato negli esempi.
Lo Spaceship Operator potrebbe risultare particolarmente utile nel caso in cui si debbano utilizzare i confronti all'interno di espressioni complesse basate su condizioni; a tal proposito si faccia riferimento al seguente esempio
function space_ship($x, $y) {
return ($x < $y) ? -1 : (($x > $y) ? 1 : 0);
}
Traducendo l'espressione che rappresenta il valore di ritorno della funzione, si avrebbe che nel caso in cui $x
(operando di sinistra) fosse inferiore a $y
(operando di destra) allora -1 sarebbe TRUE, nel caso in cui, al contrario, $x
fosse maggiore di $y
ad essere TRUE sarebbe 1; in alternativa, e quindi nel solo caso di un'identità di valori, verrebbe restituito 0. Il codice proposto in precedenza e il ragionamento appena esposto potranno quindi essere abbreviati in questo modo:
function space_ship($x, $y) {
return $x <=> $y;
}
Di fatto l'introduzione dell'operatore di confronto combinato rappresenta anche un'opportunità per rendere i sorgenti più brevi e maggiormente leggibili.
Da Elvis al null coalescing operator
PHP 7 presenta un nuovo operatore logico, il null coalescing operator, utilizzabile ora anche in questo come in altri linguaggi all'interno delle espressioni condizionali. Per comprendere la sua utilità e necessario ricordare che, a partire dalla release 5.3, è stata introdotta la possibilità di abbreviare in alcuni casi la sintassi dell'operatore ternario rendendo concomitanti i simboli ?
(TRUE) e :
(FALSE) e rimuovendo l'argomento che li divideva; motivo per il quale ad un'espressione come la seguente:
$x = $y ? $y : $z;
E' stato possibile sostituire una condizione digitata in questo modo:
$x = $y ?: $z;
In pratica, entrambi i codici proposto corrispondono a:
if ($x) {
$y = $x;
} else {
$y = $z;
}
?:
prende il nome di Elvis operator ed è un operatore condizionale utilizzabile anche per il self-checking delle variabili impiegando una sintassi simile a quella delle istruzioni proposte in precedenza; a tal proposito si analizzi il codice seguente dove $z
verrà assegnata a $x
se quest'ultima è NULL o FALSE, altrimenti $x
rimarrà invariata.
$x = $x ?: $z;
Il null coalescing operator di PHP 7, ??
, funziona in modo molto simile all'Elvis operator, in un'espressione esso restituirà l'operando di sinistra se questo non è NULL, se tale condizione non dovesse verificarsi verrà restituito l'operando di destra. Si tenga presente che l'operatore non produrrà una segnalazione di eccezione nel caso in cui l'operando di sinistra sia inesistente, ragion per cui un confronto ancora più corretto potrebbe riguardare i suoi punti in comune con la funzione isset()
, che determina se una variabile è stata definita e non è NULL; in pratica ??
permette di effettuare controlli limitati a variabili (o altri costrutti valorizzabili come gli array) NULL o inesistenti. Un esempio riguardante l'utilizzo di tale operatore potrebbe essere il seguente:
$w = $x ?? $y ?? $z;
La condizione espressa è sostanzialmente traducibile in questo modo: se $x
esiste e non è NULL allora $w
è uguale a $x
, altrimenti, se $y
esiste e non è NULL allora $w
è uguale a $y
, diversamente $w
sarà uguale a $z
. Per riportare quanto detto tramite il classico operatore ternario sarebbe servita un'istruzione come la seguente:
$w = (isset($x) ? $x : (isset($y) ? $y : $z));
Il vantaggio derivante dall'impiego del null coalescing operator appare quindi evidente anche in termini di risparmio di tempo nella digitazione del listato e di leggerezza dei sorgenti.
Le classi anonime
Tra le features più richieste dagli sviluppatori che lamentavano dei limiti nel supporto al paradigma orientato agli oggetti di PHP vi erano le classi anonime (Anonymous Classes), ora presenti nella versione 7 del linguaggio; fino a quest'ultima release l'unica possibilità per sfruttare questo tipo di costrutti era quella di simularli tramite l'impiego delle closures, tale metodologia però non faceva altro che sottolineare le lacune di PHP rispetto ad altre soluzioni come Java.
In sostanza le classi anonime non sono altro che delle classi caratterizzate non soltanto dal fatto di essere prive di un nome definito in sede di programmazione, ma anche dalle fasi di dichiarazione e istanza che, in questo particolare caso, avvengono simultaneamente; esse potrebbero rivelarsi utili nel momento in cui si ha la necessità di utilizzare una classe una sola volta in corso di esecuzione, nello stesso modo le Anonymous Classes rappresentano un'alternativa praticabile quando non si ha la necessità di documentare delle classi.
La sintassi delle classi anonime si basa sull'utilizzo della keyword new
, l'esempio seguente riguarda la generazione di un oggetto di una classe anonima che nello specifico non opera alcune estensione da un'altra classe né implementa delle interfacce.
$obj = new class("Java") {
public function __construct($linguaggio) {
$this->linguaggio = $linguaggio;
}
};
Chiaramente in questa nuova tipologia di classi non manca il supporto per l'ereditarietà; così come accade con le classi dotate di un nome, anche in questo caso verrà utilizzata la keyword extends
, ma sfruttando una sintassi sul modello di quella proposta dall'esempio seguente:
$obj = new class("Java")
extends Programmazione {
public function __construct($linguaggio) {
$this->linguaggio = $linguaggio;
}
};
Nello stesso modo, vi sarà la possibilità di implementare interfacce attraverso le Anonymous Classes utilizzando la già nota keyword implements
:
interface Informatica {
public function method();
}
$obj = new class("Java")
extends Programmazione implements Informatica {
public function __construct($linguaggio) {
$this->linguaggio = $linguaggio;
}
public function method() {}
};
Gli sviluppatori di PHP avrebbero deciso di non inserire nel linguaggio delle nuove keywords per l'impiego delle classi anonime, questo per evitare il verificarsi di conflitti garantendo la retrocompatibilità.
Dichiarazione dei tipi di dati scalari
Un'altra novità di PHP 7 riguarda l'introduzione di elementi per la dichiarazione dei tipi di dati scalari; in questo linguaggio vengono definite variabili scalari quelle che contengono valori associati ai tipi integer
, float
, string
o bool
, mentre non sono scalari i tipi array
, object
e resource
. Date le numerose critiche provenienti dalla community riguardo all'implementazione di tale feature, gli sviluppatori avrebbero deciso di concepire un sistema per la dichiarazione dei tipi identico a quello già in uso.
Per spiegare nel dettaglio tale novità è possibile fare riferimento al sistema per il type-hint (o "suggerimento del tipo") che in modalità predefinita per PHP è non-strict, questo significa che le valutazioni sui tipi vengono delegate alle espressioni (lazy evaluation); per fare un esempio, il passaggio di int(2)
in un'espressione che richiede un valore decimale determinerà un'interpretazione uguale a float(2.0)
, mentre float(7.2)
passato ad una funzione che richiede un intero porterà ad interpretare il dato come int(7)
.
La dichiarazione dei tipi scalari non introduce nuovi termini riservati in PHP, ragion per cui si potranno continuare ad utilizzare integer
, float
, string
o bool
, per l'abilitazione della modalità strict è però disponibile la seguente direttiva per il type-checking:
declare(strict_types=1);
Quest'ultima dovrà essere la prima istruzione digitata in un file, se posizionata in altro modo darà luogo ad una notifica di errore; un semplice esempio riguardante il suo utilizzo potrebbe essere il seguente:
declare(strict_types=1);
homer();
function simpson() {
homer();
}
class springfield {
function simpson() {
homer();
}
}
Nel caso specifico la direttiva per la modalità strict influenzerà tutte le chiamate alla funzione homer()
, comprese quelle effettuate dall'interno di un'ulteriore funzione o di un metodo.
Dichiarazione del tipo di dato di ritorno
La dichiarazione del tipo di dato di ritorno è una nuova feature strettamente correlata alla dichiarazione dei tipi di dati scalari e funziona sulla base del medesimo meccanismo, inoltre, anche in questo caso la valorizzazione ad 1 della direttiva strict_types
consentirà di effettuare operazioni di type-checking in modalità strict; a tal proposito si analizzi la sintassi dello snippet proposto di seguito.
declare(strict_types=1);
function intero(): int {
return 2.0;
}
class cifre {
function intero(): int {
return 2.0;
}
}
Nell'esempio precedente a quello appena proposto è possibile notare come, nella dichiarazioni per i parametri, strict_types
agisca dove si verifica una chiamata alla funzione; nella dichiarazione del tipo di dato di ritorno la direttiva verrà invece applicata nel file in cui la funzione è stata definita, quindi essa non dipenderà da un eventuale passaggio di parametri alla funzione. Per tale ragione la seguente funzione restituirà un valore di ritorno che è stato associato ad un tipo predefinito prima della sua chiamata:
function returnType(int $type): bool {
return isset($this->types[$type]);
}
In sostanza nella funzione viene eseguita una semplice operazione basata sul Return Type Hint (o "suggerimento del tipo di ritorno") per la quale la funzione returnType()
restituirà un booleano come indicato tramite : bool
.
Engine Exceptions per i fatal errors
Come è noto, PHP gestisce gli error types sulla base di 4 gruppi qui proposti per ordine di gravità crescente: notices, warnings, recoverable fatal e fatal; fino a PHP 7 questi ultimi erano di difficile gestione per via del fatto che essi non sono stati concepiti al fine di invocare il gestore nativo delle eccezioni (error handler), motivo per il quale il loro verificarsi portava comunque ad un'interruzione inattesa, e soprattutto immediata, dello script in fase di esecuzione.
Con l'ultima major release del linguaggio si è tentato di colmare questa lacuna introducendo delle apposite Engine Exceptions basate sugli oggetti EngineException
. L'esempio seguente mostrerà un caso tipico di mancata gestione di un errore fatal, cioè quello del passaggio di un paramento NULL ad un metodo:
function mostra_errore($oggetto) {
$oggetto->method();
}
mostra_errore(null);
Il risultato previsto in fase di esecuzione sarà quindi la notifica:
Fatal error: Call to a member function method() on a non-object in /../nomefile.php on line 4
Grazie alle Engine Exceptions di PHP 7 si potrà ricorrere invece ad una sintassi come la seguente:
try {
mostra_errore(null);
} catch (EngineException $e) {
echo "Attenzione: {$e->getMessage()}n";
}
Sia chiaro che le Engine Exceptions non operano in modo automatico al presentarsi un errore fatal, motivo per il quale sarà necessario adottarle all'interno di un blocco try/catch
, altrimenti l'esito dell'esecuzione sarà la mancata gestione dell'errore.
Il metodo Closure->call
Con la versione 5.4 è stato introdotto in PHP il metodo Closure::bindTo
, così come la sua variante statica Closure::bind
, che in pratica ha il compito di duplicare una closure tramite una nuova associazione all'oggetto e alla visibilità di classe; in sostanza si ha la possibilità di restituire una nuova funzione anonima caratterizzata dal medesimo corpo e dalle stesse variabili dell'originale, cambiano invece l'oggetto associato ad essa, con cui si definisce il valore della pseudovariabile $this
, e la visibilità della classe, cioè il riferimento al livello di accesso della funzione ai membri private
e protected
.
In PHP 7 si ha ora a disposizione il nuovo metodo Closure->call()
che semplifica le procedure di binding per le closures all'atto della chiamata; un semplice esempio riguardante l'utilizzo di tale metodo potrebbe essere scritto in questo modo:
class NomeCognome {
private $nome = "Homer";
}
$closure = function($cognome) {
echo $this->nome . ' ' . $cognome;
}
$obj = new NomeCognome();
$closure->call($obj, 'Simpson');
Sostanzialmente Closure->call()
esegue una chiamata alla closure sulla base dei parametri dati e restituisce il risultato atteso, che nel caso dell'esempio precedente è in pratica la concatenazione tra i valori di due variabili, mantenendo il legame tra la pseudovariabile $this
e l'oggetto associato.
Prestazioni e supporto
Come è possibile osservare, le novità presenti in PHP 7 sono numerose, ma secondo gran parte degli esponenti della community che ruota intorno al linguaggio la feature più importante di questo aggiornamento non andrebbe ricercata nelle funzionalità addizionali, ma nel livello di prestazioni raggiunto: PHP 7 dovrebbe infatti garantire una velocità di esecuzione doppia rispetto a PHP 5.6; tra le altre implementazioni presenti è possibile citare gli interventi operati per il supporto alle architetture a 64 bit, la rimozione delle mysql functions ormai deprecate da tempo e l'incompatibilità con l'uso dei delimitatori "ASP style" ("<% .. %>
") e "JavaScript style" ("<script language=php>..</script>
") precedentemente permessi.