PHP 8.0: Prezentare completă a noutăților (1/4)
Versiunea 8.0 a PHP este lansată chiar acum. Este plină de noutăți ca nicio altă versiune de până acum. Introducerea lor a meritat patru articole separate. În primul vom arunca o privire asupra a ceea ce aduce la nivel de limbaj.
Înainte de a ne adânci în PHP, să știm că versiunea actuală a Nette este complet pregătită pentru cea de-a opta versiune. Mai mult, ca un cadou, a fost lansat un Nette 2.4 complet compatibil, deci din punct de vedere al framework-ului nu există nimic care să vă împiedice să îl folosiți.
Argumente numite
Să începem imediat cu o bombă, care ar putea fi desemnată cu îndrăzneală ca fiind un schimbător de joc. Argumentele pot fi acum transmise funcțiilor și metodelor nu doar pozițional, ci și în funcție de numele lor. Ceea ce este absolut mișto în cazul în care o metodă are într-adevăr prea mulți 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
) {
...
}
}
Primele două argumente sunt trecute pozițional, iar celelalte prin numele lor: (cele numite trebuie să urmeze după cele poziționale).
$response->setCookie('lang', $lang, sameSite: 'None');
// instead of the horrible
$response->setCookie('lang', $lang, null, null, null, null, null, 'None');
Înainte de introducerea acestei caracteristici, existau planuri de a crea un
nou API pentru trimiterea de cookie-uri în Nette, deoarece numărul de
argumente pentru setCookie()
a crescut foarte mult, iar notația
pozițională era derutantă. Acest lucru nu mai este necesar, deoarece
argumentele numite sunt în acest caz cea mai convenabilă API. IDE le va indica
și există siguranță de tip.
Ele sunt ideale chiar și pentru explicarea argumentelor logice, în cazul
în care utilizarea lor nu este necesară, dar un simplu true
sau
false
nu este suficient:
// before
$db = $container->getService(Database::class, true);
// now
$db = $container->getService(Database::class, need: true);
Numele argumentelor fac acum parte din API-ul public. Nu mai este posibil să le modificăm după bunul plac. Din această cauză, chiar și Nette trece printr-un audit pentru a determina dacă toate argumentele au un nume adecvat.
Argumentele cu nume pot fi utilizate și în combinație cu variadicele:
function variadics($a, ...$args) {
dump($args);
}
variadics(a: 1, b: 2, c: 3);
// $args will contain ['b' => 2, 'c' => 3]
Matricea $args
poate conține acum chiar și chei nenumerice,
ceea ce reprezintă un fel de pauză BC. Același lucru este valabil și pentru
comportamentul din call_user_func_array($func, $args)
, unde cheile
din matricea $args
joacă acum un rol mult mai important.
Dimpotrivă, funcțiile din familia func_*()
sunt ferite de
argumente numite.
Argumentele numite sunt strâns legate de faptul că operatorul splat
...
poate acum să extindă array-uri asociative:
variadics(...['b' => 2, 'c' => 3]);
În mod surprinzător, acesta nu funcționează deocamdată în interiorul tablourilor:
$arr = [ ...['a' => 1, 'b' => 2] ];
// Fatal error: Cannot unpack array with string keys
Combinația dintre argumentele numite și variadice oferă opțiunea de a
avea în sfârșit o sintaxă fixă, de exemplu, pentru metoda
link()
, căreia îi putem transmite atât argumente numite, cât
și poziționale:
// 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);
Sintaxa pentru argumente numite este mult mai sexy decât scrierea de
array-uri, așa că Latte
a adoptat-o imediat, unde poate fi utilizată, de exemplu, în etichetele
{include}
și {link}
:
{include 'file.latte' arg1: 1, arg2: 2}
{link default page: 1}
Vom reveni la argumentele numite în a treia parte a seriei, în legătură cu atributele.
O expresie poate arunca o excepție
Aruncarea unei excepții este acum o expresie. Puteți să o puneți între
paranteze și să o adăugați la o condiție if
. Hmmm, asta nu
sună foarte practic. Totuși, acest lucru este mult mai interesant:
// 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');
Deoarece funcțiile săgeată nu pot conține până acum decât o singură expresie, ele pot acum să arunce excepții datorită acestei caracteristici:
// only single expression
$fn = fn() => throw new \Exception('oops');
Expresie de potrivire
Instrucțiunea switch-case
are două vicii majore:
- utilizează comparația non-strictă
==
în loc de===
- trebuie să aveți grijă să nu uitați din greșeală
de
break
Din această cauză, PHP vine cu o alternativă sub forma unei noi expresii
match
, care utilizează o comparație strictă și, invers, nu
utilizează break
.
switch
exemplu de cod:
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;
}
Și același lucru (doar cu comparație strictă) scris folosind
match
:
$message = match ($statusCode) {
200, 300 => $this->formatMessage('ok'),
400 => $this->formatMessage('not found'),
500 => $this->formatMessage('server error'),
default => 'unknown status code',
};
Rețineți că match
nu este o structură de control ca
switch
, ci o expresie. În exemplu, atribuim valoarea sa
rezultată unei variabile. În același timp, “opțiunile” individuale sunt,
de asemenea, expresii, astfel încât nu este posibilă scrierea mai multor
pași, ca în cazul lui switch
.
În cazul în care nu există nicio potrivire (și nu există nicio clauză
implicită), se aruncă excepția UnhandledMatchError
.
Apropo, există și etichete {switch}
, {case}
și
{default}
în Latte. Funcția lor corespunde exact noului
match
. Acestea utilizează o comparație strictă, nu necesită
break
și este posibilă specificarea mai multor valori separate
prin virgule în case
.
Operatorul Nullsafe
Încatenarea opțională vă permite să scrieți o expresie a cărei
evaluare se oprește dacă întâlnește nul. Acest lucru se datorează noului
operator ?->
. Acesta înlocuiește o mulțime de cod care,
altfel, ar trebui să verifice în mod repetat dacă este nul:
$user?->getAddress()?->street
// approximately translates to
$user !== null && $user->getAddress() !== null
? $user->getAddress()->street
: null
De ce “aproximativ”? Pentru că, în realitate, expresia este evaluată
mai ingenios, astfel încât nu se repetă niciun pas. De exemplu,
$user->getAddress()
este apelat o singură dată, astfel
încât nu se poate întâmpina problema cauzată de faptul că metoda
returnează ceva diferit pentru prima și a doua oară.
Această caracteristică a fost adusă de Latte acum un an. Acum, PHP însuși o adoptă. Grozav.
Promovarea proprietăților constructorului
Zahăr sintactic care economisește scrierea de două ori a tipului și de patru ori a variabilei. Păcat că nu a venit într-un moment în care nu aveam IDE-uri atât de inteligente care astăzi să scrie în locul nostru 🙂
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,
) {}
}
Funcționează cu Nette DI, puteți începe să îl folosiți.
Verificări de tip mai stricte pentru operatorii aritmetici și operatorii bitwise
Ceea ce odată a ajutat limbajele de scripting dinamice să se impună a devenit punctul lor cel mai slab. Cândva, PHP a scăpat de “ghilimelele magice”, de înregistrarea variabilelor globale, iar acum comportamentul relaxat este înlocuit de strictețe. Vremea în care în PHP puteai să adaugi, să înmulțești etc. aproape orice tip de date pentru care nu avea sens, a trecut de mult. Începând cu versiunea 7.0, PHP devine din ce în ce mai strict și, începând cu versiunea 8.0, o încercare de a utiliza orice operator aritmetic/bitwise pe array-uri, obiecte sau resurse se termină cu TypeError. Excepție fac adăugirile de array-uri.
// arithmetic and bitwise operators
+, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, --:
Comparații mai sănătoase între șiruri și numere
Sau faceți ca operatorul loose să fie din nou grozav.
S-ar părea că nu mai este loc pentru operatorul loose ==
, că
este doar o greșeală de scriere la scrierea ===
, dar această
modificare îl readuce din nou pe hartă. Dacă îl avem deja, lăsați-l să se
comporte în mod rezonabil. Ca urmare a comparației “nerezonabile” de mai
devreme, de exemplu, in_array()
ar putea să te trolleze
neplăcut:
$validValues = ['foo', 'bar', 'baz'];
$value = 0;
dump(in_array($value, $validValues));
// surprisingly returned true
// since PHP 8.0 returns false
Modificarea comportamentului din ==
se referă la compararea
numerelor și a șirurilor “numerice” și este prezentată în tabelul
următor:
Comparare | Înainte | 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 |
În mod surprinzător, este vorba de o întrerupere a BC în chiar centrul limbii, care a fost aprobată fără nicio rezistență. Și asta este bine. JavaScript ar putea fi foarte invidios în această privință.
Raportarea erorilor
Multe funcții interne declanșează acum TypeError și ValueError în loc de
avertismente care erau ușor de ignorat. O serie de avertismente din kernel au
fost reclasificate. Operatorul de închidere @
nu mai reduce acum
la tăcere erorile fatale. Iar PDO aruncă excepții în mod implicit.
Nette a încercat întotdeauna să rezolve aceste lucruri într-un fel sau altul. Tracy a modificat comportamentul operatorului shutup, Database a schimbat comportamentul PDO, Utils conține înlocuirea funcțiilor standard, care aruncă excepții în loc de avertismente ușor de trecut cu vederea, etc. Este plăcut să vezi că direcția strictă pe care Nette o are în ADN-ul său devine direcția nativă a limbajului.
Incrementări negative ale cheilor de matrice
$arr[-5] = 'first';
$arr[] = 'second';
Care va fi cheia celui de-al doilea element? Înainte era 0
,
since PHP 8 it’s -4
.
Virgula de sfârșit
Ultimul loc în care virgula de sfârșit nu putea fi, era definirea argumentelor funcțiilor. Acest lucru este de domeniul trecutului:
public function __construct(
Nette\Database\Connection $db,
Nette\Mail\Mailer $mailer, // trailing comma
) {
....
}
$object::class
Constanta magică ::class
funcționează, de asemenea, cu
obiectele $object::class
, înlocuind complet funcția
get_class()
.
Prindeți excepțiile numai după tip
Și în cele din urmă: nu este necesar să specificați o variabilă pentru excepție în clauza catch:
try {
$container->getService(Database::class);
} catch (MissingServiceException) { // no $e
$logger->log('....');
}
*În următoarele părți, vom vedea noutățile majore în ceea ce privește tipurile de date, vom arăta ce sunt atributele, ce funcții și clase noi au apărut în PHP și vom prezenta compilatorul Just in Time.
Pentru a trimite un comentariu, vă rugăm să vă conectați