Skip to content

Feature Service closures

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

🎯 Service closures

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

#[AutowireServiceClosure('id')] инжектирует сервис не напрямую, а как замыкание Closure(): mixed. Вызов замыкания разрешает сервис в момент обращения — отложенный доступ без прокси (аналог Symfony #[AutowireServiceClosure] / service_closure).

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

Атрибут ставится на параметр конструктора, свойство или сеттер типа \Closure. При autowiring вместо get('id') в член передаётся fn() => $container->get('id'). Сервис не строится, пока замыкание не вызвано; каждый вызов возвращает актуальный экземпляр (для singleton — тот же).

use CloudCastle\DI\Attribute\AutowireServiceClosure;

final class ReportController
{
    public function __construct(
        #[AutowireServiceClosure(HeavyReportBuilder::class)]
        private readonly Closure $builderFactory,
    ) {}

    public function export(): string
    {
        // HeavyReportBuilder строится только здесь, при первом обращении.
        return ($this->builderFactory)()->build();
    }
}

Плюсы

  • Ленивый доступ без прокси-класса — сервис не создаётся, пока не понадобится (экономия на «тяжёлых» зависимостях, редких ветках).
  • Разрывает циклы и откладывает дорогие графы без изменения самого сервиса.
  • Тип \Closure в сигнатуре — явный контракт: потребитель видит, что доступ отложенный.

Минусы

  • Точка вызова замыкания менее очевидна, чем прямая зависимость.
  • Нельзя типизировать результат замыкания на уровне сигнатуры (Closure(): Service — только в PHPDoc).

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

  • Тяжёлые/редко используемые сервисы, дорогие для конструирования на каждый запрос.
  • Разрыв циклических зависимостей, где полноценный lazy-прокси избыточен.

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

  • Оборачивать в замыкание сервис, который всё равно нужен всегда и сразу — лишняя косвенность.
  • Прятать за замыканием обязательную зависимость, скрывая граф.

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

Контейнер Аналог
PHP-DI DI\get() в фабрике (частично, ⚠️)
Symfony #[AutowireServiceClosure] / service_closure
Laravel нет прямого (⚠️fn () => app(Id::class) вручную)
Pimple $pimple->raw() / фабрики
Nette нет прямого (⚠️)

CloudCastle и Symfony дают атрибут-инъекцию сервис-замыкания из коробки; остальные — только вручную.

См. также: Lazy service · Attribute autowiring · Хуки резолвинга.

Clone this wiki locally