(PHP 8)
Атрибуты помогают добавлять к объявлениям в коде структурированную машиночитаемую метаинформацию. Атрибуты нацеливают на классы, включая анонимные, методы, функции, параметры, свойства и константы класса. Затем во время выполнения кода метаданные, которые определили атрибутами, инспектируют через API-интерфейс модуля Reflection. Поэтому атрибуты рассматривают как язык конфигурации, который встраивается в код.
Атрибуты разделяют общее и специфическое поведение классов, параметров, свойств и других элементов кода в приложении, на которые нацелили атрибут. В каком-то смысле атрибуты похожи на интерфейс с его реализациями. Но интерфейсы и реализации — это про код, а атрибуты — про добавление дополнительной информации и конфигурацию. Интерфейсы реализуют только классами, тогда как атрибуты разрешается нацеливать на методы, функции, параметры, свойства и константы классов. Поэтому атрибуты — существенно более гибкий механизм, чем интерфейсы.
Простой пример замены интерфейса с необязательными методами на код с атрибутами.
Предположим, интерфейс 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);
?>