PHP 8.0: panoramica completa delle novità (1/4)
La versione 8.0 di PHP viene rilasciata proprio in questi giorni. È piena di novità come nessun'altra versione prima. La sua introduzione ha meritato quattro articoli separati. Nel primo daremo un'occhiata alle novità a livello di linguaggio.
Prima di addentrarci nel PHP, ricordiamo che la versione attuale di Nette è completamente preparata per l'ottava versione. Inoltre, come regalo, è stato rilasciato un Nette 2.4 completamente compatibile, quindi dal punto di vista del framework non c'è nulla che vi impedisca di usarlo.
Argomenti denominati
Cominciamo subito con una bomba, che potrebbe essere definita audacemente come una svolta. Gli argomenti possono essere passati a funzioni e metodi non solo in posizione, ma anche in base al loro nome. Il che è assolutamente interessante nel caso in cui un metodo abbia davvero troppi 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
) {
...
}
}
I primi due argomenti sono passati in posizione, gli altri con il loro nome: (il nome deve seguire i parametri posizionali)
$response->setCookie('lang', $lang, sameSite: 'None');
// instead of the horrible
$response->setCookie('lang', $lang, null, null, null, null, null, 'None');
Prima dell'introduzione di questa funzionalità, si pensava di creare una
nuova API per l'invio dei cookie in Nette, perché il numero di argomenti per
setCookie()
cresceva molto e la notazione posizionale creava
confusione. Ora non è più necessario, perché gli argomenti con nome sono in
questo caso l'API più conveniente. L'IDE li segnala e c'è la sicurezza
del tipo.
Sono ideali anche per spiegare gli argomenti logici, dove il loro uso non è
necessario, ma un semplice true
o false
non è
sufficiente:
// before
$db = $container->getService(Database::class, true);
// now
$db = $container->getService(Database::class, need: true);
I nomi degli argomenti sono ora parte dell'API pubblica. Non è più possibile cambiarli a piacimento. Per questo motivo, anche Nette sta effettuando una verifica per stabilire se tutti gli argomenti hanno un nome adeguato.
Gli argomenti con nome possono essere usati anche in combinazione con i variadici:
function variadics($a, ...$args) {
dump($args);
}
variadics(a: 1, b: 2, c: 3);
// $args will contain ['b' => 2, 'c' => 3]
L'array $args
può ora contenere anche chiavi non numeriche, il
che rappresenta una sorta di rottura del BC. Lo stesso vale per il comportamento
di call_user_func_array($func, $args)
, dove le chiavi dell'array
$args
giocano ora un ruolo molto più significativo. Al contrario,
le funzioni della famiglia func_*()
sono protette dagli argomenti
con nome.
Gli argomenti denominati sono strettamente correlati al fatto che l'operatore
splat ...
può ora espandere array associativi:
variadics(...['b' => 2, 'c' => 3]);
Sorprendentemente, al momento non funziona all'interno degli array:
$arr = [ ...['a' => 1, 'b' => 2] ];
// Fatal error: Cannot unpack array with string keys
La combinazione di argomenti denominati e variadici offre la possibilità di
avere finalmente una sintassi fissa, ad esempio per il metodo
link()
, al quale si possono passare argomenti denominati e
posizionali:
// before
$presenter->link('Product:detail', $id, 1, 2);
$presenter->link('Product:detail', [$id, 'page' => 1]); // had to be an array
// now
$presenter->link('Product:detail', $id, page: 1);
La sintassi per gli argomenti denominati è molto più sexy della scrittura
di array, per cui Latte
l'ha subito adottata, dove può essere utilizzata, ad esempio, nei tag
{include}
e {link}
:
{include 'file.latte' arg1: 1, arg2: 2}
{link default page: 1}
Torneremo a parlare di argomenti con nome nella terza parte della serie, in relazione agli attributi.
Un'espressione può lanciare un'eccezione
Lanciare un'eccezione è ora un'espressione. Si può avvolgere tra parentesi
e aggiungere a una condizione if
. Non sembra molto pratico.
Tuttavia, questo è molto più interessante:
// before
if (!isset($arr['value'])) {
throw new \InvalidArgumentException('value not set');
}
$value = $arr['value'];
// now, when throw is an expression
$value = $arr['value'] ?? throw new \InvalidArgumentException('value not set');
Poiché finora le funzioni freccia possono contenere solo un'espressione, ora possono lanciare eccezioni grazie a questa caratteristica:
// only single expression
$fn = fn() => throw new \Exception('oops');
Espressione di corrispondenza
L'istruzione switch-case
ha due difetti principali:
- utilizza un confronto non rigoroso
==
al posto di===
- bisogna fare attenzione a non dimenticare per
errore
break
Per questo motivo, PHP offre un'alternativa sotto forma di una nuova
espressione match
, che utilizza un confronto rigoroso e, al
contrario, non utilizza break
.
switch
esempio di codice:
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 un 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',
};
Si noti che match
non è una struttura di controllo come
switch
, ma un'espressione. Nell'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
.
In caso di mancata corrispondenza (e non esiste una clausola di default),
viene lanciata l'eccezione UnhandledMatchError
.
A proposito, in Latte esistono anche i tag {switch}
,
{case}
e {default}
. La loro funzione corrisponde
esattamente al nuovo match
. Utilizzano un confronto rigoroso, non
richiedono break
ed è possibile specificare più valori separati
da virgole in case
.
Operatore Nullsafe
Il concatenamento opzionale consente di scrivere un'espressione la cui
valutazione si interrompe se incontra null. Questo grazie al nuovo operatore
?->
. Sostituisce molto codice che altrimenti dovrebbe verificare
ripetutamente la presenza di null:
$user?->getAddress()?->street
// approximately translates to
$user !== null && $user->getAddress() !== null
? $user->getAddress()->street
: null
Perché “approssimativamente”? Perché in realtà l'espressione viene
valutata in modo più ingegnoso, in modo che nessun passaggio venga ripetuto. Ad
esempio, $user->getAddress()
viene richiamato una sola volta,
quindi il problema causato dal metodo che restituisce qualcosa di diverso la
prima e la seconda volta non si presenta.
Questa caratteristica è stata introdotta da Latte un anno fa. Ora è lo stesso PHP ad adottarla. Ottimo.
Promozione della proprietà del costruttore
Uno zucchero sintattico che risparmia di scrivere due volte il tipo e quattro volte la variabile. È un peccato che non sia arrivato in un momento in cui 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, potete iniziare a usarlo.
Controlli di tipo più severi per gli operatori aritmetici e bitwise
Ciò che una volta aiutava i linguaggi di scripting dinamici a salire alla ribalta è diventato il loro punto debole. Una volta, PHP si sbarazzava delle “virgolette magiche”, della registrazione delle variabili globali e ora il comportamento rilassato viene sostituito dalla rigidità. Il tempo in cui in PHP si poteva aggiungere, moltiplicare ecc. quasi tutti i tipi di dati per i quali non aveva senso, è passato da tempo. A partire dalla versione 7.0, PHP sta diventando sempre più rigoroso e dalla versione 8.0, il tentativo di utilizzare qualsiasi operatore aritmetico/bitwise su array, oggetti o risorse termina con un TypeError. L'eccezione è rappresentata dalle aggiunte di array.
// arithmetic and bitwise operators
+, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, --:
Confronti più sani tra stringhe e numeri
Oppure rendere di nuovo grande l'operatore loose.
Sembrerebbe che non ci sia più spazio per l'operatore loose ==
,
che sia solo un errore di battitura quando si scrive ===
, ma questa
modifica lo riporta di nuovo nella mappa. Se lo abbiamo già, lasciamo che si
comporti in modo ragionevole. Come risultato del precedente paragone
“irragionevole”, per esempio, in_array()
potrebbe trollare in
modo spiacevole:
$validValues = ['foo', 'bar', 'baz'];
$value = 0;
dump(in_array($value, $validValues));
// surprisingly returned true
// since PHP 8.0 returns false
Il cambiamento di comportamento di ==
riguarda il confronto tra
numeri e stringhe “numeriche” ed è mostrato nella tabella seguente:
Confronto | Prima | PHP 8.0 |
---|---|---|
0 == "0" |
vero | vero |
0 == "0.0" |
vero | vero |
0 == "foo" |
vero | falso |
0 == "" |
vero | falso |
42 == " 42" |
vero | vero |
42 == "42 " |
vero | vero |
42 == "42foo" |
vero | falso |
42 == "abc42" |
falso | falso |
"42" == " 42" |
vero | vero |
"42" == "42 " |
falso | vero |
Sorprendentemente, si tratta di un'interruzione del BC nel cuore della lingua, che è stata approvata senza alcuna resistenza. E questo è un bene. JavaScript potrebbe essere molto geloso di questo aspetto.
Segnalazione degli errori
Molte funzioni interne ora attivano TypeError e ValueError invece di
avvertimenti che erano facili da ignorare. Alcuni avvisi del kernel sono stati
riclassificati. L'operatore di chiusura @
ora non silenzia gli
errori fatali. E PDO lancia eccezioni per impostazione predefinita.
Nette ha sempre cercato di risolvere questi problemi in qualche modo. Tracy ha modificato il comportamento dell'operatore shutup, Database ha cambiato il comportamento di PDO, Utils contiene funzioni sostitutive di quelle standard, che lanciano eccezioni invece di avvisi facili da perdere, ecc. È bello vedere che la direzione rigorosa che Nette ha nel suo DNA diventa la direzione nativa del linguaggio.
Incrementi negativi delle chiavi degli array
$arr[-5] = 'first';
$arr[] = 'second';
Quale sarà la chiave del secondo elemento? Una volta era 0
,
since PHP 8 it’s -4
.
Virgola finale
L'ultimo punto in cui la virgola non poteva essere presente era la definizione degli argomenti delle funzioni. Questo appartiene al passato:
public function __construct(
Nette\Database\Connection $db,
Nette\Mail\Mailer $mailer, // trailing comma
) {
....
}
$object::class
La costante magica ::class
funziona anche con gli oggetti
$object::class
, sostituendo completamente la funzione
get_class()
.
Cattura le eccezioni solo per tipo
Infine, non è necessario specificare una variabile per l'eccezione nella clausola catch:
try {
$container->getService(Database::class);
} catch (MissingServiceException) { // no $e
$logger->log('....');
}
Nelle prossime parti, vedremo le principali innovazioni riguardanti i tipi di dati, mostreremo cosa sono gli attributi, quali nuove funzioni e classi sono apparse in PHP e introdurremo il compilatore Just in Time.
Per inviare un commento, effettuare il login