PHP 8.0: popoln pregled novic (1/4)
Prav zdaj je izšla različica PHP 8.0. Polna je novosti, kot še nobena različica do zdaj. Njihova predstavitev si je zaslužila štiri ločene članke. V prvem si bomo ogledali, kaj prinaša na ravni jezika.
Preden se poglobimo v PHP, naj povemo, da je trenutna različica Nette v celoti pripravljena na osmo različico. Poleg tega je bila kot darilo izdana popolnoma združljiva različica Nette 2.4, tako da z vidika ogrodja ni ničesar, kar bi vas oviralo pri uporabi.
Poimenovani argumenti
Takoj začnimo z bombo, ki bi jo lahko pogumno označili kot spreminjajočo igro. Argumentov lahko zdaj funkcijam in metodam ne posredujemo le pozicijsko, temveč tudi glede na njihovo ime. Kar je povsem kul, če ima metoda res preveč 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 se posredujeta pozicijsko, drugi pa po imenu: (poimenovani morajo slediti za pozicijskimi)
$response->setCookie('lang', $lang, sameSite: 'None');
// instead of the horrible
$response->setCookie('lang', $lang, null, null, null, null, null, 'None');
Pred uvedbo te funkcije je bilo načrtovano, da se ustvari nov API za
pošiljanje piškotkov v Nette, saj je število argumentov za
setCookie()
res naraslo, položajni zapis pa je bil zmeden. To ni
več potrebno, saj so poimenovani argumenti v tem primeru najprimernejši API.
IDE jih bo namignil in zagotovljena je tipska varnost.
Idealno so primerni tudi za razlago logičnih argumentov, kjer njihova
uporaba ni nujna, vendar navaden true
ali false
ne
zadostuje:
// before
$db = $container->getService(Database::class, true);
// now
$db = $container->getService(Database::class, need: true);
Imena argumentov so zdaj del javnega vmesnika API. Ni jih več mogoče poljubno spreminjati. Zaradi tega celo Nette izvaja revizijo, s katero ugotavlja, ali imajo vsi argumenti ustrezno ime.
Poimenovani argumenti se lahko uporabljajo tudi v kombinaciji z variadikami:
function variadics($a, ...$args) {
dump($args);
}
variadics(a: 1, b: 2, c: 3);
// $args will contain ['b' => 2, 'c' => 3]
Polje $args
lahko zdaj vsebuje tudi neštevilčne ključe, kar
je nekakšen prelom BC. Enako velja za obnašanje
call_user_func_array($func, $args)
, kjer ključi v polju
$args
zdaj igrajo veliko pomembnejšo vlogo. Nasprotno pa so
funkcije družine func_*()
zaščitene pred poimenovanimi
argumenti.
Poimenovani argumenti so tesno povezani z dejstvom, da lahko operator splat
...
zdaj razširi asociativna polja:
variadics(...['b' => 2, 'c' => 3]);
Presenetljivo je, da trenutno ne deluje znotraj polj:
$arr = [ ...['a' => 1, 'b' => 2] ];
// Fatal error: Cannot unpack array with string keys
Kombinacija poimenovanih argumentov in variadike omogoča, da imamo končno
fiksno sintakso, na primer za metodo link()
, ki ji lahko
posredujemo tako poimenovane kot pozicijske argumente:
// 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);
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 argumentom se bomo vrnili v tretjem delu serije v povezavi z atributi.
Izraz lahko vrže izjemo
Odvrženje izjeme je zdaj izraz. Lahko ga zavijete v oklepaje in dodate
v pogoj if
. Hmmm, to se ne sliši preveč praktično. Vendar je to
veliko bolj zanimivo:
// 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');
Ker so lahko puščične funkcije do zdaj vsebovale le en izraz, lahko zdaj zaradi te lastnosti mečejo izjeme:
// only single expression
$fn = fn() => throw new \Exception('oops');
Izraz ujemanja
Izjava switch-case
ima dve glavni pomanjkljivosti:
- uporablja strogo primerjavo
==
namesto===
- paziti morate, da ne bi pomotoma pozabili na
break
Zaradi tega ima PHP alternativo v obliki novega izraza match
,
ki uporablja strogo primerjavo in, nasprotno, ne uporablja
break
.
switch
primer kode:
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;
}
In enako (le s strogo primerjavo), napisano z uporabo
match
:
$message = match ($statusCode) {
200, 300 => $this->formatMessage('ok'),
400 => $this->formatMessage('not found'),
500 => $this->formatMessage('server error'),
default => 'unknown status code',
};
Upoštevajte, da match
ni kontrolna struktura kot
switch
, temveč izraz. V primeru njegovo dobljeno vrednost
pripišemo spremenljivki. Hkrati so tudi posamezne “možnosti” izrazi, zato
ni mogoče zapisati več korakov, kot v primeru switch
.
V primeru, da ni ujemanja (in ni privzetega stavka), se vrže izjema
UnhandledMatchError
.
Mimogrede, v programu Latte obstajajo tudi oznake {switch}
,
{case}
in {default}
. Njihova funkcija natančno
ustreza novi oznaki match
. Uporabljajo strogo primerjavo, ne
zahtevajo break
in v case
je mogoče navesti več
vrednosti, ločenih z vejicami.
Operator Nullsafe
Neobvezno veriženje omogoča zapis izraza, katerega vrednotenje se ustavi,
če naleti na ničlo. To omogoča novi operator ?->
. Z njim
nadomestimo veliko kode, ki bi sicer morala večkrat preveriti ničlo:
$user?->getAddress()?->street
// approximately translates to
$user !== null && $user->getAddress() !== null
? $user->getAddress()->street
: null
Zakaj “približno”? Ker se v resnici izraz ovrednoti bolj domiselno,
tako da se noben korak ne ponovi. Na primer, $user->getAddress()
se kliče samo enkrat, zato ne moremo naleteti na težavo, ki bi jo povzročila
metoda, ki bi prvič in drugič vrnila nekaj drugega.
To funkcijo je pred letom dni:https://blog.nette.org/…om-functions prinesel Latte. Zdaj jo je prevzel tudi PHP. Odlično.
Spodbujanje lastnosti konstruktorja
Sintaktični sladkor, ki prihrani dvakratno zapisovanje tipa in štirikratno zapisovanje spremenljivke. Škoda, da se ni pojavil v času, ko nismo imeli tako pametnih IDE, ki ga 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,
) {}
}
Deluje z Nette DI, lahko ga začnete uporabljati.
Strožji pregledi tipov za aritmetične in bitne operatorje
Kar je nekoč pomagalo dinamičnim skriptnim jezikom, da so postali prepoznavni, je postalo njihova najšibkejša točka. Nekoč se je PHP znebil “čarobnih narekovajev”, registracije globalnih spremenljivk, zdaj pa sproščeno obnašanje nadomešča strogost. Časi, ko ste lahko v PHP dodajali, množili itd. skoraj vse vrste podatkov, za katere to ni imelo smisla, so že zdavnaj minili. Od različice 7.0 je PHP vse strožji, od različice 8.0 pa se poskus uporabe kakršnih koli aritmetičnih/bitnih operatorjev na poljih, predmetih ali virih konča z TypeError. Izjema je dodajanje polj.
// arithmetic and bitwise operators
+, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, --:
Varnejše primerjave nizov s številkami
Ali pa ponovno naredite odličen operater loose.
Zdi se, da za operator loose ==
ni več prostora, da gre le za
tiskarsko napako pri pisanju ===
, vendar ga ta sprememba spet
vrača na zemljevid. Če ga že imamo, naj se obnaša razumno. Zaradi prejšnje
“nerazumne” primerjave bi lahko na primer in_array()
neprijetno
trotil:
$validValues = ['foo', 'bar', 'baz'];
$value = 0;
dump(in_array($value, $validValues));
// surprisingly returned true
// since PHP 8.0 returns false
Sprememba obnašanja ==
se nanaša na primerjavo števil in
“številskih” nizov in je prikazana v naslednji tabeli:
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 je bil ta prelom BC v samem jedru jezika odobren brez kakršnega koli odpora. In to je dobro. JavaScript bi bil lahko v tem pogledu zelo ljubosumen.
Poročanje o napakah
Številne notranje funkcije zdaj namesto opozoril, ki jih je bilo preprosto
prezreti, sprožijo TypeError in ValueError. Številna opozorila jedra so bila
prerazvrščena. Operator za izklop @
zdaj ne utiša usodnih napak.
In PDO privzeto meče izjeme.
Nette je te stvari vedno poskušal rešiti na nek način. Tracy je spremenil obnašanje operatorja shutup, baza podatkov je spremenila obnašanje PDO, Utils vsebuje nadomestne standardne funkcije, ki namesto lahko spregledljivih opozoril mečejo izjeme, itd. Lepo je videti, da stroga smer, ki jo ima Nette v svoji DNK, postane izvorna smer jezika.
Negativni prirastki ključa polja
$arr[-5] = 'first';
$arr[] = 'second';
Kakšen bo ključ drugega elementa? Včasih je bil 0
, since PHP
8 it’s -4
.
Končna vejica
Zadnje mesto, kjer vejice ni bilo mogoče postaviti, je bila definicija argumentov funkcij. To je stvar preteklosti:
public function __construct(
Nette\Database\Connection $db,
Nette\Mail\Mailer $mailer, // trailing comma
) {
....
}
$object::class
Čarobna konstanta ::class
deluje tudi pri predmetih
$object::class
in popolnoma nadomesti funkcijo
get_class()
.
Ujemanje izjem samo po vrsti
In končno: v stavku catch ni treba določiti spremenljivke za izjemo:
try {
$container->getService(Database::class);
} catch (MissingServiceException) { // no $e
$logger->log('....');
}
V naslednjih delih si bomo ogledali glavne novosti v zvezi s podatkovnimi tipi, pokazali bomo, kaj so atributi, katere nove funkcije in razredi so se pojavili v PHP in predstavili prevajalnik Just in Time.
Če želite oddati komentar, se prijavite