> LOADING ARTICLE...
17 Dec 2024 Desenvolvimento

Atributos PHP 8.3. Como funcionam e a sua Utilização no Mundo Real

Atributos PHP 8.3. Como funcionam e a sua Utilização no Mundo Real

atributos php

Entre as muitas novas funcionalidades os atributos PHP foram uma das mais promissoras que encontrei no PHP 8. Os Atributos no PHP 8 foram revistos diversas vezes antes de se estabelecerem na implementação atual, a qual iremos aprofundar um pouco neste artigo.

O que são Atributos PHP?


Essencialmente, Atributos são tipos especiais de classes que podem ser usados para adicionar metadados a outras classes, propriedades, funções, métodos, parâmetros e constantes.

Antes do PHP 8, era prática comum usar “doc-comments” ou “anotações” para adicionar metadados a essas entidades. Por exemplo, aqui está como pode definir um DocBlock de método com metadados que indicam o tipo dos argumentos e o tipo de retorno.

/**
 * Descrição do método aqui.
 *
 * @param Random         $mathRandom
 * @param StdlibDateTime $dateTime
 * @param int            $number
 *
 * @return int
 */
private function doSomething(
    Random $mathRandom, 
    StdlibDateTime $dateTime, 
    int $number
) : int {

}

Estas anotações de comentários são apenas strings e são usados para manter informações estruturadas, e algumas bibliotecas usam as anotações para gerar documentação completa analisando-os, como é o caso do Package L5-Swagger da Darkonline. Além disso, IDEs como o PhpStorm utilizam esses DocBlocks para mostrar diagnósticos de forma inteligente e mostrar avisos e erros em tempo de execução no código, se houver algum.

A maneira de fazer tudo isso nativamente estava ausente, e portanto, os Atributos foram introduzidos no PHP 8.0 para fornecer a capacidade de definir metadados nativamente.

Vamos verificar como você pode definir atributos.

Como Definir Atributos PHP?


Na sua forma mais simples, um atributo PHP pode ser aplicado usando a sintaxe #[attr].

🎯 Curiosidade: A sintaxe de atributos do PHP é inspirada na sintaxe de Atributos do Rust.

Então, se quiser aplicar um atributo a uma classe, por exemplo, pode fazer assim.

#[Attribute]
final class TestClass {

}

Os atributos também podem ser aplicados a várias outras coisas, como propriedades, funções, métodos, parâmetros e constantes, assim.

#[ExampleAttribute]
class Foo
{
    #[ExampleAttribute]
    public const FOO = 'foo';
 
    #[ExampleAttribute]
    public $x;
 
