Atributy přinášejí zcela nový způsob, jak zapisovat strukturovaná
metadata ke třídám a všem jejím členům, taktéž funkcím, closurám a
jejich parametrům. K tomuto účelu se dosud využívaly phpDoc komentáře,
ale jejich syntax byla vždy natolik volná a nejednotná, že nebylo možné je
začít strojově zpracovávat. Proto je nahrazují atributy s pevnou syntaxí
a podporou v reflexních třídách.
Tudíž knihovny, které dosud získávaly metadata parsováním phpDoc
komentářů, je budou moci nahradit za atributy. Příkladem je třeba Nette,
kde v nejnovějších verzích Application a DI už můžete místo anotací
@persistent
, @crossOrigin
a @inject
používat atributy Persistent
, CrossOrigin
a
Inject
.
Kód používající anotace:
/**
* @persistent(comp1, comp2)
*/
class SomePresenter
{
/** @persistent */
public $page = 1;
/** @inject */
public Facade $facade;
/**
* @crossOrigin
*/
public function handleSomething()
{
}
}
Totéž pomocí atributů:
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 vyhodnocuje názvy atributů stejně, jako by se jednalo o třídy, tedy
v kontextu jmenného prostoru a klauzulí use
. Tedy bylo by
možné je zapsat kupříkladu i takto:
use Nette\Application\Attributes;
class SomePresenter
{
#[Attributes\Persistent]
public int $page = 1;
#[\Nette\DI\Attributes\Inject]
public Facade $facade;
Třída představující atribut existovat může i nemusí. Je ale určitě
lepší, pokud existuje, protože pak ji může editor napovídat při psaní,
statický analyzátor rozpozná překlepy atd.
Syntax
Šikovné je, že PHP před verzí 8 vidí atributy jen jako komentáře,
takže je lze používat i v kódu, který má fungovat ve starších
verzích.
Zápis jednotlivého atributu vypadá jako vytváření instance objektu,
pokud bychom vynechali operátor new
. Tedy název třídy za
kterým mohou následovat v závorkách argumenty:
#[Column('string', 32, true, false)]#
protected $username;
A tady lze skvěle uplatnit další horkou novinku PHP 8.0 – pojmenované
argumenty:
#[Column(
type: 'string',
length: 32,
unique: true,
nullable: false,
)]#
protected $username;
Každý element může mít více atributů, které lze zapsat samostatně
nebo oddělené čárkou:
#[Inject]
#[Lazy]
public Foo $foo;
#[Inject, Lazy]
public Bar $bar;
Následující atribut platí pro všechny tři properties:
#[Common]
private $a, $b, $c;
Ve výchozích hodnotách properties lze používat jednoduché výrazy a
konstanty, které lze vyhodnotit během kompilace, a totéž platí i pro
argumenty atributů:
#[
ScalarExpression(1 + 1),
ClassNameAndConstants(PDO::class, PHP_VERSION_ID),
BitShift(4 >> 1, 4 << 1),
BitLogic(1 | 2, JSON_HEX_TAG | JSON_HEX_APOS),
]
Bohužel hodnotou argumentu nemůže být další atribut, tj. nelze atributy
vnořovat. Například následující anotaci používanou v Doctrine nelze
zcela přímočaře přetvořit do atributů:
/**
* @Table(name="products",uniqueConstraints={@UniqueConstraint(columns={"name", "email"})})
*/
Taktéž neexistuje atributová obdoba pro souborový phpDoc, tedy komentář
nacházející se na začátku souboru, který využívá například Nette
Tester.
Zjišťování atributů
Jaké atributy mají jednotlivé elementy zjistíme pomocí reflexe.
Reflexní třídy disponují novou metodou getAttributes()
, která
vrátí pole objektů ReflectionAttribute
.
use MyAttributes\Example;
#[Example('Hello', 123)]
class Foo
{}
$reflection = new ReflectionClass(Foo::class);
foreach ($reflection->getAttributes() as $attribute) {
$attribute->getName(); // plný název atributu, např. MyAttributes\Example
$attribute->getArguments(); // ['Hello', 123]
$attribute->newInstance(); // vrací instanci new MyAttributes\Example('Hello', 123)
}
Vrácené atributy lze filtrovat parametrem, např.
$reflection->getAttributes(Example::class)
vrátí jen atributy
Example
.
Třídy atributů
Třída atributu MyAttributes\Example
nemusí existovat.
Existenci vyžaduje pouze volání metody newInstance()
protože
vytváří její instanci. Pojďme ji tedy napsat. Půjde o úplně běžnou
třídu, jen u ní musíme uvést atribut Attribute
(tj.
z globálního systémového namespace):
namespace MyAttributes;
use Attribute;
#[Attribute]
class Example
{
public function __construct(string $message, int $number)
{
...
}
}
Lze omezit, u jakých jazykových elementů bude legální atribut použít.
Takto třeba jen u tříd a properties:
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
class Example
{
...
}
K dispozici jsou flagy TARGET_CLASS
,
TARGET_FUNCTION
, TARGET_METHOD
,
TARGET_PROPERTY
, TARGET_CLASS_CONSTANT
,
TARGET_PARAMETER
a výchozí TARGET_ALL
.
Ale pozor, k ověření správného či nesprávného použití dochází
překvapivě až při zavolání metody newInstance()
. Samotný
kompilátor tuto kontrolu neprovádí.
Budoucnost s atributy
Díky atributům a novým typům se
z PHP dokumentačních komentářů poprvé v jejich historii stanou opravdu
jen dokumentující komentáře. Už teď přichází PhpStorm s vlastními
atributy, které mohou nahradit třeba anotaci @deprecated
.
A dá se předpokládat, že tento atribut bude jednou standardně v PHP.
Podobně se nahradí i další anotace, jako @throws
atd.
Ačkoliv Nette používá anotace od své úplně první verze pro označení
persistentních parametrů a komponent, k jejich masivnějšímu využití
nedošlo proto, že nešlo o nativní jazykový konstrukt, takže editory je
nenapovídaly a bylo snadné v nich udělat chybu. Tohle sice dnes už řeší
pluginy do editorů, ale opravdu nativní cesta, jakou přinášejí atributy,
otevírá zcela nové možnosti.
Mimochodem, atributy získaly výjimku v Nette Coding Standard, který
požaduje, aby název třídy kromě specifičnosti (např.
Product
, InvalidValue
) obsahoval i obecnost (tedy
ProductPresenter
, InvalidValueException
). Jinak by
nebylo při užití v kódu zřejmé, co přesně třída představuje.
U atributů tohle naopak není žádoucí, tedy třída se jmenuje
Inject
místo InjectAttribute
.
V posledním díle se podíváme, jaké se v PHP objevily nové funkce
a třídy a představíme si Just in Time Compiler.
Komentáře (RSS)
Děkujeme. Je dobré, že to tak sepisuješ.
Fakt super práce!
Super, luxusní, díky!
Chcete-li odeslat komentář, přihlaste se