PHP 8.0: Panoramica completa delle novità (1/4)
È stata rilasciata la versione 8.0 di PHP. È così ricca di novità come nessuna versione precedente. La loro presentazione ha richiesto ben quattro articoli separati. In questo primo, vedremo cosa c'è di nuovo dal punto di vista del linguaggio.

Prima di immergerci in PHP, sappi che l'attuale versione di Nette è completamente pronta per l'otto. Inoltre, come regalo, è stata rilasciata anche Nette 2.4, che è pienamente compatibile con essa, quindi dal punto di vista del framework, nulla ti impedisce di iniziare a utilizzare la nuova versione.
Argomenti nominati
E iniziamo subito con una bomba, che può essere audacemente definita un game changer. Ora è possibile passare argomenti a funzioni e metodi non solo posizionalmente, ma anche per nome. Il che è assolutamente fantastico nel caso in cui un metodo abbia davvero molti parametri:
class Response implements IResponse
{
public function setCookie(
string $name,
string $value,
string|DateInterface|null $time,
string $path = null,
string $domain = null,
bool $secure = null,
bool $httpOnly = null,
string $sameSite = null
) {
...
}
}
Passiamo i primi due argomenti posizionalmente, gli altri per nome: (quelli nominati devono sempre seguire quelli posizionali)
$response->setCookie('lang', $lang, sameSite: 'None');
// invece del folle
$response->setCookie('lang', $lang, null, null, null, null, null, 'None');
Prima dell'arrivo di questa feature, era in programma creare una nuova API in
Nette per l'invio di cookie, poiché il numero di parametri di
setCookie()
era davvero cresciuto e la notazione posizionale era
poco chiara. Ora non è più necessario, perché gli argomenti nominati sono in
questo caso l'API più pratica. L'IDE li suggerirà e hanno il controllo
dei tipi.
Sono ottimi anche per chiarire i parametri logici, dove il loro uso non è
necessario, ma true
o false
da soli dicono poco:
// prima
$db = $container->getService(Database::class, true);
// ora
$db = $container->getService(Database::class, need: true);
I nomi dei parametri diventano ora parte dell'API pubblica. Non è possibile cambiarli arbitrariamente come prima. Per questo motivo, anche Nette sta subendo un audit per verificare che tutti i parametri abbiano una denominazione appropriata.
Gli argomenti nominati possono essere utilizzati anche in combinazione con i variadics:
function variadics($a, ...$args) {
dump($args);
}
variadics(a: 1, b: 2, c: 3);
// in $args ci sarà ['b' => 2, 'c' => 3]
Ora l'array $args
può contenere anche chiavi non numeriche, il
che costituisce un certo BC break. Lo stesso vale per il comportamento della
funzione call_user_func_array($func, $args)
, dove ora le chiavi
nell'array $args
giocano un ruolo significativo. Al contrario, le
funzioni della famiglia func_*()
sono schermate dagli argomenti
nominati.
Agli argomenti nominati è strettamente correlato anche il fatto che
l'operatore splat ...
può ora espandere anche array
associativi:
variadics(...['b' => 2, 'c' => 3]);
Stranamente, questo non funziona ancora all'interno degli array:
$arr = [ ...['a' => 1, 'b' => 2] ];
// Fatal error: Cannot unpack array with string keys
La combinazione di argomenti nominati e variadics offre finalmente la
possibilità di avere una sintassi fissa, ad esempio per il metodo del presenter
link()
, al quale ora possiamo passare argomenti nominati così come
posizionali:
// prima
$presenter->link('Product:detail', $id, 1, 2);
$presenter->link('Product:detail', [$id, 'page' => 1]); // doveva essere un array
// ora
$presenter->link('Product:detail', $id, page: 1);
La sintassi per i parametri nominati è molto più sexy della scrittura di
array, motivo per cui è stata immediatamente adottata
da Latte, dove può essere utilizzata ad esempio nei tag
{include}
e {link}
:
{include 'file.latte' arg1: 1, arg2: 2}
{link default page: 1}
Torneremo sui parametri nominati nella terza parte in relazione agli attributi.
Un'espressione può lanciare un'eccezione
Lanciare un'eccezione è ora un'espressione. Puoi, ad esempio, racchiuderla
tra parentesi e aggiungerla a una condizione if
. Hmmm, non sembra
molto pratico. Ma questo è già più interessante:
// prima
if (!isset($arr['value'])) {
throw new \InvalidArgumentException('valore non impostato');
}
$value = $arr['value'];
// ora, quando throw è un'espressione
$value = $arr['value'] ?? throw new \InvalidArgumentException('valore non impostato');
Poiché le arrow function possono finora contenere solo una singola espressione, grazie a questa feature possono lanciare eccezioni:
// solo una singola espressione
$fn = fn() => throw new \Exception('oops');
Match Expressions
Il costrutto switch-case
ha due grandi difetti:
- utilizza un confronto non rigoroso
==
invece di===
- devi fare attenzione a non dimenticare
accidentalmente
break
PHP offre quindi un'alternativa sotto forma del nuovo costrutto
match
, che utilizza un confronto rigoroso e, al contrario, non
utilizza break
.
Esempio di codice switch
:
switch ($statusCode) {
case 200:
case 300:
$message = $this->formatMessage('ok');
break;
case 400:
$message = $this->formatMessage('not found');
break;
case 500:
$message = $this->formatMessage('server error');
break;
default:
$message = 'unknown status code';
break;
}
E lo stesso (solo con confronto rigoroso) scritto usando
match
:
$message = match ($statusCode) {
200, 300 => $this->formatMessage('ok'),
400 => $this->formatMessage('not found'),
500 => $this->formatMessage('server error'),
default => 'unknown status code',
};
Nota che match
non è una struttura di controllo come
switch
, ma un'espressione. Nel nostro esempio, assegniamo il suo
valore risultante a una variabile. Allo stesso tempo, anche le singole
“opzioni” sono espressioni, quindi non è possibile scrivere più passaggi,
come nel caso di switch
.
Se non c'è corrispondenza con nessuna delle opzioni (e non esiste una
clausola default), viene lanciata un'eccezione
UnhandledMatchError
.
A proposito, anche in Latte esistono i tag {switch}
,
{case}
e {default}
. Il loro funzionamento corrisponde
esattamente al nuovo match
. Utilizzano un confronto rigoroso, non
richiedono break
e in case
è possibile specificare
più valori separati da virgole.
Operatore Nullsafe
Il concatenamento opzionale (optional chaining) consente di scrivere
un'espressione la cui valutazione si interrompe se incontra null. E questo
grazie al nuovo operatore ?->
. Sostituirà molto codice che
altrimenti controllerebbe ripetutamente null:
$user?->getAddress()?->street
// significa circa
$user !== null && $user->getAddress() !== null
? $user->getAddress()->street
: null
Perché “significa circa”? Perché in realtà l'espressione viene
valutata in modo più intelligente e nessun passaggio viene ripetuto. Ad
esempio, $user->getAddress()
viene chiamato solo una volta,
quindi non può sorgere il problema causato dal fatto che il metodo
restituirebbe qualcosa di diverso la prima e la seconda volta.
Questa fresca novità è stata introdotta un anno fa da Latte. Ora arriva in PHP stesso. Fantastico.
Promozione delle proprietà del costruttore
Zucchero sintattico che risparmia la doppia scrittura del tipo e la quadrupla scrittura della variabile. Peccato solo che non sia arrivato quando non avevamo IDE così intelligenti, che oggi lo scrivono per noi 🙂
class Facade
{
private Nette\Database\Connection $db;
private Nette\Mail\Mailer $mailer;
public function __construct(Nette\Database\Connection $db, Nette\Mail\Mailer $mailer)
{
$this->db = $db;
$this->mailer = $mailer;
}
}
class Facade
{
public function __construct(
private Nette\Database\Connection $db,
private Nette\Mail\Mailer $mailer,
) {}
}
Funziona con Nette DI, puoi iniziare a usarlo subito.
Comportamento rigoroso degli operatori aritmetici e bitwise
Ciò che un tempo ha portato alla ribalta i linguaggi di scripting dinamici, col tempo è diventato il loro punto più debole. PHP si è sbarazzato delle “magic quotes”, della registrazione delle variabili globali e ora il comportamento permissivo viene sostituito dalla rigorosità. L'epoca in cui in PHP si potevano sommare, moltiplicare, ecc. quasi tutti i tipi di dati per i quali non aveva alcun senso è finita da tempo. A partire dalla versione 7.0, PHP è diventato sempre più rigoroso e dalla versione 8.0, il tentativo di utilizzare qualsiasi operatore aritmetico/bitwise su array, oggetti o resources termina con un TypeError. L'eccezione è la somma di array.
// operatori aritmetici e bitwise
+, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, --:
Confronto più ragionevole tra stringhe e numeri
Ovvero, rendiamo di nuovo grande l'operatore loose.
Sembrerebbe che per l'operatore loose ==
non ci sia più posto,
che sia solo un errore di battitura nello scrivere ===
, ma questa
modifica lo riporta sulla mappa. Visto che ce l'abbiamo, che si comporti in modo
ragionevole. La conseguenza del precedente confronto “irragionevole” era, ad
esempio, il comportamento di in_array()
, che poteva giocarvi un
brutto scherzo:
$validValues = ['foo', 'bar', 'baz'];
$value = 0;
dump(in_array($value, $validValues));
// sorprendentemente restituiva true
// da PHP 8.0 restituisce false
La modifica nel comportamento di ==
riguarda il confronto tra
numeri e stringhe “numeriche” ed è mostrata nella seguente tabella:
Confronto | Prima | PHP 8.0 |
---|---|---|
0 == "0" |
true | true |
0 == "0.0" |
true | true |
0 == "foo" |
true | false |
0 == "" |
true | false |
42 == " 42" |
true | true |
42 == "42 " |
true | true |
42 == "42foo" |
true | false |
42 == "abc42" |
false | false |
"42" == " 42" |
true | true |
"42" == "42 " |
false | true |
Sorprendente è che si tratta di un BC break nel fondamento stesso del linguaggio, che è stato approvato senza alcuna opposizione. E questo è un bene. Qui JavaScript potrebbe invidiare molto.
Segnalazione degli errori
Molte funzioni interne ora sollevano TypeError e ValueError invece di avvisi,
che potevano essere facilmente trascurati. Sono stati riclassificati numerosi
avvisi del core. L'operatore shutup @
ora non silenzia gli errori
fatali. E PDO nella modalità predefinita lancia eccezioni.
Nette ha sempre cercato di risolvere queste cose in qualche modo. Tracy modificava il comportamento dell'operatore shutup, Database cambiava il comportamento di PDO, Utils contiene sostituti delle funzioni standard che lanciano eccezioni invece di avvisi discreti, ecc. È bello vedere che la direzione rigorosa, che Nette ha nel suo DNA, sta diventando la direzione nativa del linguaggio.
Array con indice negativo
$arr[-5] = 'first';
$arr[] = 'second';
Quale sarà la chiave del secondo elemento? Prima era 0
, da PHP
8 è -4
.
Virgola finale
L'ultimo posto dove non poteva esserci una virgola finale era la definizione dei parametri della funzione. Questo è ormai passato:
public function __construct(
Nette\Database\Connection $db,
Nette\Mail\Mailer $mailer, // virgola finale
) {
....
}
$object::class
La costante magica ::class
funziona anche con gli oggetti
$object::class
, sostituendo completamente la funzione
get_class()
.
catch senza variabile
E infine: nella clausola catch non è necessario specificare la variabile per l'eccezione:
try {
$container->getService(Database::class);
} catch (MissingServiceException) { // no $e
$logger->log('....');
}
Nelle prossime parti ci aspettano novità fondamentali nei tipi di dati, mostreremo cosa sono gli attributi, quali nuove funzioni e classi sono apparse in PHP e presenteremo il Just in Time Compiler.
Per inviare un commento, effettuare il login