PHP 8.0: Atributi (3/4)
Različica PHP 8.0 je pravkar izšla. Polna je novosti kot še nobena druga različica. Njihova predstavitev si zasluži štiri ločene članke. V tretjem delu si bomo ogledali atribute.
Atributi omogočajo povsem nov način zapisovanja strukturiranih metapodatkov za razrede in vse njihove člane ter funkcije, zapore in njihove parametre. Doslej smo v ta namen uporabljali komentarje PhpDoc, vendar je bila njihova sintaksa vedno tako ohlapna in nedosledna, da jih ni bilo mogoče začeti strojno obdelovati. Zato jih nadomeščajo atributi z določeno sintakso in podporo v razredih za razmislek.
Zaradi tega bodo knjižnice, ki so doslej metapodatke pridobivale
z razčlenjevanjem komentarjev phpDoc, te lahko nadomestile z atributi. Primer
je Nette, kjer lahko v najnovejših različicah aplikacij in DI namesto opomb
@persistent
, @crossOrigin
in @inject
že
uporabljate atribute Persistent
, CrossOrigin
in
Inject
.
Koda z uporabo opomb:
/**
* @persistent(comp1, comp2)
*/
class SomePresenter
{
/** @persistent */
public $page = 1;
/** @inject */
public Facade $facade;
/**
* @crossOrigin
*/
public function handleSomething()
{
}
}
Enako velja za atribute:
use Nette\Application\Attributes\CrossOrigin;
use Nette\Application\Attributes\Persistent;
use Nette\DI\Attributes\Inject;
#[Persistent('comp1', 'comp2')]
class SomePresenter
{
#[Persistent]
public int $page = 1;
#[Inject]
public Facade $facade;
#[CrossOrigin]
public function handleSomething()
{
}
}
PHP vrednoti imena atributov enako, kot če bi šlo za razrede, v okviru
imenskih prostorov in stavkov use
. Tako bi jih bilo mogoče
zapisati na primer takole:
use Nette\Application\Attributes;
class SomePresenter
{
#[Attributes\Persistent]
public int $page = 1;
#[\Nette\DI\Attributes\Inject]
public Facade $facade;
Razred, ki predstavlja atribut, lahko obstaja ali pa tudi ne. Vsekakor pa je bolje, če obstaja, saj ga lahko urednik predlaga med pisanjem, statični analizator prepozna tipkarske napake itd.
Sintaksa
Pametno je, da PHP pred različico 8 atribute vidi le kot komentarje, zato jih je mogoče uporabiti tudi v kodi, ki bi morala delovati v starejših različicah.
Sintaksa posameznega atributa je videti kot ustvarjanje instance predmeta,
če izpustimo operator new
. Torej ime razreda, ki mu sledijo
neobvezni argumenti v oklepajih:
#[Column('string', 32, true, false)]#
protected $username;
In tu je mesto, kjer lahko uporabimo novo vročo funkcijo PHP 8.0 – poimenovane argumente:
#[Column(
type: 'string',
length: 32,
unique: true,
nullable: false,
)]#
protected $username;
Vsak element ima lahko več atributov, ki jih lahko zapišemo posamezno ali pa jih ločimo z vejico:
#[Inject]
#[Lazy]
public Foo $foo;
#[Inject, Lazy]
public Bar $bar;
Naslednji atribut velja za vse tri lastnosti:
#[Common]
private $a, $b, $c;
Enostavni izrazi in konstante, ki se lahko ovrednotijo med sestavljanjem in se uporabljajo kot privzete vrednosti lastnosti, se lahko uporabijo kot argumenti v atributih:
#[
ScalarExpression(1 + 1),
ClassNameAndConstants(PDO::class, PHP_VERSION_ID),
BitShift(4 >> 1, 4 << 1),
BitLogic(1 | 2, JSON_HEX_TAG | JSON_HEX_APOS),
]
Vrednost argumenta žal ne more biti drug atribut, tj. atributov ni mogoče gnezditi. Na primer, ni preprostega načina, da bi naslednjo opombo, ki se uporablja v programu Doctrine, pretvorili v atribute:
/**
* @Table(name="products",uniqueConstraints={@UniqueConstraint(columns={"name", "email"})})
*/
Prav tako ni ekvivalenta atributa za datoteko phpDoc, tj. komentar, ki se nahaja na začetku datoteke in ga uporablja na primer Nette Tester.
Odraz atributa
Katere atribute imajo posamezni elementi, lahko določite z uporabo
refleksije. Razredi za odboj imajo novo metodo getAttributes()
, ki
vrne polje predmetov ReflectionAttribute
.
use MyAttributes\Example;
#[Example('Hello', 123)]
class Foo
{}
$reflection = new ReflectionClass(Foo::class);
foreach ($reflection->getAttributes() as $attribute) {
$attribute->getName(); // full attribute name, e.g. MyAttributes\Example
$attribute->getArguments(); // ['Hello', 123]
$attribute->newInstance(); // returns an instance: new MyAttributes\Example('Hello', 123)
}
Vrnjene atribute lahko filtriramo s parametrom, npr.
$reflection->getAttributes(Example::class)
vrne samo atribute
Example
.
Razredi atributov
Razred atributov MyAttributes\Example
morda ne obstaja. Le klic
metode newInstance()
zahteva njegov obstoj, saj ga instancira. Zato
ga zapišimo. To bo povsem običajen razred, le da moramo zagotoviti atribut
Attribute
(tj. iz globalnega sistemskega imenskega prostora):
namespace MyAttributes;
use Attribute;
#[Attribute]
class Example
{
public function __construct(string $message, int $number)
{
...
}
}
Lahko omejimo, za katere jezikovne elemente bo uporaba atributa dovoljena. Na primer samo za razrede in lastnosti:
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
class Example
{
...
}
TARGET_CLASS
, TARGET_FUNCTION
,
TARGET_METHOD
, TARGET_PROPERTY
,
TARGET_CLASS_CONSTANT
, TARGET_PARAMETER
in privzeto
TARGET_ALL
.
Toda pozor, preverjanje pravilne ali nepravilne uporabe se presenetljivo
zgodi le ob klicu metode newInstance()
. Sam prevajalnik tega
preverjanja ne opravi.
Prihodnost z atributi
Zaradi atributov in novih tipov
bodo komentarji dokumentacije PHP prvič v svoji zgodovini resnično postali le
dokumentarni komentarji. PhpStorm že ima na voljo atribute
po meri, ki lahko nadomestijo na primer anotacijo @deprecated
.
In domnevamo lahko, da bo ta atribut nekega dne v PHP privzeto uporabljen.
Podobno bodo nadomeščene tudi druge opombe, kot je
@throws
itd.
Čeprav Nette že od svoje prve različice uporablja anotacije za označevanje trajnih parametrov in komponent, se te niso uporabljale množičneje, ker niso bile izvorni konstrukt jezika, zato jih uredniki niso predlagali in se je bilo enostavno zmotiti. Čeprav se tega že lotevajo vtičniki za urejevalnike, pa resnično domač način, ki ga prinašajo atributi, odpira povsem nove možnosti.
Mimogrede, atributi so dobili izjemo v kodirnem standardu Nette, ki zahteva,
da ime razreda poleg specifičnosti (npr. Product
,
InvalidValue
) vsebuje tudi splošnost (npr.
ProductPresenter
, InvalidValueException
).
V nasprotnem primeru pri uporabi v kodi ne bi bilo jasno, kaj točno razred
predstavlja. Pri atributih to ni zaželeno, zato se razred imenuje
Inject
namesto InjectAttribute
.
V zadnjem delu si bomo ogledali, katere nove funkcije in razredi so se pojavili v jeziku PHP, in predstavili prevajalnik Just in Time Compiler.
Če želite oddati komentar, se prijavite