Visão geral dos atributos

(PHP 8)

Os atributos oferecem a capacidade de adicionar informações de metadados estruturadas e legíveis por máquina em declarações no código: classes, métodos, funções, parâmetros, propriedades e constantes de classe podem ser o destino de um atributo. Os metadados definidos pelos atributos podem ser inspecionados em tempo de execução usando as APIs Reflection. Os atributos podem, portanto, ser pensados como uma linguagem de configuração incorporada diretamente no código.

Com atributos, a implementação genérica de um recurso e seu uso concreto em uma aplicação podem ser desacoplados. De certa forma, é comparável a interfaces e suas implementações. Mas onde as interfaces e implementações são sobre código, os atributos são sobre a anotação de informações extras e configuração. As interfaces podem ser implementadas por classes, mas os atributos também podem ser declarados em métodos, funções, parâmetros, propriedades e constantes de classe. Como tal, eles são mais flexíveis do que as interfaces.

Um exemplo simples de uso de atributo é converter uma interface que possui métodos opcionais para usar atributos. Vamos assumir uma interface ActionHandler que representa uma operação em uma aplicação, onde algumas implementações de um manipulador de ações requerem configuração e outras não. Em vez de exigir que todas as classes que implementam ActionHandler implementem um método setUp(), um atributo pode ser usado. Um benefício dessa abordagem é que o atributo pode ser usado várias vezes.

Example #1 Implementando métodos opcionais de uma interface com Atributos

<?php
interface ActionHandler
{
    public function execute();
}

#[Attribute]
class SetUp {}

class CopyFile implements ActionHandler
{
    public string $fileName;
    public string $targetDirectory;

    #[SetUp]
    public function fileExists()
    {
        if (!file_exists($this->fileName)) {
            throw new RuntimeException("Arquivo não existe");
        }
    }

    #[SetUp]
    public function targetDirectoryExists()
    {
        if (!file_exists($this->targetDirectory)) {
            mkdir($this->targetDirectory);
        } elseif (!is_dir($this->targetDirectory)) {
            throw new RuntimeException("Diretório de destino $this->targetDirectory não é um diretório");
        }
    }

    public function execute()
    {
        copy($this->fileName, $this->targetDirectory . '/' . basename($this->fileName));
    }
}

function executeAction(ActionHandler $actionHandler)
{
    $reflection = new ReflectionObject($actionHandler);

    foreach ($reflection->getMethods() as $method) {
        $attributes = $method->getAttributes(SetUp::class);

        if (count($attributes) > 0) {
            $methodName = $method->getName();

            $actionHandler->$methodName();
        }
    }

    $actionHandler->execute();
}

$copyAction = new CopyFile();
$copyAction->fileName = "/tmp/foo.jpg";
$copyAction->targetDirectory = "/home/user";

executeAction($copyAction);