Справочник API
Полный справочник по публичной поверхности Winter DI. Это авторитетный источник сигнатур и значений по умолчанию; страницы руководств и глубокого погружения ссылаются сюда.
Container
final class Container implements Psr\Container\ContainerInterface. Реестр и резолвер. Он
саморегистрируется, поэтому Container и ContainerInterface можно внедрять как зависимости.
Инициализация
Container::init(): static
Container::getInstance(): static
Container::isInitialized(): bool| Метод | Описание |
|---|---|
init() |
Создаёт новый контейнер и сохраняет его как процессный синглтон. Вызывается один раз при bootstrap. Возвращает экземпляр для fluent-цепочки. |
getInstance() |
Возвращает инициализированный контейнер. Бросает ContainerException, если init() не вызывался. |
isInitialized() |
Существует ли уже контейнер. Для кода, работающего и с контейнером, и без него — библиотеки, доступной как из поднятого приложения, так и из голого скрипта. Спросить дешевле, чем ловить исключение getInstance(): отсутствие контейнера там — законное состояние, а не ошибка. |
PSR-11
get(string $id): mixed
has(string $id): bool| Метод | Описание |
|---|---|
get($id) |
Псевдоним для make($id). Бросает NotFoundException, если у $id нет привязки и это не инстанцируемый класс. |
has($id) |
true, если у $id есть привязка, разрешённое / set()-значение, или совпадает с существующим именем класса (class_exists). |
Регистрация
bind(string $abstract, string|callable $concrete): static
singleton(string $abstract, string|callable|null $concrete = null): static
transient(string $abstract, string|callable|null $concrete = null): static
request(string $abstract, string|callable|null $concrete = null): static
contextual(string $abstract, callable $factory): static
set(string $id, mixed $value): static| Метод | Scope | Описание |
|---|---|---|
bind() |
transient |
Сопоставляет $abstract конкретному классу или фабрике-замыканию. Новый экземпляр при каждом разрешении. |
singleton() |
singleton |
Один общий экземпляр на процесс. $concrete по умолчанию $abstract (самопривязка). |
transient() |
transient |
Как bind(), но допускает самопривязку, когда $concrete равен null. |
request() |
request |
Один экземпляр на HTTP-запрос / корутину. $concrete по умолчанию $abstract. |
contextual() |
— | Фабрика, знающая потребителя: fn(Container $c, ?string $consumer). Наложение на время внедрения; результат никогда не кэшируется. См. makeContextual(). |
set() |
— | Сохраняет скаляр или заранее построенный экземпляр под именованным ключом. Извлекается через make($id) или #[Inject('id')]. |
Фабрика-замыкание, переданная в bind() / singleton() / transient() / request(),
получает контейнер: fn(Container $c) => new Service($c->make(Dep::class)).
Ручная регистрация переопределяет атрибуты
Вызов bind() / singleton() / transient() / request() всегда имеет приоритет над
scope-атрибутом класса. Повторная регистрация того же абстракта заменяет предыдущую привязку и
сбрасывает всё, что оставил прежний scope — закэшированный экземпляр и его учёт для
flushRequestScope(), — поэтому переопределение действует и для класса, который уже
разрешался.
Разрешение
make(string $abstract, array $overrides = []): mixed
call(callable|array $callable, array $overrides = []): mixed
makeContextual(string $abstract, ?string $consumer): mixedmake()
Разрешает абстракт — класс, интерфейс или именованное значение.
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
$abstract |
string |
— | Имя класса/интерфейса или ключ именованного значения. |
$overrides |
array |
[] |
Переопределения параметров по имени. Обходит автовайринг для этих параметров. |
Порядок разрешения: (1) уже разрешённый синглтон / set()-значение, (2) кэш request-scope в
контексте корутины, (3) проверка цикла по стеку текущей единицы работы, (4) ожидание
синглтона, который строит другая корутина, (5) ручная привязка, (6) автовайринг по имени
класса. Передача $overrides всегда строит свежий экземпляр и никогда не кэшируется.
Бросает NotFoundException, если не разрешить, ContainerException при циклической
зависимости — с полной цепочкой вида [A] → [B] → [A]. Шаги 3 и 4 разобраны в
Конкурентном разрешении.
call()
Вызывает метод или замыкание с параметрами, разрешёнными из контейнера.
$c->call([UserController::class, 'index']); // разрешить класс, затем вызвать
$c->call([$controller, 'store']); // существующий экземпляр
$c->call(fn(UserService $s) => $s->all()); // замыкание
$c->call([ImportJob::class, 'run'], ['chunkSize' => 100]); // с overrides| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
$callable |
callable|array |
— | [class-string, method], [object, method] или любой callable/замыкание. |
$overrides |
array |
[] |
Переопределения параметров по имени. |
makeContextual()
makeContextual(string $abstract, ?string $consumer): mixedТочка входа резолвера для контекстного внедрения. Если для $abstract зарегистрирована
фабрика contextual(), она вызывается с $consumer (результат не кэшируется); иначе
делегирует make($abstract). Прикладной код обычно использует make() — это существует для
механизма внедрения. Контекстное наложение применяется только при внедрении (конструктор /
метод / свойство); прямой make()/get() использует обычную привязку.
inject()
inject(object $instance): objectЗаполняет свойства #[Autowired] / #[Inject] у объекта, который вы построили сами, и
возвращает его же. Это вторая половина make(), вынесенная отдельно: make() и строит, и
внедряет — что правильно, когда объект принадлежит контейнеру, и неправильно, когда
идентичностью объекта должен управлять вызывающий.
public static function instance(?string $alias = null): static
{
$repository = new static(); // идентичность остаётся за вызывающим
Container::getInstance()->inject($repository); // зависимости всё равно приезжают
return $alias === null ? $repository : $repository->as($alias);
}Классический случай — хэндл репозитория с алиасом запроса: алиас живёт в состоянии объекта,
поэтому два алиаса одной таблицы требуют двух разных объектов, а разрешение их через make()
на общей привязке схлопнуло бы оба в один и потеряло бы алиас. Экземпляр никогда не
подменяется, состояние конструктора не трогается, повторный вызов безвреден.
flushRequestScope()
flushRequestScope(): voidЗавершает текущий request-scope: следующее разрешение #[Request]-привязки построит свежий
экземпляр. Синглтоны и transient не затрагиваются — это конец scope’а, а не сброс контейнера.
По HTTP scope заканчивается сам: запрос — это корутина, её контекст умирает вместе с ней. Больше ни у чего такой границы нет. Тело долгоживущего воркера целиком выполняется в одной корутине, поэтому request-scoped бин, разрешённый в цикле, живёт весь прогон и передаёт каждой задаче состояние предыдущей:
while ($this->isRunning()) {
$job = $queue->pop();
$container->flushRequestScope(); // ← здесь начинается новая единица работы
$ctx = $container->make(JobContext::class); // свежий, каждый раз
// ... работа ...
}Где заканчивается одна единица работы, знает только код, который крутит цикл, — поэтому вызов явный.
Провайдеры
register(string $providerClass): staticИнстанцирует $providerClass, проверяет, что он наследует ServiceProvider, и сразу
выполняет его register(). Бросает ContainerException, если класс не наследует
ServiceProvider.
Scanner
Проходит по дереву проекта один раз и передаёт каждый найденный класс всем зарегистрированным коллекторам.
Scanner::run(string $rootDir, ?string $cache = null): static
->collect(CollectorInterface $collector): static
->exclude(array $dirs): static
->execute(): void| Метод | Описание |
|---|---|
run($rootDir, $cache) |
Создаёт сканер для $rootDir. Когда $cache — путь: попадание в кэш загружает список FQCN и пропускает обход ФС; промах обходит и записывает список. null (по умолчанию) всегда обходит, никогда не кэширует. |
collect($collector) |
Регистрирует коллектор, получающий каждый найденный класс. Вызываются в порядке регистрации. |
exclude($dirs) |
Добавляет абсолютные пути каталогов для исключения. vendor/ исключается всегда. |
execute() |
Запускает сканирование. Абстрактные классы, интерфейсы и трейты пропускаются до запуска коллекторов. |
Файл кэша — обычный PHP-файл, возвращающий string[] FQCN. Удалите его, чтобы принудительно
пересканировать. См. Сканирование и автообнаружение.
DICollector
final readonly class DICollector implements CollectorInterface. Встроенный коллектор,
регистрирующий классы со scope-атрибутом.
new DICollector(Container $container)
collect(string $class, ReflectionClass $ref): void| Атрибут класса | Регистрация |
|---|---|
#[Singleton] |
$container->singleton($class) |
#[Request] |
$container->request($class) |
#[Transient] |
$container->transient($class) |
Классы без scope-атрибута игнорируются (остаются автовайримыми как transient).
CollectorInterface
Реализуйте, чтобы подключить свою логику к проходу Scanner.
interface CollectorInterface
{
// @param class-string $class FQCN
// @param ReflectionClass $ref экземпляр рефлексии
public function collect(string $class, ReflectionClass $ref): void;
}collect() вызывается один раз на инстанцируемый класс, в плотном цикле — держите его лёгким.
ServiceProvider
Абстрактная база для группировки привязок.
abstract class ServiceProvider
{
abstract public function register(Container $c): void;
}Регистрируется через $container->register(MyProvider::class). См.
Сервис-провайдеры.
ProxyInterface
Реализуйте, если генерируете подкласс, который должен разрешаться под именем того класса, чьё место он занимает.
interface ProxyInterface
{
/** @return class-string класс, за который выступает этот proxy */
public static function proxyTarget(): string;
}Прокси — это сгенерированный класс, наследующий оригинал и добавляющий поведение вокруг его
методов: выполнить в фоне, обернуть в транзакцию, закэшировать результат. Именно так устроен
#[Async] в ядре Winter. Проблема в том, что после подмены $instance::class возвращает
UserService__Async, а не UserService, — и резолвер, который по этому имени решает, для
кого строится зависимость, начинает ошибаться: фабрика
contextual() назвала бы канал логгера сгенерированным
классом.
Реализация интерфейса возвращает идентичность на место: резолвер планирует внедрение от целевого класса и его же передаёт фабрикам как потребителя.
final class UserServiceProxy extends UserService implements ProxyInterface
{
public static function proxyTarget(): string
{
return UserService::class;
}
}
$container->singleton(UserService::class, UserServiceProxy::class);Метод статический намеренно: идентичность нужна резолверу до того, как экземпляр
существует, — при сборке аргументов конструктора. $instance::class продолжает возвращать
сгенерированное имя: интерфейс меняет то, как о нём рассуждает контейнер, а не то, что
сообщает PHP. Внедрение в приватные свойства цели при этом работает — резолвер обходит иерархию
классов, а не полагается на ReflectionClass::getProperties().
ReflectionCache
Кэш объектов рефлексии на процесс — строится один раз, переиспользуется всё время жизни процесса.
ReflectionCache::classOf(string $class): ReflectionClass
ReflectionCache::enumOf(string $enum): ReflectionEnum
ReflectionCache::method(string $class, string $method): ReflectionMethod
ReflectionCache::parameters(string $class, string $method): ReflectionParameter[]| Метод | Возвращает | Примечания |
|---|---|---|
classOf($class) |
ReflectionClass |
Кэшируется по классу. |
enumOf($enum) |
ReflectionEnum |
Кэшируется по enum. |
method($class, $method) |
ReflectionMethod |
Кэшируется по class::method. |
parameters($class, $method) |
ReflectionParameter[] |
Делегирует method(); разделяет его запись кэша. |
Публичная утилита — см. Кэш рефлексии.
Исключения
Оба реализуют интерфейсы исключений PSR-11 и наследуют \RuntimeException.
| Исключение | Реализует | Бросается когда |
|---|---|---|
NotFoundException |
Psr\Container\NotFoundExceptionInterface |
Нет привязки для id, и это не инстанцируемый класс. |
ContainerException |
Psr\Container\ContainerExceptionInterface |
Циклическая зависимость, неразрешимый параметр, неинициализированный контейнер, неверный провайдер, неверная цель #[Lazy]. |
Атрибуты
Справочные таблицы по #[Singleton], #[Request], #[Transient], #[Autowired], #[Inject]
и #[Lazy] находятся на странице Атрибуты.