Введение в атрибуты

(PHP 8)

Атрибуты — это структурированные машиночитаемые метаданные, объявленные в коде. Целью атрибутов могу быть: классы (включая анонимные), методы, функции, параметры, свойства и константы класса. Затем описанные атрибутами метаданные можно проанализировать во время исполнения средствами Reflection API. Поэтому атрибуты можно рассматривать как встроенный в код язык конфигурации.

Атрибуты разделяют общее и специфическое поведение сущностей в приложении. В каком-то смысле это похоже на интерфейс с его реализациями. Но интерфейсы и реализации — это про код, а атрибуты — про добавление дополнительной информации и конфигурацию. Интерфейсы могут реализовываться только классами, тогда как атрибуты можно нацеливать на методы, функции, параметры, свойства и константы классов. Поэтому атрибуты — существенно более гибкий механизм, чем интерфейсы.

Простой пример замены интерфейса с необязательными методами на код с атрибутами. Предположим, интерфейс ActionHandler описывает в приложении операцию, для выполнения которой одним реализациям нужна предварительная настройка, а другим — нет. И вместо внесения в интерфейс ActionHandler дополнительного метода setUp(), который для части реализаций будет пустым, можно использовать атрибут. Одним из преимуществ этого подхода является то, что мы можем использовать атрибут несколько раз.

Пример #1 Реализация опциональных методов интерфейса с помощью атрибутов

<?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("File does not exist");
}
}

#[
SetUp]
public function
targetDirectoryExists()
{
if (!
file_exists($this->targetDirectory)) {
mkdir($this->targetDirectory);
} elseif (!
is_dir($this->targetDirectory)) {
throw new
RuntimeException("Target directory $this->targetDirectory is not a directory");
}
}

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);