Пакет · di

Атрибуты

Полная поверхность атрибутов. Scope-атрибуты ставятся на классы; атрибуты внедрения — на свойства и параметры конструктора. Все они final readonly.

Кратко

Атрибут Цель Аргумент Назначение
#[Singleton] класс Один общий экземпляр на процесс.
#[Request] класс Один экземпляр на запрос / корутину.
#[Transient] класс Новый экземпляр при каждом разрешении (по умолчанию).
#[Autowired] свойство, параметр Внедрить по объявленному типу.
#[Inject] свойство, параметр ?string $id = null Внедрить конкретный класс или именованное значение.
#[Lazy] свойство, параметр Внедрить отложенный proxy, разрешаемый при первом использовании.

Пространство имён: Flytachi\Winter\DI\Attribute\.

Scope-атрибуты

Задают время жизни класса. Читаются DICollector во время сканирования или контейнером при первом make(), если класс не привязан вручную. Ручная привязка всегда переопределяет атрибут. Полная семантика — на странице Scope’ы.

#[Singleton]

php
use Flytachi\Winter\DI\Attribute\Singleton;

#[Singleton]
class UserRepository
{
  public function __construct(private DatabaseConnection $db) {}
}

Один экземпляр на время жизни контейнера (процесса). Эквивалентно $container->singleton(UserRepository::class). Для stateless-сервисов. Не для пер-запросного состояния под Swoole — используйте #[Request].

#[Request]

php
use Flytachi\Winter\DI\Attribute\Request;

#[Request]
class AuthContext
{
  private ?User $user = null;
}

Один экземпляр на HTTP-запрос / корутину. Под Swoole изолируется через Coroutine::getContext(); под FPM / CLI эквивалентно #[Singleton] (один процесс = один запрос). Для пер-запросного состояния.

#[Transient]

php
use Flytachi\Winter\DI\Attribute\Transient;

#[Transient]
class QueryBuilder
{
  private array $clauses = [];
}

Новый экземпляр при каждом make() / внедрении. Это уже поведение по умолчанию, когда атрибута нет — #[Transient] просто делает намерение явным.

Атрибуты внедрения

Отмечают, где и как предоставляется зависимость. На свойстве внедрение выполняется автоматически после конструктора во время make(); свойство не обязано быть публичным.

#[Autowired]

php
use Flytachi\Winter\DI\Attribute\Autowired;

class UserController
{
  #[Autowired]
  private UserService $service;

  public function __construct(
      #[Autowired] private CacheInterface $cache,
  ) {}
}

Разрешает зависимость по объявленному PHP-типу. Эквивалентно #[Inject] без аргумента — явная метка внедрения по типу. Интерфейсу всё равно нужна привязка, чтобы это разрешилось.

#[Inject]

php
use Flytachi\Winter\DI\Attribute\Inject;

// #[Inject(?string $id = null)]

Переопределяет автовайринг для одного параметра или свойства.

Значение $id Поведение
null (по умолчанию) Разрешить по объявленному PHP-типу — как #[Autowired].
строка FQCN, напр. #[Inject(FileCache::class)] Внедрить этот конкретный класс, обходя глобальную привязку объявленного типа.
именованный ключ, напр. #[Inject('config.timeout')] Внедрить скаляр / заранее построенное значение, заданное через Container::set().
php
class UserService
{
  public function __construct(
      private CacheInterface $primary,                 // глобальная привязка
      #[Inject(FileCache::class)] private CacheInterface $fallback,  // конкретный класс
      #[Inject('config.timeout')] private int $timeout,             // именованное значение
  ) {}
}

#[Lazy]

php
use Flytachi\Winter\DI\Attribute\Lazy;

class SmsSendService
{
  #[Lazy]
  private FakeSendService $peer;      // нативный proxy PHP 8.4; разрешается при первом использовании
}

Внедряет нативный ленивый proxy PHP 8.4 (ReflectionClass::newLazyProxy()) вместо немедленного разрешения. Реальный экземпляр строится из контейнера при первом обращении — механизм разрыва циклических зависимостей.

  • Работает на свойствах и параметрах конструктора; сочетается с #[Autowired] / #[Inject].
  • Проксируемый тип должен быть конкретным классом. Для интерфейса укажите конкретный: #[Inject(SmsSendService::class), Lazy]. Пустой #[Lazy] на интерфейсе или абстрактном классе бросает ContainerException.
  • Proxy совместим по типу с реальным классом и остаётся неинициализированным до первого использования.

Внутренности — в Ленивых proxy.

Приоритет

Для одной зависимости контейнер применяет первое подходящее правило:

  1. Override у make() / call() по имени параметра.
  2. #[Inject] — явный $id или объявленный тип, когда $id равен null.
  3. #[Autowired] — объявленный тип.
  4. Автовайринг — объявленный тип, со значением по умолчанию параметра как запасным вариантом, если тип разрешить не удалось и значение по умолчанию есть.

#[Lazy] ортогонален: он меняет когда строится выбранная зависимость (откладывает до первого использования), а не какая именно.