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

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

?>