PHP 8.0: Noutăți în tipurile de date (2/4)
Versiunea 8.0 a PHP tocmai a fost lansată. Este plină de caracteristici noi, ca nicio versiune anterioară. Introducerea lor merită patru articole separate. În cea de-a doua parte, vom arunca o privire asupra tipurilor de date.
Să ne întoarcem în istorie. Introducerea indicilor de tip scalar a fost un
progres semnificativ în PHP 7. Aproape că nu s-a întâmplat. Andreu Faulds, autorul soluției ingenioase,
care era în întregime retrocompatibilă și opțională datorită
declare(strict_types=1)
, a fost respinsă cu duritate de către
comunitate. Din fericire, Anthony
Ferrara i-a luat apărarea ei și a propunerii la vremea respectivă, a
lansat o campanie și RFC-ul a trecut foarte aproape. Uf. Cele mai multe dintre
noutățile din PHP 8 sunt grație legendarului Nikita Popov și toate au trecut de vot ca un
cuțit prin unt. Lumea se schimbă în bine.
PHP 8 aduce tipurile la perfecțiune. Marea majoritate a adnotărilor
phpDoc, cum ar fi @param
, @return
și
@var
vor fi înlocuite cu notații native. Dar, cel mai important,
tipurile vor fi verificate de motorul PHP. Doar descrierile structurilor precum
string[]
sau adnotările mai complexe pentru PHPStan vor rămâne
în comentarii.
Tipuri de uniune
Tipurile de uniune sunt o enumerare a două sau mai multe tipuri pe care o variabilă le poate accepta:
class Button
{
private string|object $caption;
public function setCaption(string|object $caption)
{
$this->caption = $caption;
}
}
Anumite tipuri de uniune au mai fost introduse în PHP. De exemplu, tipurile
nulificabile. Cum ar fi ?string
, care este echivalent cu tipul de
uniune string|null
. Notația cu semnul întrebării poate fi
considerată o abreviere. Desigur, funcționează și în PHP 8, dar nu
o puteți combina cu barele verticale. Astfel, în loc de
?string|object
trebuie să scrieți
string|object|null
. În plus, iterable
a fost
întotdeauna echivalent cu array|Traversable
. S-ar putea să fiți
surprins de faptul că float
este, de asemenea, un tip de uniune,
deoarece acceptă atât int|float
, dar transformă valoarea în
float
.
Nu puteți utiliza pseudotipurile void
și mixed
în uniuni, deoarece nu ar avea sens.
Nette este pregătit pentru tipurile de uniune. În Schema, Expect::from()
le acceptă, iar prezentatorii le acceptă și ei. Le puteți utiliza în
metodele de redare și de acțiune, de exemplu:
public function renderDetail(int|array $id)
{
...
}
Pe de altă parte, autowiring în Nette DI respinge tipurile de uniune. Până în prezent, nu există niciun caz de utilizare în care ar avea sens ca constructorul să accepte fie unul, fie un alt obiect. Desigur, dacă apare un astfel de caz de utilizare, va fi posibil să se ajusteze comportamentul containerului în consecință.
Metodele getParameterType()
, getReturnType()
și
getPropertyType()
din Nette\Utils\Reflection
aruncă
o excepție în cazul tipului de uniune (asta în versiunea 3.1; în versiunea
anterioară 3.0, aceste metode se întorc pentru a menține compatibilitatea
cu null).
mixed
Pseudotipul mixed
acceptă orice valoare.
În cazul parametrilor și al proprietăților, acesta are același
comportament ca și în cazul în care nu se specifică niciun tip. Așadar,
care este scopul său? Pentru a distinge când un tip lipsește pur și simplu
și când este intenționat mixed
.
În cazul valorilor de retur ale funcțiilor și metodelor, faptul că nu se
specifică tipul diferă de utilizarea mixed
. Înseamnă opusul lui
void
, deoarece necesită ca funcția să returneze ceva.
O returnare lipsă produce atunci o eroare fatală.
În practică, ar trebui să o utilizați rar, deoarece, datorită tipurilor de uniune, puteți specifica valoarea cu precizie. Prin urmare, este adecvată doar în situații unice:
function dump(mixed $var): mixed
{
// print variable
return $var;
}
false
Spre deosebire de mixed
, puteți utiliza noul pseudotip
false
exclusiv în tipurile de uniune. Acesta a apărut din
necesitatea de a descrie tipul de retur al funcțiilor native, care în mod
istoric returnează false în caz de eșec:
function strpos(string $haystack, string $needle): int|false
{
}
Prin urmare, nu există un tip true
. Nu se poate folosi nici
false
singur, nici false|null
, nici
bool|false
.
static
Pseudotipul static
poate fi utilizat numai ca tip de retur al
unei metode. Acesta spune că metoda returnează un obiect de același tip ca
și obiectul însuși (în timp ce self
spune că returnează clasa
în care este definită metoda). Este excelent pentru a descrie interfețe
fluente:
class Item
{
public function setValue($val): static
{
$this->value = $val;
return $this;
}
}
class ItemChild extends Item
{
public function childMethod()
{
}
}
$child = new ItemChild;
$child->setValue(10)
->childMethod();
resource
Acest tip nu există în PHP 8 și nu va fi introdus în viitor. Resursele
sunt o relicvă din perioada în care PHP nu avea obiecte. Treptat, resursele
vor fi înlocuite de obiecte. În cele din urmă, acestea vor dispărea complet.
De exemplu, PHP 8.0 înlocuiește resursa imagine cu obiectul
GdImage
, iar resursa curl cu obiectul CurlHandle
.
Stringable
Este o interfață care este implementată automat de fiecare obiect cu
o metodă magică __toString()
.
class Email
{
public function __toString(): string
{
return $this->value;
}
}
function print(Stringable|string $s)
{
}
print('abc');
print(new Email);
Este posibil să se precizeze în mod explicit
class Email implements Stringable
în definiția clasei, dar nu
este necesar.
Nette\Utils\Html
reflectă, de asemenea, această schemă de
denumire prin implementarea interfeței Nette\HtmlStringable
în
locul fostei IHtmlString
. Obiectele de acest tip, de exemplu, nu
sunt scăpate de Latte.
Tip varianță, contravarianță, covarianță
Principiul de substituție Liskov (Liskov Substitution Principle – LSP) prevede că clasele de extensie și implementările interfețelor nu trebuie niciodată să ceară mai mult și să ofere mai puțin decât părintele. Altfel spus, metoda copil nu trebuie să solicite mai multe argumente sau să accepte o gamă mai restrânsă de tipuri de parametri decât cea părinte și, invers, nu trebuie să returneze o gamă mai largă de tipuri. Dar poate returna mai puține. De ce? Pentru că, în caz contrar, moștenirea s-ar întrerupe. O funcție ar accepta un obiect de un anumit tip, dar nu ar avea nicio idee despre parametrii pe care îi poate transmite metodelor sale și despre tipurile pe care le-ar returna. Orice copil ar putea să o rupă.
Așadar, în POO, clasa copil poate:
- accepta o gamă mai largă de tipuri în parametri (acest lucru se numește contravarianță)
- să returneze o gamă mai restrânsă de tipuri (covarianță)
- iar proprietățile nu își pot schimba tipul (sunt invariante).
PHP poate face acest lucru încă din versiunea 7.4, iar toate tipurile nou introduse în PHP 8.0 acceptă, de asemenea, contravarianța și covarianța.
În cazul lui mixed
, copilul poate restrânge valoarea de
returnare la orice tip, dar nu și void
, deoarece acesta nu
reprezintă o valoare, ci mai degrabă absența ei. De asemenea, copilul nu
poate omite o declarație de tip, deoarece aceasta permite, de asemenea,
absența unei valori.
class A
{
public function foo(mixed $foo): mixed
{}
}
class B extends A
{
public function foo($foo): string
{}
}
Tipurile de uniune pot fi, de asemenea, extinse în ceea ce privește parametrii și restrânse în ceea ce privește valorile de returnare:
class A
{
public function foo(string|int $foo): string|int
{}
}
class B extends A
{
public function foo(string|int|float $foo): string
{}
}
În plus, false
poate fi extins la bool
în
parametru sau invers, bool
la false
în valoarea de
returnare.
Toate infracțiunile împotriva covarianței/contravarianței conduc la o eroare fatală în PHP 8.0.
În următoarele părți ale acestei serii, vom arăta care sunt atributele, ce funcții și clase noi au apărut în PHP și vom prezenta Just in Time Compiler.
Pentru a trimite un comentariu, vă rugăm să vă conectați