    #[ExampleAttribute]
    public function foo(#[ExampleAttribute] $bar) { }
}
 
$object = new #[ExampleAttribute] class () { };
 
#[ExampleAttribute]
function f1() { }
 
$f2 = #[ExampleAttribute] function () { };
 
$f3 = #[ExampleAttribute] fn () => 1;

Uso Prático
Agora, vamos dar uma olhada em como você pode usar Atributos para recuperar metadados quando aplicados a uma classe. Para isso, você precisará criar um atributo personalizado assim.

Como pode ver, a classe TestAttribute é apenas uma classe regular à qual aplicamos um atributo global usando #[\Attribute]. Para receber metadados, podemos definir um construtor que será usado para recuperar os metadados posteriormente.

#[\Attribute]
class TestAttribute
{
    public function __construct(public string $testArgument)
    {}
}

Uma vez criado, podemos aplicar este atributo personalizado a outra classe assim.

#[TestAttribute('Olá Mundo')]
class MinhaClasse
{

}

Como pode ver, podemos aplicar o atributo personalizado da mesma forma que o atributo global que aplicamos anteriormente. E como pode perceber, também passamos o argumento que servirá como metadado para esta classe.

Agora é hora de recuperar os metadados desta classe. E como faremos? É relativamente simples.

Tudo que você precisa fazer é usar a ReflectionClass incorporada para obter os detalhes sobre MinhaClasse assim.

$reflection = new \ReflectionClass(MinhaClasse::class);
$classAttributes = $reflection->getAttributes();

print_r($classAttributes[0]->newInstance()->testArgument);

Vamos desmembrar isso. Como pode ver, primeiro obtivemos a instância de ReflectionClass passando MinhaClasse como argumento.

Feito isso, podemos obter todos os atributos aplicados a esta classe chamando o método getAttributes.

Isso retornará um array de todos os atributos aplicados à classe. Então, se você imprimir $classAttributes, parecerá assim.

array:1 [▼
  0 => ReflectionAttribute {#1829}
]

Em seguida, para obter o atributo da classe, chamamos o método newInstance() no atributo que retornará a instância do TestAttribute e a partir daí, pode recuperar os metadados chamando o argumento assim.

$classAttributes[0]->newInstance()->testArgument // "Olá Mundo"

Âmbitos de Atributos


Além de aplicar atributos a diferentes entidades em PHP, também é possível definir o âmbito onde um atributo em particular deve ser utilizado.

Para isso, é necessário passar sinalizadores especiais para o Atributo, que incluem o seguinte: Attribute::TARGET_METHOD, Attribute::TARGET_CLASS, Attribute::TARGET_FUNCTION, Attribute::TARGET_PROPERTY, Attribute::TARGET_CLASS_CONSTANT, Attribute::TARGET_PARAMETER e Attribute::TARGET_ALL.

Portanto, se eu quiser limitar um atributo personalizado a ser aplicado apenas a classes PHP, eu faria da seguinte forma.

#[\Attribute(Attribute::TARGET_CLASS)]
class TestAttribute
{
    public function __construct(public string $testArgument)
    {}
}

Agora, se eu tentar aplicar este atributo a um método de classe e obter os metadados por meio de reflexão, como abaixo…

class MyClass
{
    #[TestAttribute('Olá Mundo')]
    public function index()
    {

    }
}

$reflection = new \ReflectionMethod(MyClass::class, 'index');
$methodAttributes = $reflection->getAttributes();

print_r($methodAttributes[0]->newInstance()->testArgument);

… o PHP lançará um erro fatal algo como o abaixo.

Error Attribute “TestAttribute” cannot target method (allowed targets: class)

Como pode ser visto, o erro é autoexplicativo. Ou seja, não é possível aplicar o atributo em métodos de classe, pois ele é projetado para ser usado em classes.

Uso no Mundo Real de Atributos PHP


Enquanto o exemplo anterior parece bom para entender o conceito de Atributos php, a equipa da Spatie encontrou uma maneira interessante de usar Atributos num cenário do mundo real no seu package laravel-route-attributes.

O que essencialmente este pacote faz é fornecer anotações para registrar automaticamente rotas em aplicações Laravel em execução no PHP 8.

Você pode registrar rotas diretamente aplicando um atributo à ação do controlador assim.

use Spatie\RouteAttributes\Attributes\Get;

class MyController
{
    #[Get('minha-rota')]
    public function meuMetodo()
    {

    }
}

Este atributo registrará automaticamente esta rota:

Route::get('minha-rota', [MyController::class, 'meuMetodo']);

Como pode ser visto, o atributo Spatie\RouteAttributes\Attributes\Get aplicado aqui pode ser usado para registrar a rota de obtenção. Existem outros atributos para o restante dos verbos HTTP que você pode usar assim.

#[Spatie\RouteAttributes\Attributes\Post('minha-uri')]
#[Spatie\RouteAttributes\Attributes\Put('minha-uri')]
#[Spatie\RouteAttributes\Attributes\Patch('minha-uri')]
#[Spatie\RouteAttributes\Attributes\Delete('minha-uri')]
#[Spatie\RouteAttributes\Attributes\Options('minha-uri')]

Pode ainda usar o metodo apiResource para gerar automaticamente as rotas em todos os metodos predefinidos pelo laravel tal como o exemplo seguinte:

use Spatie\RouteAttributes\Attributes\Resource;

#[Prefix('api/v1')]
#[Resource(
    resource: 'photos.comments', 
    apiResource: true,
    shallow: true, 
    parameters: ['comments' => 'comment:uuid'],
    names: 'api.v1.photoComments',
    except: ['destroy'],
)]
// OR #[ApiResource(resource: 'photos.comments', shallow: true, ...)]
class PhotoCommentController
{   
    public function index(Photo $photo)
    {
    }

    public function store(Request $request, Photo $photo)
    {
    }

    public function show(Comment $comment)
    {
    }

    public function update(Request $request, Comment $comment)
    {
    }
}

Podemos usar com metodos customizados:

use Spatie\RouteAttributes\Attributes\Get;

class MyController
{
    #[Get('my-route', name: "my-route-name")]
    public function myMethod()
    {
    }
}

Ou ainda adicionar os nossos middlewares:

use Spatie\RouteAttributes\Attributes\Get;

class MyController
{
    #[Get('my-route', middleware: MyMiddleware::class)]
    public function myMethod()
    {

    }
}
webhs
Alojamento recomendado pela DigitalDev

Conclusão:

Os Atributos PHP 8.3 representam uma adição significativa ao arsenal de recursos dos programadores PHP, oferecendo uma maneira mais eficaz e nativa de adicionar metadados a diferentes partes do código. Ao contrário das práticas tradicionais de “doc-comments”, os Atributos proporcionam uma abordagem mais limpa e estruturada para enriquecer o código com informações adicionais.

Baseado em : https://www.amitmerchant.com/how-to-use-php-80-attributes/

About Post Author

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *