PHP 8.0: Atribute (3/4)
Versiunea 8.0 a PHP este în curs de lansare chiar acum. Este plină de noutăți ca nicio altă versiune de până acum. Introducerea lor a meritat patru articole separate. În cea de-a treia parte vom arunca o privire asupra atributelor.
Atributele oferă o modalitate complet nouă de a scrie metadate structurate pentru clase și toți membrii acestora, precum și pentru funcții, închideri și parametrii acestora. Comentariile PhpDoc au fost folosite până acum în acest scop, dar sintaxa lor a fost întotdeauna atât de vagă și inconsecventă încât nu a fost posibil să se înceapă procesarea automată a acestora. Prin urmare, acestea sunt înlocuite de atribute cu o sintaxă determinată și cu suport în clasele de reflecție.
Din acest motiv, bibliotecile care au recuperat anterior metadatele prin
analiza comentariilor phpDoc vor putea să le înlocuiască cu atribute. Un
exemplu este Nette, unde în cele mai recente versiuni ale aplicației și DI se
pot utiliza deja atributele Persistent
, CrossOrigin
și Inject
în locul adnotărilor @persistent
,
@crossOrigin
și @inject
.
Codul care utilizează adnotări:
/**
* @persistent(comp1, comp2)
*/
class SomePresenter
{
/** @persistent */
public $page = 1;
/** @inject */
public Facade $facade;
/**
* @crossOrigin
*/
public function handleSomething()
{
}
}
Același lucru cu atributele:
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 evaluează numele atributelor la fel ca și cum ar fi clase, în
contextul spațiilor de nume și al clauzelor use
. Astfel, ar fi
posibil chiar să le scriem, de exemplu, după cum urmează:
use Nette\Application\Attributes;
class SomePresenter
{
#[Attributes\Persistent]
public int $page = 1;
#[\Nette\DI\Attributes\Inject]
public Facade $facade;
Clasa care reprezintă atributul poate exista sau nu. Dar cu siguranță este mai bine dacă există, pentru că astfel editorul o poate sugera în timpul scrierii, analizorul static recunoaște greșelile de scriere etc.
Sintaxa
Este inteligent faptul că PHP înainte de versiunea 8 vede atributele doar ca fiind comentarii, astfel încât acestea pot fi utilizate și în codul care ar trebui să funcționeze în versiuni mai vechi.
Sintaxa unui atribut individual arată ca și cum am crea o instanță de
obiect dacă omitem operatorul new
. Așadar, numele clasei urmat de
argumente opționale în paranteze:
#[Column('string', 32, true, false)]#
protected $username;
Și aici este locul în care poate fi folosită noua caracteristică fierbinte a PHP 8.0 – argumente numite:
#[Column(
type: 'string',
length: 32,
unique: true,
nullable: false,
)]#
protected $username;
Fiecare element poate avea mai multe atribute care pot fi scrise individual sau separate prin virgulă:
#[Inject]
#[Lazy]
public Foo $foo;
#[Inject, Lazy]
public Bar $bar;
Următorul atribut se aplică tuturor celor trei proprietăți:
#[Common]
private $a, $b, $c;
Expresiile și constantele simple, care pot fi evaluate în timpul compilării și care sunt utilizate ca valori implicite pentru proprietăți, pot fi utilizate ca argumente în atribute:
#[
ScalarExpression(1 + 1),
ClassNameAndConstants(PDO::class, PHP_VERSION_ID),
BitShift(4 >> 1, 4 << 1),
BitLogic(1 | 2, JSON_HEX_TAG | JSON_HEX_APOS),
]
Din păcate, valoarea unui argument nu poate fi un alt atribut, adică atributele nu pot fi imbricate. De exemplu, nu există o modalitate directă de a converti în atribute următoarea adnotare utilizată în Doctrine:
/**
* @Table(name="products",uniqueConstraints={@UniqueConstraint(columns={"name", "email"})})
*/
De asemenea, nu există un atribut echivalent pentru fișierul phpDoc, adică un comentariu situat la începutul unui fișier, care este utilizat, de exemplu, de Nette Tester.
Reflectarea atributelor
Atributele pe care le au elementele individuale pot fi determinate prin
utilizarea reflecției. Clasele de reflecție au o nouă metodă
getAttributes()
, care returnează un tablou de obiecte
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)
}
Atributele returnate pot fi filtrate printr-un parametru, de exemplu,
$reflection->getAttributes(Example::class)
returnează numai
atributele Example
.
Clase de atribute
Este posibil ca clasa de atribute MyAttributes\Example
să nu
existe. Doar un apel la o metodă newInstance()
necesită
existența acesteia, deoarece o instanțiază. Să o scriem deci. Va fi
o clasă complet obișnuită, doar că trebuie să furnizăm atributul
Attribute
(adică din spațiul de nume al sistemului global):
namespace MyAttributes;
use Attribute;
#[Attribute]
class Example
{
public function __construct(string $message, int $number)
{
...
}
}
Se poate restricționa pentru care elemente de limbă va fi permisă utilizarea atributului. De exemplu, doar pentru clase și proprietăți:
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
class Example
{
...
}
Sunt disponibile stegulețele TARGET_CLASS
,
TARGET_FUNCTION
, TARGET_METHOD
,
TARGET_PROPERTY
, TARGET_CLASS_CONSTANT
,
TARGET_PARAMETER
și implicit TARGET_ALL
.
Atenție însă, verificarea utilizării corecte sau incorecte are loc în
mod surprinzător numai atunci când este apelată metoda
newInstance()
. Compilatorul în sine nu efectuează această
verificare.
Viitorul cu atribute
Datorită atributelor și noilor
tipuri, comentariile din documentația PHP vor deveni, pentru prima dată
în istoria lor, doar comentarii de documentare. PhpStorm vine deja cu atribute
personalizate, care pot înlocui, de exemplu, adnotările
@deprecated
. Și se poate presupune că acest atribut va fi
într-o zi în PHP în mod implicit. În mod similar, vor fi înlocuite și
alte adnotări, cum ar fi @throws
etc.
Deși Nette a folosit adnotări încă de la prima sa versiune pentru a indica parametrii și componentele persistente, acestea nu au fost folosite mai masiv deoarece nu erau o construcție nativă a limbajului, astfel încât editorii nu le sugerau și era ușor de făcut o greșeală. Chiar dacă acest aspect este deja abordat de plugin-urile editorilor, modul cu adevărat nativ, adus de atribute, deschide posibilități complet noi.
Apropo, atributele au obținut o excepție în standardul de codificare
Nette, care cere ca numele clasei, pe lângă specificitate (de exemplu,
Product
, InvalidValue
), să conțină și
o generalitate (de exemplu, ProductPresenter
,
InvalidValueException
). În caz contrar, atunci când este utilizat
în cod, nu ar fi clar ce anume reprezintă clasa. Pentru atribute, acest lucru
nu este de dorit, astfel încât clasa se numește Inject
în loc
de InjectAttribute
.
*În ultima parte, vom analiza ce funcții și clase noi au apărut în PHP și vom prezenta Compilatorul Just in Time.
Pentru a trimite un comentariu, vă rugăm să vă conectați