PHP 8.0: Podatkovni tipi (2/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 drugem si bomo pogledali podatkovne tipe.

Vrnimo se v zgodovino. Bistven preboj PHP 7 je bila uvedba skalarnih type
hintov. Skoraj se to ni zgodilo. Avtorico izjemne rešitve Andreo Faulds, ki je bila zahvaljujoč
declare(strict_types=1)
popolnoma povratno združljiva in izbirna,
je skupnost grdo zavrnila. Na srečo sta se nje in njenega predloga takrat
zavzela Anthony Ferrara, sprožila
kampanjo in RFC je zelo tesno šel skozi. Ufff. Večino novosti v PHP 8 ima na
vesti legendarni Nikita Popov in pri
glasovanju so mu šle skozi kot po maslu. Svet se spreminja na bolje.
PHP 8 pripelje tipe do popolnosti. Izginila bo velika večina phpDoc
anotacij @param
, @return
in @var
v kodi
in jih bodo nadomestili nativni zapis in predvsem kontrola s strani PHP
motorja. V komentarjih bodo ostali le opisi struktur, kot je
string[]
, ali bolj zapletene anotacije za PHPStan.
Union Types
Union tipi so naštevanje dveh ali več tipov, ki jih lahko vrednost prevzame:
class Button
{
private string|object $caption;
public function setCaption(string|object $caption)
{
$this->caption = $caption;
}
}
Nekatere posebne union tipe je PHP poznal že prej. Na primer nullable tipe
kot ?string
, kar je ekvivalent union tipa string|null
in vprašajni zapis lahko štejemo le za okrajšavo. Seveda deluje tudi v PHP
8, vendar ga ni mogoče kombinirati z navpičnicami, torej namesto
?string|object
je treba pisati polno
string|object|null
. Nadalje je iterable
bil vedno
ekvivalent array|Traversable
. Morda vas bo presenetilo, da je union
tip pravzaprav tudi float
, ki dejansko sprejema
int|float
, vendar pretipizira na float
.
V unionih ni mogoče uporabljati pseudotipov void
in
mixed
, ker to ne bi imelo nobenega smisla.
Nette je pripravljen za tipe zvez. V shemi Expect::from()
jih sprejema, prav tako jih sprejemajo predstavniki. Uporabljate jih lahko na
primer v metodah upodabljanja in akcije:
public function renderDetail(int|array $id)
{
...
}
Nasprotno pa autowiring v Nette DI union tipe zavrača. Manjka zaenkrat use case, kjer bi bilo smiselno, da bi na primer konstruktor sprejemal bodisi ta ali oni objekt. Seveda, ko se pojavi, bo mogoče ustrezno prilagoditi obnašanje vsebnika.
Metode getParameterType()
, getReturnType()
in
getPropertyType()
v Nette\Utils\Reflection vržejo v primeru
union tipa izjemo (v različici 3.1, v starejši 3.0 vračajo zaradi
združljivosti null).
mixed
Pseudotip mixed
pravi, da je vrednost lahko popolnoma
karkoli.
V primeru parametrov in lastnosti gre pravzaprav za enako obnašanje, kot
če nobenega tipa ne navedemo. Za kaj je torej dober? Da bi se dalo razlikovati,
kdaj tip preprosto manjka in kdaj je resnično mixed
.
V primeru povratne vrednosti funkcije in metode se nenavedba tipa od navedbe
tipa mixed
razlikuje. Gre pravzaprav za nasprotje
void
, saj pravi, da mora funkcija nekaj vrniti. Manjkajoči return
je potem fatalna napaka.
V praksi bi ga morali uživati redko, saj lahko zahvaljujoč union tipom vrednost natančneje specificirate. Primeren je torej v izjemnih situacijah:
function dump(mixed $var): mixed
{
// izpiši spremenljivko
return $var;
}
false
Novi pseudotip false
lahko nasprotno uporabljamo le v union
tipih. Nastala je iz potrebe po nativnem opisu tipa povratne vrednosti pri
nativnih funkcijah, ki zgodovinsko v primeru neuspeha vračajo false:
function strpos(string $haystack, string $needle): int|false
{
}
Zato ne obstaja tip true
, ni mogoče uporabljati niti samega
false
ali false|null
ali bool|false
.
static
Pseudotip static
lahko uporabimo le kot povratni tip metode.
Pravi, da metoda vrača objekt istega tipa, kot je objekt sam (medtem ko
self
pravi, da vrača razred, v katerem je metoda definirana). Kar
se odlično obnese za opis fluent interfaces:
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
Ta tip v PHP 8 ne obstaja in tudi v prihodnosti ne bo uveden. Resources so
zgodovinski ostanek iz časov, ko PHP še ni imel objektov. Postopoma se bodo
resources nadomeščali z objekti in sčasoma bo ta tip popolnoma izginil. Na
primer PHP 8.0 nadomešča resource, ki predstavlja sliko, z objektom
GdImage
in resource povezave curl
z objektom
CurlHandle
.
Stringable
Gre za vmesnik, ki ga samodejno implementira vsak objekt z magično metodo
__toString()
.
class Email
{
public function __toString(): string
{
return $this->value;
}
}
function print(Stringable|string $s)
{
}
print('abc');
print(new Email);
V definiciji razreda je mogoče eksplicitno navesti
class Email implements Stringable
, vendar to ni potrebno.
Ta slog poimenovanja odraža tudi Nette\Utils\Html
, ki
implementira vmesnik Nette\HtmlStringable
namesto prejšnjega
IHtmlString
. Objekte tega tipa potem npr. Latte ne escapuje.
Type variance, contravariance, covariance
Liskovin princip zamenljivosti (Liskov Substitution Principle – LSP) pravi, da potomci razreda in implementacije vmesnika nikoli ne smejo zahtevati več in zagotavljati manj kot starš. Torej, da metoda potomca ne sme zahtevati več argumentov ali sprejemati pri parametrih ožjega razpona tipov kot prednik in nasprotno ne sme vračati širšega razpona tipov. Lahko pa vrača ožjega. Zakaj? Ker sicer dedovanje sploh ne bi delovalo. Funkcija bi sicer sprejela objekt določenega tipa, vendar ne bi vedela, katere parametre lahko metodam posreduje in kaj bodo dejansko vračale, saj bi katerikoli potomec to lahko porušil.
Torej v OOP velja, da potomec lahko:
- v parametrih sprejema širši razpon tipov (temu se reče kontravarianca)
- vrača ožji razpon tipov (kovarianca)
- in lastnosti ne morejo spreminjati tipa (so invariantne)
PHP to zna od različice 7.4 in vsi novo uvedeni tipi v PHP 8.0 prav tako podpirajo kontravarianco in kovarianco.
V primeru mixed
lahko potomec zoži povratno vrednost na
katerikoli tip, nikakor pa ne void
, ker ne gre za tip vrednosti,
ampak za njeno odsotnost. Niti potomec ne more ne navesti tipa, ker to prav tako
dopušča odsotnost.
class A
{
public function foo(mixed $foo): mixed
{}
}
class B extends A
{
public function foo($foo): string
{}
}
Tudi union tipe je mogoče v parametrih razširjati in v povratnih vrednostih ožiti:
class A
{
public function foo(string|int $foo): string|int
{}
}
class B extends A
{
public function foo(string|int|float $foo): string
{}
}
Nadalje false
lahko v parametru razširimo na bool
ali nasprotno bool
v povratni vrednosti zožimo na
false
.
Vse kršitve kovariance/kontravariance vodijo v PHP 8.0 do fatalne napake.
V prihodnjih delih si bomo pokazali, 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