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