Введение в атрибуты
(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("Файл не найден");
}
}
#[SetUp]
public function targetDirectoryExists()
{
mkdir($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);