PHP 8.0: Az újdonságok teljes áttekintése (1/4)
Megjelent a PHP 8.0-s verziója. Annyira tele van újdonságokkal, mint még egyetlen verzió sem korábban. Bemutatásukhoz egyenesen négy különálló cikkre volt szükség. Ebben az elsőben megnézzük, mit hoz újat a nyelv szintjén.

Mielőtt belemerülnénk a PHP-ba, tudnia kell, hogy a Nette jelenlegi verziója teljesen készen áll a nyolcasra. Sőt, ajándékként még a Nette 2.4 is megjelent, amely teljesen kompatibilis vele, így a keretrendszer szempontjából semmi sem akadályozza meg az új verzió használatának megkezdését.
Elnevezett argumentumok
És rögtön egy bombával kezdünk, amelyet bátran nevezhetünk game changernek. Újdonság, hogy a függvényeknek és metódusoknak az argumentumokat nemcsak pozicionálisan, hanem név szerint is át lehet adni. Ami teljesen nagyszerű abban az esetben, ha egy metódusnak valóban sok paramétere van:
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
) {
...
}
}
Az első két argumentumot pozicionálisan adjuk át, a többit név szerint: (az elnevezetteknek mindig a pozicionálisak után kell következniük)
$response->setCookie('lang', $lang, sameSite: 'None');
// az őrült helyett
$response->setCookie('lang', $lang, null, null, null, null, null, 'None');
Ennek a funkciónak a megjelenése előtt tervben volt egy új API
létrehozása a Nette-ben a sütik küldésére, mivel a
setCookie()
paramétereinek száma valóban megnőtt, és a
pozicionális írásmód átláthatatlan volt. Most már erre nincs szükség,
mert az elnevezett argumentumok ebben az esetben a legpraktikusabb API-t
jelentik. Az IDE súgni fogja őket, és típusellenőrzéssel rendelkeznek.
Kiválóan alkalmasak a logikai paraméterek magyarázatára is, ahol
használatuk ugyan nem szükséges, de önmagában a true
vagy
false
nem sokat mond:
// korábban
$db = $container->getService(Database::class, true);
// most
$db = $container->getService(Database::class, need: true);
A paraméternevek mostantól a nyilvános API részévé válnak. Nem lehet őket tetszőlegesen megváltoztatni, mint eddig. Emiatt a Nette is átvizsgáláson esik át, hogy minden paraméternek megfelelő elnevezése legyen.
Az elnevezett argumentumokat a variadics-szal kombinálva is lehet használni:
function variadics($a, ...$args) {
dump($args);
}
variadics(a: 1, b: 2, c: 3);
// az $args-ban ['b' => 2, 'c' => 3] lesz
Újdonság, hogy az $args
tömb mostantól nem numerikus
kulcsokat is tartalmazhat, ami egy bizonyos BC break. Ugyanez vonatkozik a
call_user_func_array($func, $args)
függvény viselkedésére is,
ahol mostantól az $args
tömb kulcsai szignifikáns szerepet
játszanak. Ezzel szemben a func_*()
család függvényei
árnyékolva vannak az elnevezett argumentumoktól.
Az elnevezett argumentumokkal szorosan összefügg az a tény is, hogy a
splat operátor ...
mostantól asszociatív tömböket is
kibonthat:
variadics(...['b' => 2, 'c' => 3]);
Meglepő módon ez még nem működik a tömbökön belül:
$arr = [ ...['a' => 1, 'b' => 2] ];
// Fatal error: Cannot unpack array with string keys
Az elnevezett argumentumok és a variadics kombinációja lehetőséget ad
arra, hogy végre legyen egy fix szintaxis például a presenter
link()
metódusához, amelynek mostantól az elnevezett
argumentumokat ugyanúgy átadhatjuk, mint a pozicionálisakat:
// korábban
$presenter->link('Product:detail', $id, 1, 2);
$presenter->link('Product:detail', [$id, 'page' => 1]); // tömbnek kellett lennie
// most
$presenter->link('Product:detail', $id, page: 1);
A megnevezett argumentumok szintaxisa sokkal szexibb, mint a tömbök
írása, ezért a Latte
azonnal átvette, ahol például a {include}
és a
{link}
címkékben használható:
{include 'file.latte' arg1: 1, arg2: 2}
{link default page: 1}
Az elnevezett paraméterekhez még visszatérünk a harmadik részben az attribútumokkal kapcsolatban.
Egy kifejezés kivételt dobhat
A kivétel dobása mostantól kifejezés. Például zárójelekbe teheti és
hozzáadhatja egy if
feltételhez. Hmmm, ez nem hangzik túl
praktikusnak. De ez már érdekesebb:
// korábban
if (!isset($arr['value'])) {
throw new \InvalidArgumentException('érték nincs beállítva');
}
$value = $arr['value'];
// most, amikor a throw kifejezés
$value = $arr['value'] ?? throw new \InvalidArgumentException('érték nincs beállítva');
Mivel az arrow függvények eddig csak egyetlen kifejezést tartalmazhattak, ennek a funkciónak köszönhetően kivételeket dobhatnak:
// csak egyetlen kifejezés
$fn = fn() => throw new \Exception('hoppá');
Match kifejezések
A switch-case
szerkezetnek két nagy hibája van:
- nem szigorú összehasonlítást (
==
) használ a===
helyett - figyelni kell, nehogy véletlenül elfelejtsük a
break
-et
A PHP ezért egy alternatívát kínál az új match
szerkezet
formájában, amely szigorú összehasonlítást használ, és ezzel szemben nem
használ break
-et.
Példa switch
kódra:
switch ($statusCode) {
case 200:
case 300:
$message = $this->formatMessage('ok');
break;
case 400:
$message = $this->formatMessage('nem található');
break;
case 500:
$message = $this->formatMessage('szerverhiba');
break;
default:
$message = 'ismeretlen státuszkód';
break;
}
És ugyanez (csak szigorú összehasonlítással) a match
segítségével írva:
$message = match ($statusCode) {
200, 300 => $this->formatMessage('ok'),
400 => $this->formatMessage('nem található'),
500 => $this->formatMessage('szerverhiba'),
default => 'ismeretlen státuszkód',
};
Figyelje meg, hogy a match
nem egy vezérlési struktúra, mint
a switch
, hanem egy kifejezés. A példában az
eredményértékét egy változóhoz rendeljük. Ugyanakkor az egyes
“lehetőségek” is kifejezések, tehát nem lehet több lépést írni, mint
a switch
esetében.
Ha egyik opció sem egyezik meg (és nincs default klauzula), egy
UnhandledMatchError
kivétel dobódik.
Mellesleg, a Latte-ban is léteznek {switch}
,
{case}
és {default}
tagek. Működésük pontosan
megfelel az új match
-nek. Szigorú összehasonlítást
használnak, nem igényelnek break
-et, és a case
-ben
több, vesszővel elválasztott értéket is meg lehet adni.
Nullsafe operátor
Az opcionális láncolás (optional chaining) lehetővé teszi olyan
kifejezés írását, amelynek kiértékelése leáll, ha null-ra ütközik. És
ez az új ?->
operátornak köszönhető. Sok olyan kódot
helyettesít, amely egyébként ismételten ellenőrizné a null-t:
$user?->getAddress()?->street
// kb. ezt jelenti
$user !== null && $user->getAddress() !== null
? $user->getAddress()->street
: null
Miért „kb. ezt jelenti”? Mert valójában a kifejezés kiértékelése
bonyolultabban történik, és egyetlen lépés sem ismétlődik meg. Például
a $user->getAddress()
csak egyszer hívódik meg, tehát nem
merülhet fel az a probléma, hogy a metódus először és másodszor mást ad
vissza.
Ezt a friss újdonságot egy évvel ezelőtt hozta a Latte. Most magába a PHP-ba is bekerül. Nagyszerű.
Konstruktor property promóció
Szintaktikai cukorka, amely megspórolja a típus kétszeres és a változó négyszeres írását. Kár, hogy nem akkor jött, amikor még nem voltak ilyen okos IDE-ink, amelyek ma már helyettünk írják ezt 🙂
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,
) {}
}
A Nette DI-vel működik, azonnal elkezdheti használni.
Aritmetikai és bitenkénti operátorok szigorú viselkedése
Ami egykor a dinamikus szkriptnyelveket a csúcsra repítette, idővel a leggyengébb pontjukká vált. A PHP egykor megszabadult a “magic quotes”-tól, a globális változók regisztrálásától, és most a laza viselkedést a szigorúság váltja fel. Az az idő, amikor a PHP-ban szinte bármilyen adattípust összeadhattunk, szorozhattunk stb., amelyeknek semmi értelme nem volt, már rég elmúlt. A 7.0-s verziótól kezdve a PHP egyre szigorúbb, és a 8.0-s verziótól kezdve már bármilyen aritmetikai/bitenkénti operátor használatának kísérlete tömbökön, objektumokon vagy erőforrásokon TypeError-t eredményez. Kivétel a tömbök összeadása.
// aritmetikai és bitenkénti operátorok
+, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, --:
Ésszerűbb string és szám összehasonlítások
Avagy make loose operator great again.
Úgy tűnhet, hogy a laza ==
operátornak már nincs helye, hogy
csak egy elírás a ===
írásakor, de ez a változás újra a
térképre helyezi. Ha már megvan, viselkedjen ésszerűen. A korábbi
“ésszerűtlen” összehasonlítás következménye volt például az
in_array()
viselkedése, amely kellemetlenül megviccelhetett:
$validValues = ['foo', 'bar', 'baz'];
$value = 0;
dump(in_array($value, $validValues));
// meglepő módon true-t adott vissza
// PHP 8.0-tól false-t ad vissza
Az ==
viselkedésének változása a számok és a
“numerikus” stringek összehasonlítására vonatkozik, és a következő
táblázat mutatja:
Összehasonlítás | Korábban | 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 |
Meglepő, hogy ez egy BC break a nyelv alapjaiban, amelyet minden ellenállás nélkül jóváhagytak. És ez jó. Itt a JavaScript sokat irigykedhetne.
Hibajelentés
Sok belső függvény mostantól TypeError-t és ValueError-t vált ki a
figyelmeztetések helyett, amelyeket könnyű volt figyelmen kívül hagyni.
Számos mag figyelmeztetést újraosztályoztak. A shutup operátor
@
mostantól nem némítja el a fatális hibákat. És a PDO
alapértelmezett módban kivételeket dob.
Ezeket a dolgokat a Nette mindig is megpróbálta valamilyen módon kezelni. A Tracy módosította a shutup operátor viselkedését, a Database átkapcsolta a PDO viselkedését, a Utils tartalmazza a standard függvények helyettesítőit, amelyek kivételeket dobnak a feltűnésmentes figyelmeztetések helyett stb. Jó látni, hogy a szigorú irány, amely a Nette DNS-ében van, a nyelv natív irányává válik.
Negatív indexű tömbök
$arr[-5] = 'első';
$arr[] = 'második';
Mi lesz a második elem kulcsa? Korábban 0
volt, PHP 8-tól
-4
.
Végződő vessző
Az utolsó hely, ahol nem lehetett végződő vessző, a függvényparaméterek definíciója volt. Ez már a múlté:
public function __construct(
Nette\Database\Connection $db,
Nette\Mail\Mailer $mailer, // végződő vessző
) {
....
}
$object::class
A mágikus ::class
konstans objektumokkal is működik
($object::class
), ezzel teljesen helyettesítve a
get_class()
függvényt.
catch változó nélkül
És végül: a catch klauzulában nem szükséges megadni a kivétel változóját:
try {
$container->getService(Database::class);
} catch (MissingServiceException) { // nincs $e
$logger->log('....');
}
A következő részekben az adattípusok terén történt alapvető újdonságokkal foglalkozunk, megmutatjuk, mik azok az attribútumok, milyen új függvények és osztályok jelentek meg a PHP-ban, és bemutatjuk a Just in Time Compilert.
A hozzászólás elküldéséhez kérjük, jelentkezzen be