PHP 8.0: Popoln pregled novosti (1/4)
Izšla je različica PHP 8.0. Tako je polna novosti, kot še nobena različica pred njo. Njihova predstavitev je zahtevala kar štiri ločene članke. V tem prvem si bomo pogledali, kaj novega prinaša po jezikovni plati.

Še preden se poglobimo v PHP, vedite, da je trenutna različica Nette popolnoma pripravljena za osmico. Še več, kot darilo je izšel celo Nette 2.4, ki je z njo popolnoma združljiv, tako da vam z vidika ogrodja nič ne preprečuje, da začnete uporabljati novo različico.
Poimenovani argumenti
In začnimo kar z bombo, ki jo lahko mirno označimo za game changer. Po novem lahko funkcijam in metodam argumente posredujemo ne le pozicijsko, ampak tudi po imenih. Kar je popolnoma odlično v primeru, da ima metoda res veliko parametrov:
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
) {
...
}
}
Prva dva argumenta posredujemo pozicijsko, ostale po imenu: (poimenovani morajo vedno slediti pozicijskim)
$response->setCookie('lang', $lang, sameSite: 'None');
// namesto norega
$response->setCookie('lang', $lang, null, null, null, null, null, 'None');
Pred prihodom te funkcije je bilo v načrtu ustvariti v Nette nov API za
pošiljanje piškotkov, ker je število parametrov setCookie()
res
naraslo in pozicijski zapis je bil nepregleden. Zdaj to ni več potrebno, ker so
poimenovani argumenti v tem primeru najbolj praktičen API. IDE jih bo
predlagal in imajo tipsko kontrolo.
Odlično se obnesejo tudi za dodatno pojasnitev logičnih parametrov, kjer
njihova uporaba sicer ni nujna, a samo true
ali false
ne pove veliko:
// prej
$db = $container->getService(Database::class, true);
// zdaj
$db = $container->getService(Database::class, need: true);
Imena parametrov zdaj postajajo del javnega API-ja. Ni jih mogoče poljubno spreminjati kot doslej. Zato tudi Nette prehaja skozi revizijo, ali imajo vsi parametri ustrezno poimenovanje.
Poimenovane argumente lahko uporabljamo tudi v kombinaciji z variadics:
function variadics($a, ...$args) {
dump($args);
}
variadics(a: 1, b: 2, c: 3);
// v $args bo ['b' => 2, 'c' => 3]
Po novem lahko polje $args
vsebuje tudi neštevilske ključe,
kar je določen BC break. Enako velja tudi za obnašanje funkcije
call_user_func_array($func, $args)
, kjer zdaj ključi v polju
$args
igrajo pomembno vlogo. Nasprotno pa so funkcije iz družine
func_*()
od poimenovanih argumentov zaščitene.
S poimenovanimi argumenti je tesno povezano tudi dejstvo, da splat operator
...
lahko zdaj razširi tudi asociativna polja:
variadics(...['b' => 2, 'c' => 3]);
Presenetljivo to zaenkrat ne deluje znotraj polj:
$arr = [ ...['a' => 1, 'b' => 2] ];
// Fatal error: Cannot unpack array with string keys
Kombinacija poimenovanih argumentov in variadics daje možnost končno imeti
fiksno sintakso na primer za metodo presenterja link()
, kateri zdaj
lahko poimenovane argumente posredujemo enako kot pozicijske:
// prej
$presenter->link('Product:detail', $id, 1, 2);
$presenter->link('Product:detail', [$id, 'page' => 1]); // moralo je biti polje
// zdaj
$presenter->link('Product:detail', $id, page: 1);
Sintaksa za poimenovane argumente je veliko bolj seksi kot pisanje polj, zato
jo je “Latte takoj posvojil:https://blog.nette.org/…ot-for-least#…”,
kjer jo lahko uporabljamo na primer v oznakah {include}
in
{link}
:
{include 'file.latte' arg1: 1, arg2: 2}
{link default page: 1}
K poimenovanim parametrom se bomo vrnili še v tretjem delu v povezavi z atributi.
Izraz lahko vrže izjemo
Vržanje izjeme je zdaj izraz. Lahko ga na primer ovijete z oklepaji in
dodate v pogoj if
. Hmmm, to ne zveni preveč praktično. Ampak
tole je že bolj zanimivo:
// prej
if (!isset($arr['value'])) {
throw new \InvalidArgumentException('vrednost ni nastavljena');
}
$value = $arr['value'];
// zdaj, ko je throw izraz
$value = $arr['value'] ?? throw new \InvalidArgumentException('vrednost ni nastavljena');
Ker puščične funkcije lahko doslej vsebujejo le en izraz, lahko zahvaljujoč tej funkciji mečejo izjeme:
// samo en izraz
$fn = fn() => throw new \Exception('ups');
Match Expressions
Konstrukcija switch-case
ima dve veliki slabosti:
- uporablja nestrogo primerjanje
==
namesto===
- morate paziti, da pomotoma ne pozabite na
break
PHP zato prihaja z alternativo v obliki nove konstrukcije
match
, ki uporablja strogo primerjanje in nasprotno ne uporablja
break
.
Primer kode switch
:
switch ($statusCode) {
case 200:
case 300:
$message = $this->formatMessage('ok');
break;
case 400:
$message = $this->formatMessage('ni najdeno');
break;
case 500:
$message = $this->formatMessage('napaka strežnika');
break;
default:
$message = 'neznana koda stanja';
break;
}
In enako (le s strogim primerjanjem) zapisano s pomočjo
match
:
$message = match ($statusCode) {
200, 300 => $this->formatMessage('ok'),
400 => $this->formatMessage('ni najdeno'),
500 => $this->formatMessage('napaka strežnika'),
default => 'neznana koda stanja',
};
Opazite, da match
ni kontrolna struktura kot
switch
, ampak izraz. Njegovo končno vrednost v primeru dodelimo
spremenljivki. Hkrati so tudi posamezne “možnosti” izrazi, zato ni mogoče
zapisati več korakov, kot v primeru switch
.
Če ne pride do ujemanja z nobeno od izbir (in ne obstaja klavzula default),
se vrže izjema UnhandledMatchError
.
Mimogrede, v Latte obstajajo tudi značke {switch}
,
{case}
in {default}
. Njihovo delovanje natančno
ustreza novemu match
. Uporabljajo strogo primerjanje, ne zahtevajo
break
in v case
je mogoče navesti več vrednosti,
ločenih z vejicami.
Nullsafe operator
Opcijsko veriženje (optional chaining) omogoča pisanje izraza, katerega
vrednotenje se ustavi, če naleti na null. In to zahvaljujoč novemu operatorju
?->
. Nadomesti veliko kode, ki bi sicer ponavljajoče
preverjala null:
$user?->getAddress()?->street
// pomeni približno
$user !== null && $user->getAddress() !== null
? $user->getAddress()->street
: null
Zakaj “pomeni približno”? Ker se v resnici izraz vrednoti bolj
domiselno in se noben korak ne ponovi. Na primer
$user->getAddress()
se kliče le enkrat, torej ne more nastati
problem, povzročen s tem, da bi metoda prvič in drugič vrnila nekaj
drugega.
To svežo izboljšavo je pred enim letom prinesel Latte. Zdaj prihaja v sam PHP. Odlično.
Constructor property promotion
Sintaktični sladkor, ki prihrani dvojno pisanje tipa in štirikratno pisanje spremenljivke. Škoda le, da ni prišel v času, ko nismo imeli tako pametnih IDE, ki to danes pišejo namesto nas 🙂
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,
) {}
}
Z Nette DI deluje, lahko takoj začnete uporabljati.
Strogo obnašanje aritmetičnih in bitnih operatorjev
Kar je nekoč dinamične skriptne jezike izstrelilo na vrh, je sčasoma postalo njihova najšibkejša točka. PHP se je nekoč znebil “magic quotes”, registriranja globalnih spremenljivk in zdaj sproščeno obnašanje zamenjuje strogost. Čas, ko ste v PHP lahko seštevali, množili itd. skoraj katerekoli podatkovne tipe, pri katerih to ni imelo prav nobenega smisla, je že davno minil. Od različice 7.0 je PHP vedno bolj strog in od različice 8.0 že poskus uporabe kateregakoli aritmetičnega/bitnega operatorja pri poljih, objektih ali virih konča s TypeError. Izjema je seštevanje polj.
// aritmetični in bitni operatorji
+, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, --:
Razumnejše primerjanje nizov in števil
Ali make loose operator great again.
Zdelo bi se, da za loose operator ==
ni več prostora, da gre le
za tipkarsko napako pri pisanju ===
, ampak ta sprememba ga vrača
nazaj na zemljevid. Ko ga že imamo, naj se obnaša razumno. Posledica
prejšnjega “nerazumnega” primerjanja je bilo na primer obnašanje
in_array()
, ki vas je lahko neprijetno presenetilo:
$validValues = ['foo', 'bar', 'baz'];
$value = 0;
dump(in_array($value, $validValues));
// presenetljivo je vračalo true
// od PHP 8.0 vrača false
Sprememba v obnašanju ==
se nanaša na primerjanje števil in
“številskih” nizov in jo prikazuje naslednja tabela:
Primerjava | Pred | 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 |
Presenetljivo je, da gre za BC break v samem temelju jezika, ki je bil odobren brez kakršnegakoli odpora. In to je dobro. Tu bi JavaScript lahko zelo zavidal.
Poročanje napak
Mnoge interne funkcije zdaj sprožijo TypeError in ValueError namesto
opozoril, ki jih je bilo mogoče zlahka spregledati. Reklasificirana je bila
vrsta opozoril jedra. Shutup operator @
zdaj ne utiša fatalnih
napak. In PDO v privzetem načinu meče izjeme.
Te stvari je Nette vedno poskušal nekako rešiti. Tracy je prilagajal obnašanje shutup operatorja, Database je preklapljal obnašanje PDO, Utils vsebuje nadomestke standardnih funkcij, ki mečejo izjeme namesto neopaznih opozoril itd. Lepo je videti, da stroga smer, ki jo ima Nette v svoji DNK, postaja nativna smer jezika.
Polja z negativnim indeksom
$arr[-5] = 'prvi';
$arr[] = 'drugi';
Kakšen bo ključ drugega elementa? Prej je bil 0
, od PHP 8 je
-4
.
Končna vejica
Zadnje mesto, kjer ni mogla biti končna vejica, je bila definicija parametrov funkcije. To je zdaj preteklost:
public function __construct(
Nette\Database\Connection $db,
Nette\Mail\Mailer $mailer, // končna vejica
) {
....
}
$object::class
Magična konstanta ::class
deluje tudi z objekti
$object::class
, s čimer popolnoma nadomešča funkcijo
get_class()
.
catch brez spremenljivke
In končno: v klavzuli catch ni potrebno navajati spremenljivke za izjemo:
try {
$container->getService(Database::class);
} catch (MissingServiceException) { // brez $e
$logger->log('....');
}
V prihodnjih delih nas čakajo bistvene novosti v podatkovnih tipih, pokazali si bomo, kaj so atributi, katere nove funkcije in razredi so se pojavili v PHP in predstavili si bomo Just in Time Compiler.
Če želite oddati komentar, se prijavite