Skip to content

Feature Auto registration

github-actions[bot] edited this page Jul 8, 2026 · 1 revision

🏷️ Авто-регистрация через #[AsService]

← Возможности · Главная · Сравнение

Class-level attribute #[AsService] позволяет классу декларативно объявить свои теги и alias'ы прямо в коде. При сканировании каталога (scan()) контейнер autowire'ит класс и применяет объявленное — без ручной проводки. Аналог #[Autoconfigure] / #[AsTaggedItem] / #[AsAlias] Symfony.

Принцип работы

  1. Помечаете класс attribute #[AsService(tags: [...], aliases: [...])].
  2. Вызываете scan($directory) — для каждого найденного instantiable-класса контейнер:
    • регистрирует его через autowire();
    • читает #[AsService] и применяет tag() для каждого тега и alias() для каждого alias.
  3. Класс, уже определённый вручную (set() / bind()), при сканировании пропускается — явная конфигурация имеет приоритет.
use CloudCastle\DI\Attribute\AsService;

#[AsService(tags: ['event.listener'], aliases: [HandlerInterface::class])]
final class CreateUserHandler implements HandlerInterface
{
    public function __construct(private readonly Mailer $mailer) {}
}

$c = new Container();
$c->scan(__DIR__ . '/src/Handlers');       // autowire + теги + alias из attribute

$c->getTagged('event.listener');            // ['App\CreateUserHandler' => object]
$c->get(HandlerInterface::class);           // экземпляр CreateUserHandler (через alias)

Плюсы

  • Ноль ручной конфигурации: новый обработчик регистрируется и тегируется одним attribute — не нужно править конфиг или service provider.
  • Локальность: правила регистрации живут рядом с классом, а не в отдельном файле; при удалении класса «висячих» записей не остаётся.
  • Совместимость: tags/aliases ложатся на штатные tag() и alias() — работает со всем tagged-API.

Минусы

  • Применяется только при scan() — вызов autowire($class) для одного класса attribute не читает (быстрый путь регистрации остаётся без reflection).
  • Требует reflection на этапе сканирования (это фаза сборки, не рантайма) — не для горячего пути.
  • Общий с scan-подходом риск: правила размазаны по классам, единого «реестра» нет (компенсируется dump()).

✅ Рекомендуется

  • Помечать #[AsService] реализации, которые собираются по тегу (listeners, handlers, middleware) и/или должны резолвиться по интерфейсу.
  • Держать сканируемые каталоги узкими (фильтр по namespace), чтобы scan() не обходил лишнее.

❌ Антипаттерны

  • Дублировать в attribute то, что уже задаёт autoconfigure() по интерфейсу — выберите один механизм на правило.
  • Класть в aliases id, конфликтующий с другим сервисом, — alias перезапишет цель резолвинга.
  • Рассчитывать, что #[AsService] сработает без scan() (например, только при get()).

Аналоги в других контейнерах

Контейнер Аналог
PHP-DI #[Injectable] (⚠️ — только scope, устарел; тегов/alias нет)
Symfony #[AsTaggedItem], #[Autoconfigure], #[AsAlias] (эталон)
Laravel нет — регистрация через service providers (❌)
Pimple нет attributes (❌)
Nette регистрация через NEON, не через PHP-attributes (❌)

CloudCastle, Symfony — теги и alias'ы объявляются class-level attribute'ом.

См. также: Сканирование каталога · Autoconfigure · Теги · Алиасы.

Clone this wiki locally