Атрибуты вида #[CustomAttribute]
добавлены в PHP 8.0 (RFC, Документация)
Атрибуты предоставляют возможность добавлять различную конфигурацию к конкретным классам, методам, константам класса и тд. При этом, пока вы не начнёте обращаться к этой конфигурации, выполнение PHP никак её не воспринимает, как и обычные комментарии. Через рефлексию можно запросить атрибут конкретного класса/метода и убедиться, что такой атрибут ему назначен и также в атрибуте есть необходимые настройки, уникальные для данного класса/метода.
Атрибут может быть назначен не только классу или методу, но и другим структурным сущностям. Это могут быть функции, константы и поля класса, параметры метода. Также можно разрешить дублирование атрибутов.
Например:
<?php
class PostsController
{
#[\Route(route:"/api/posts/{id}", methods: ["GET"])]
public function get($id) { /* ... */ }
}
Это пример из документации и он не абстрактный, а прямо указывает, что можно реализовать маршрутизацию приложения на конфигурации. Такой вариант уже давно реализован в Symfony. Что происходит, когда скрипт выполняет этот код? Ничего. Если вы укажете в атрибуте несуществующий класс, ошибки на этом этапе не будет. Однако в маршрутизаторе, при обработке запроса, учитываются эти данные. Так как код разбора запроса может быть различным (по усмотрению программиста), не буду вдаваться в подробности. В целом же - разработчик сам должен создать класс Route в проекте и через рефлексию запросить его экземляр через название класса и метода, к которым прикреплён атрибут (или не прикреплен, это тоже предстоит проверить в коде). Только тогда будет создан уникальный экземпляр класса Route и можно будет проверить, подходит ли этот метод под заданные параметры экземпляра. По сути это обычный комментарий к классу или методу(и не только), но который можно получить не в виде текста, а некоего класса с параметрами.
Для вышеобозначенного кода класс Route может выглядеть так:
<?php
#[Attribute]
readonly class Route {
public function __construct(
public string $route;
public array $methods;
){ }
}
Специальный атрибут в классе указывает, что класс является шаблоном атрибута. Его инициализированный экземпляр и будет получен при запросе атрибутов метода get() у класса PostsController. Остаётся сравнить значения в полях $route и $methods с текущим запросом и произвести другие сопутствующие действия.
Насчет удобства маршрутизации через атрибуты... По мне, так удобнее, чтобы вся карта маршрутов хранилась в одном месте, чем была размазана по контроллерам. Но это дело вкуса.
В целом актуальность атрибутов сильно преувеличена. Если бы решаемая проблема была глобальнее, то мы увидели бы её в официальных примерах к атрибутам. Но атрибуты решают узкоспециализированные задачи, вроде маршрутизации, не внося особо важных архитектурных новшеств для программирования на PHP. Поэтому добавление атрибутов не вынужденная мера для развития языка, а внесение в него альтернативных решений частных задач. Например, аналогичный функционал существовал и ранее в Symfony, только выглядел по-другому:
<?php
class PostsController
{
/**
* @Route("/api/posts/{id}", methods={"GET"})
*/
public function get($id) { /* ... */ }
}
Принцип его обработки в целом точно такой-же, логическая интерпретация определялась в коде фреймворка, только разбор был применён для блока phpDoc. Кроме этого, трудно представить, зачем рядовому разработчику создавать новые атрибуты в проекте, если он уже использует фреймворк с маршрутизацией и основными решениями. Создание дополнительной логики в комментариях сделает обычный код не таким наглядным.
Атрибуты PHP поддерживаются многими IDE и анализаторами кода для решения собственных задач, подробнее в статье.
В PHP 8.3 планируется добавить атрибут #[Override]
, с помощью которого можно будет явно помечать методы, которые переопределяют методы из родительского класса.
