PHP 8.0: Adattípusok (2/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 a másodikban az adattípusokat nézzük meg.

Térjünk vissza a történelembe. A PHP 7 alapvető áttörése a
skaláris type hintek bevezetése volt. Majdnem nem történt meg.
A csodálatos megoldás szerzőjét, Andrea
Faulds, amely a declare(strict_types=1)
-nek köszönhetően
teljesen visszamenőlegesen kompatibilis és opcionális volt, a közösség
csúnyán elutasította. Szerencsére akkoriban Anthony Ferrara mellé állt, kampányt
indított, és az RFC nagyon szűken átment. Húha. A PHP 8 legtöbb
újdonsága a legendás Nikita Popov
nevéhez fűződik, és a szavazáson simán átmentek neki. A világ
jobbá válik.
A PHP 8 tökéletesíti a típusokat. A kód phpDoc annotációinak
(@param
, @return
és @var
) túlnyomó
többsége eltűnik, és helyüket natív írásmód és főleg a PHP motor
általi ellenőrzés veszi át. A kommentekben csak a struktúrák leírásai
maradnak, mint a string[]
vagy bonyolultabb annotációk a PHPStan
számára.
Union típusok
Az union típusok két vagy több olyan típus felsorolása, amelyeket egy érték felvehet:
class Button
{
private string|object $caption;
public function setCaption(string|object $caption)
{
$this->caption = $caption;
}
}
Néhány speciális union típust a PHP már korábban is ismert. Például a
nullable típusokat, mint a ?string
, ami a string|null
union típus ekvivalense, és a kérdőjeles írásmódot csak rövidítésnek
tekinthetjük. Természetesen ez PHP 8-ban is működik, de nem kombinálható
függőleges vonalakkal, tehát a ?string|object
helyett a teljes
string|object|null
-t kell írni. Továbbá az iterable
mindig is az array|Traversable
ekvivalense volt. Talán meglepő,
hogy az union típus tulajdonképpen a float
is, amely valójában
int|float
-ot fogad el, de float
-ra konvertálja.
Az unionokban nem használhatók a void
és mixed
pszeudotípusok, mert ennek semmi értelme nem lenne.
A Nette készen áll az union típusokra. A Schemában a Expect::from()
elfogadja őket, és a prezenterek is elfogadják őket. Használhatod őket
például a render és action metódusokban:
public function renderDetail(int|array $id)
{
...
}
Ezzel szemben a Nette DI autowiring elutasítja az union típusokat. Hiányzik még a use case, ahol értelme lenne annak, hogy például a konstruktor vagy ezt, vagy azt az objektumot fogadja el. Természetesen, ha megjelenik, a konténer viselkedését ennek megfelelően lehet majd módosítani.
A getParameterType()
, getReturnType()
és
getPropertyType()
metódusok a Nette\Utils\Reflection-ben kivételt
dobnak union típus esetén (a 3.1-es verzióban, a régebbi 3.0-ban a
kompatibilitás miatt null-t adnak vissza).
mixed
A mixed
pszeudotípus azt jelenti, hogy az érték
bármi lehet.
Paraméterek és property-k esetén ez tulajdonképpen ugyanaz a
viselkedés, mintha nem adnánk meg típust. Mire jó tehát? Hogy meg lehessen
különböztetni, mikor hiányzik egyszerűen a típus, és mikor valóban
mixed
.
Függvények és metódusok visszatérési értékénél a típus meg nem
adása eltér a mixed
típus megadásától. Ez tulajdonképpen a
void
ellentéte, mivel azt mondja, hogy a függvénynek valamit
vissza kell adnia. A hiányzó return ekkor fatális hiba.
A gyakorlatban ritkán kellene használni, mert az union típusoknak köszönhetően pontosabban meg lehet határozni az értéket. Tehát kivételes helyzetekben hasznos:
function dump(mixed $var): mixed
{
// változó kiírása
return $var;
}
false
Az új false
pszeudotípust ezzel szemben csak union típusokban
lehet használni. Azért jött létre, hogy natívan le lehessen írni a natív
függvények visszatérési értékének típusát, amelyek történelmileg
sikertelenség esetén false-t adnak vissza:
function strpos(string $haystack, string $needle): int|false
{
}
Ebből az okból nem létezik true
típus, nem használható
önmagában a false
, sem a false|null
, sem a
bool|false
.
static
A static
pszeudotípust csak metódus visszatérési
típusaként lehet használni. Azt mondja, hogy a metódus ugyanolyan típusú
objektumot ad vissza, mint maga az objektum (míg a self
azt
mondja, hogy azt az osztályt adja vissza, amelyben a metódus definiálva van).
Ami kiválóan alkalmas a fluent interface-ek leírására:
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
Ez a típus PHP 8-ban nem létezik, és a jövőben sem kerül bevezetésre.
Az erőforrások (resources) történelmi maradványok abból az időből,
amikor a PHP-nak még nem voltak objektumai. Fokozatosan az erőforrásokat
objektumokra cserélik, és idővel ez a típus teljesen eltűnik. Például a
PHP 8.0 a képet reprezentáló erőforrást a GdImage
objektumra,
a curl
kapcsolat erőforrását pedig a CurlHandle
objektumra cseréli.
Stringable
Ez egy interfész, amelyet automatikusan implementál minden objektum a
mágikus __toString()
metódussal.
class Email
{
public function __toString(): string
{
return $this->value;
}
}
function print(Stringable|string $s)
{
}
print('abc');
print(new Email);
Az osztálydefinícióban explicit módon meg lehet adni a
class Email implements Stringable
-t, de ez nem szükséges.
Ez az elnevezési stílus tükröződik a Nette\Utils\Html
-ben
is, amely a Nette\HtmlStringable
interfészt implementálja a
korábbi IHtmlString
helyett. Az ilyen típusú objektumokat
például a Latte nem escapeli.
Típusvariancia, kontravariancia, kovariancia
A Liskov-féle helyettesítési elv (Liskov Substitution Principle – LSP) kimondja, hogy az osztály leszármazottai és az interfész implementációi soha nem követelhetnek többet és nem nyújthatnak kevesebbet, mint a szülő. Tehát a leszármazott metódusa nem követelhet több argumentumot, vagy nem fogadhat el szűkebb típus tartományt a paramétereknél, mint az ős, és fordítva, nem adhat vissza szélesebb típus tartományt. De adhat vissza szűkebbet. Miért? Mert különben az öröklődés egyáltalán nem működne. A függvény ugyan elfogadna egy bizonyos típusú objektumot, de nem tudná, milyen paramétereket lehet átadni a metódusoknak, és mit fognak valójában visszaadni, mert bármelyik leszármazott ezt felboríthatná.
Tehát az OOP-ban érvényes, hogy a leszármazott:
- a paraméterekben szélesebb típus tartományt fogadhat el (ezt nevezik kontravarianciának)
- szűkebb típus tartományt adhat vissza (kovariancia)
- és a property-k nem változtathatják meg a típust (invariánsak)
A PHP ezt a 7.4-es verziótól tudja, és a PHP 8.0-ban bevezetett összes új típus is támogatja a kontravarianciát és a kovarianciát.
A mixed
esetén a leszármazott szűkítheti a visszatérési
értéket bármilyen típusra, de nem void
-ra, mert az nem az
érték típusa, hanem annak hiánya. A leszármazott sem hagyhatja el a
típust, mert az is megengedi a hiányt.
class A
{
public function foo(mixed $foo): mixed
{}
}
class B extends A
{
public function foo($foo): string
{}
}
Az union típusokat is lehet bővíteni a paraméterekben és szűkíteni a visszatérési értékekben:
class A
{
public function foo(string|int $foo): string|int
{}
}
class B extends A
{
public function foo(string|int|float $foo): string
{}
}
Továbbá a false
a paraméterben bővíthető
bool
-ra, vagy fordítva a bool
a visszatérési
értékben szűkíthető false
-ra.
Minden kovariancia/kontravariancia elleni vétség PHP 8.0-ban fatális hibához vezet.
A következő részekben 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