(PHP 8)
PHP-атрибуты — структурированные машиночитаемые метаданные для классов, методов, функций, параметров, свойств и констант. Атрибуты инспектируют при выполнении кода через 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("Файл не существует");
}
}
#[SetUp]
public function targetDirectoryExists()
{
if (!file_exists($this->targetDirectory)) {
mkdir($this->targetDirectory);
} elseif (!is_dir($this->targetDirectory)) {
throw new RuntimeException("Целевой путь $this->targetDirectory не указывает на каталог");
}
}
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);
?>