-
-
Notifications
You must be signed in to change notification settings - Fork 1
Scopes
Статус: v1.19.0 (#33) · аддитивно, PHP 8.1+, breaking нет.
Scoped-сервис — это третий режим времени жизни между singleton (get() — один на контейнер) и prototype (make() — новый на каждый вызов): один экземпляр в пределах активного scope, новый — в каждом новом scope.
Типичный сценарий — жизнь запроса: RequestContext, UnitOfWork, соединение с БД на HTTP-запрос или задачу очереди. Внутри одного запроса — один экземпляр; следующий запрос получает свежий.
use CloudCastle\DI\Container;
$container = new Container();
$container->scoped('request.context', static fn (): RequestContext => new RequestContext());
$container->beginScope(); // начало запроса
$a = $container->get('request.context');
$b = $container->get('request.context');
$a === $b; // true — один экземпляр в scope
$container->endScope(); // конец запроса — экземпляр уничтожается
$container->beginScope(); // следующий запрос
$c = $container->get('request.context');
$c === $a; // false — новый экземпляр
$container->endScope();| Метод | Назначение |
|---|---|
scoped(string $id, callable|string $concrete) |
Регистрирует scoped-сервис. $concrete — фабрика callable(self): mixed или FQCN класса для autowiring |
beginScope() |
Открывает новый scope (вложенные — через стек LIFO) |
endScope() |
Закрывает текущий scope и уничтожает его scoped-экземпляры |
inScope(): bool |
Активен ли хотя бы один scope |
isScoped(string $id): bool |
Зарегистрирован ли сервис как scoped |
scopeDepth(): int |
Глубина вложенности активных scope-ов (0 — вне scope) |
// Через фабрику (получает контейнер):
$container->scoped('uow', static fn (Container $c): UnitOfWork => new UnitOfWork($c->get('db')));
// Через FQCN (autowiring в каждом scope):
$container->scoped(UnitOfWork::class, UnitOfWork::class);Вне активного scope scoped-сервис ведёт себя как transient — фабрика вызывается на каждый get(), экземпляр не кэшируется:
$container->scoped('svc', static fn () => new Service());
$x = $container->get('svc');
$y = $container->get('svc');
$x === $y; // false — вне scope каждый get() создаёт новый
$container->inScope(); // falseЭто безопасный дефолт: код, использующий scoped-сервис без открытого scope, продолжает работать (как make()), а не падает.
beginScope() можно вкладывать — каждый уровень имеет свой кэш (стек LIFO). Вложенный scope изолирован от внешнего; после endScope() возобновляется экземпляр внешнего:
$container->beginScope();
$outer = $container->get('svc');
$container->beginScope();
$inner = $container->get('svc');
$inner === $outer; // false — вложенный scope изолирован
$container->scopeDepth(); // 2
$container->endScope();
$container->get('svc') === $outer; // true — вернулись во внешний scope
$container->endScope();endScope() без активного scope бросает ContainerException.
| Режим | Метод | Время жизни |
|---|---|---|
| Singleton |
set() + get()
|
Один на контейнер |
| Prototype | make() |
Новый на каждый вызов |
| Scoped |
scoped() + get()
|
Один на активный scope, новый — в каждом scope |
На runtime Container доступен полный scoped-API. В compiled-контейнере работает жизненный цикл scope (beginScope/endScope/inScope/scopeDepth), но регистрация scoped-сервисов недоступна — compiled-контейнер immutable, определения зафиксированы на этапе компиляции. Регистрируйте scoped-сервисы на runtime-контейнере до freeze()/компиляции.
$container->scoped('request', static fn () => new RequestContext());
foreach ($requests as $httpRequest) {
$container->beginScope();
try {
$handler = $container->get(RequestHandler::class);
$handler->handle($httpRequest); // все get('request') внутри — один контекст
} finally {
$container->endScope(); // гарантированная очистка scope
}
}Оборачивайте endScope() в finally, чтобы scope закрывался даже при исключении.
См. также: API-reference · Фабрики и singleton · Прототипы, alias, lazy
CloudCastle DI · · PHP 8.1+ · PSR-11
GitHub · Packagist · Releases · Discussions · Сравнение
Исходники Wiki — каталог wiki/ в репозитории (синхронизация через GitHub Actions)
- 🏗️ Архитектура
- ⚡ Быстрый старт
- 📊 Сравнение — 5 аналогов, таблица + победители
- 🧩 Возможности (детально) — каждая фича: принцип, +/−, аналоги
- 🧪 Bootstrap
- 🚀 Compiled container
- 🔗 Contextual binding
- 🤖 Autowiring
- 📄 Конфигурация
- 📖 Справочник конфигурации
- 📂 Сканирование классов
- 🌍 Глобальный реестр
- 🏷️ Теги и декораторы
- 🔗 call(), bind(), hooks
- 🔄 Прототипы, alias, lazy, lazyGhost
- 📋 Справочник API
- 🏭 Фабрики и singleton