PHP 8.0: Attribútumok (3/4)
A PHP 8.0-ás verziója most jelenik meg. Tele van olyan újdonságokkal, mint egyetlen korábbi verzió sem. Bevezetésük négy külön cikket érdemelt volna. A harmadik részben az attribútumokat vesszük szemügyre.
Az attribútumok egy teljesen új módot biztosítanak az osztályok és minden tagjuk, valamint a függvények, zárlatok és paramétereik strukturált metaadatainak megírására. Erre a célra eddig is használták a PhpDoc kommenteket, de ezek szintaxisa mindig is olyan laza és következetlen volt, hogy nem lehetett gépi feldolgozásukba kezdeni. Ezért ezeket a reflection osztályokban meghatározott szintaxissal és támogatással rendelkező attribútumokkal váltják fel.
Emiatt azok a könyvtárak, amelyek korábban a phpDoc kommentek elemzése
révén szereztek metaadatokat, képesek lesznek ezeket attribútumokkal
helyettesíteni. Erre példa a Nette, ahol az Application és a DI legújabb
verzióiban már a Persistent
, CrossOrigin
és
Inject
attribútumokat használhatjuk a @persistent
,
@crossOrigin
és @inject
megjegyzések helyett.
Az annotációkat használó kód:
/**
* @persistent(comp1, comp2)
*/
class SomePresenter
{
/** @persistent */
public $page = 1;
/** @inject */
public Facade $facade;
/**
* @crossOrigin
*/
public function handleSomething()
{
}
}
Ugyanez vonatkozik az attribútumokra is:
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()
{
}
}
A PHP ugyanúgy értékeli az attribútumneveket, mintha azok osztályok
lennének, a névterek és a use
záradékok kontextusában. Így
akár az is lehetséges lenne, hogy például a következőképpen írjuk
le őket:
use Nette\Application\Attributes;
class SomePresenter
{
#[Attributes\Persistent]
public int $page = 1;
#[\Nette\DI\Attributes\Inject]
public Facade $facade;
Az attribútumot reprezentáló osztály létezhet vagy nem létezhet. De mindenképpen jobb, ha létezik, mert akkor a szerkesztő az írás során javasolhatja, a statikus elemző felismeri a gépelési hibákat stb.
Szintaxis
Okos dolog, hogy a 8-as verzió előtti PHP az attribútumokat csak megjegyzésként látja, így olyan kódban is használhatók, amelynek a régebbi verziókban is működnie kell.
Az egyedi attribútum szintaxisa úgy néz ki, mintha egy objektumpéldányt
hoznánk létre, ha kihagyjuk a new
operátort. Tehát az osztály
neve, amelyet zárójelben opcionális argumentumok követnek:
#[Column('string', 32, true, false)]#
protected $username;
És itt van az a hely, ahol a PHP 8.0 új, forró funkcióját, a named arguments használhatjuk:
#[Column(
type: 'string',
length: 32,
unique: true,
nullable: false,
)]#
protected $username;
Minden elemnek több attribútuma is lehet, amelyeket külön-külön vagy vesszővel elválasztva lehet leírni:
#[Inject]
#[Lazy]
public Foo $foo;
#[Inject, Lazy]
public Bar $bar;
A következő attribútum mindhárom tulajdonságra vonatkozik:
#[Common]
private $a, $b, $c;
A fordítás során kiértékelhető és a tulajdonságok alapértelmezett értékeiként használt egyszerű kifejezések és konstansok használhatók az attribútumok argumentumaként:
#[
ScalarExpression(1 + 1),
ClassNameAndConstants(PDO::class, PHP_VERSION_ID),
BitShift(4 >> 1, 4 << 1),
BitLogic(1 | 2, JSON_HEX_TAG | JSON_HEX_APOS),
]
Sajnos egy argumentum értéke nem lehet egy másik attribútum, azaz az attribútumok nem lehetnek egymásba ágyazva. Például a Doctrine-ban használt következő annotáció nem alakítható át egyszerűen attribútumokká:
/**
* @Table(name="products",uniqueConstraints={@UniqueConstraint(columns={"name", "email"})})
*/
Szintén nincs attribútum megfelelője a phpDoc fájlnak, azaz a fájl elején található megjegyzésnek, amelyet például a Nette Tester használ.
Attribútum tükrözés
Az, hogy az egyes elemek milyen attribútumokkal rendelkeznek, a tükrözés
segítségével határozható meg. A reflexiós osztályok rendelkeznek egy új
getAttributes()
metódussal, amely egy objektumtömböt ad vissza
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)
}
A visszaadott attribútumok egy paraméterrel szűrhetők, pl. a
$reflection->getAttributes(Example::class)
csak az
attribútumokat adja vissza Example
.
Attribútum osztályok
A MyAttributes\Example
attribútumosztály nem létezhet. Csak
egy metódushívás newInstance()
megköveteli a létezését, mert
instanciálja. Írjuk tehát meg. Ez egy teljesen hétköznapi osztály lesz,
csak az attribútumot Attribute
(azaz a globális rendszer
névteréből) kell megadnunk:
namespace MyAttributes;
use Attribute;
#[Attribute]
class Example
{
public function __construct(string $message, int $number)
{
...
}
}
Korlátozható, hogy mely nyelvi elemeknél lesz engedélyezett az attribútum használata. Például csak az osztályok és tulajdonságok esetében:
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
class Example
{
...
}
A TARGET_CLASS
, TARGET_FUNCTION
,
TARGET_METHOD
, TARGET_PROPERTY
,
TARGET_CLASS_CONSTANT
, TARGET_PARAMETER
és az
alapértelmezett TARGET_ALL
zászlók állnak rendelkezésre.
De vigyázat, a helyes vagy helytelen használat ellenőrzése meglepő
módon csak a newInstance()
metódus meghívásakor történik.
Maga a fordító nem végzi el ezt az ellenőrzést.
A jövő az attribútumokkal
Az attribútumoknak és az új
típusoknak köszönhetően a PHP dokumentációs megjegyzések
történelmük során először valóban csak dokumentációs megjegyzésekké
válnak. A PhpStorm már most is rendelkezik egyéni
attribútumokkal, amelyek helyettesíthetik például az annotációkat
@deprecated
. És feltételezhető, hogy ez az attribútum egy nap
alapértelmezetten is benne lesz a PHP-ban. Hasonlóképpen más annotációk is
helyettesíthetők lesznek, mint például a @throws
stb.
Bár a Nette már az első verziója óta használ annotációkat a perzisztens paraméterek és komponensek jelölésére, nem használták tömegesebben, mert nem volt anyanyelvi konstrukció, így a szerkesztők nem javasolták őket, és könnyű volt hibázni. Bár ezt már kezelik a szerkesztő pluginok, a valóban natív mód, amit az attribútumok hoznak, teljesen új lehetőségeket nyit meg.
Az attribútumok egyébként kivételt nyertek a Nette kódolási
szabványban, amely megköveteli, hogy az osztálynév a specifikusság mellett
(pl. Product
, InvalidValue
) tartalmazzon egy
általánosságot is (pl. ProductPresenter
,
InvalidValueException
). Ellenkező esetben a kódban való
használat során nem lenne egyértelmű, hogy az osztály pontosan mit
képvisel. Az attribútumok esetében ez nem kívánatos, ezért az osztály
neve InjectAttribute
helyett Inject
.
*Az utolsó részben megnézzük, milyen új függvények és osztályok jelentek meg a PHP-ben, és bemutatjuk a Just in Time Compilert.
A hozzászólás elküldéséhez kérjük, jelentkezzen be