Skip to content

Feature PSR11

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

📦 PSR-11 и базовая регистрация

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

Стандартный контракт доступа к сервисам: get(string $id) и has(string $id) из PSR-11, плюс явная регистрация set().

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

Container реализует Psr\Container\ContainerInterface. Регистрация складывает определение (экземпляр, скаляр или фабрику) в карту definitions по строковому id. Первый get($id) разрешает определение (вызывает фабрику или возвращает значение), кэширует результат в resolved и возвращает его; повторный get($id) отдаёт тот же объект одним хеш-lookup (hot path). has($id) проверяет наличие без создания экземпляра.

use CloudCastle\DI\Container;

$c = new Container();
$c->set('db.dsn', 'pgsql://localhost/app');            // скаляр
$c->set(Logger::class, fn () => new Logger('/var/log')); // фабрика

$c->has('db.dsn');          // true — без создания
$c->get(Logger::class);     // Logger (создан и закэширован)
$c->get(Logger::class);     // тот же экземпляр

При отсутствии id get() бросает NotFoundException (реализует Psr\Container\NotFoundExceptionInterface), при ошибке создания — ContainerException (ContainerExceptionInterface).

Плюсы

  • Совместимость. Любой код, ожидающий Psr\Container\ContainerInterface, работает с CloudCastle без адаптеров.
  • Предсказуемость. set() явно фиксирует, что и как создаётся — без «магии» скрытых провайдеров.
  • Быстрый get() из кэша — прямой возврат без alias-резолва и проверок (см. Производительность).
  • Честный has() — не инстанцирует сервис ради проверки (в отличие от контейнеров, где has() = «попробовать создать»).

Минусы

  • Строковые id не типобезопасны — опечатка ловится только в рантайме (митигируется использованием Class::class как id).
  • Явная регистрация многословнее, чем чистый autowiring, — для больших графов включайте autowiring.

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

  • Использовать Class::class / Interface::class как id — рефакторинг-safe и совместимо с autowiring.
  • Регистрировать фабрики как callable, а не готовые объекты, — экземпляр создаётся лениво при первом get().
  • Проверять наличие через has() перед get() в опциональных сценариях.

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

  • Service Locator по всему коду: прокидывать сам контейнер в сервисы и дёргать get() внутри бизнес-логики. Внедряйте зависимости через конструктор — контейнер собирает граф на границе (composition root). См. Анти-паттерны.
  • Хранить в контейнере изменяемое состояние запроса под общими id (см. Scoped-сервисы).

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

Контейнер Аналог
PHP-DI get() / has() (PSR-11); has() может пытаться разрешить autowiring
Symfony PSR-11 get() / has(); регистрация — через services.yaml / ContainerBuilder
Laravel make() / bound(); PSR-11 адаптер есть, но has() неоднозначен
Pimple $c['id'] (ArrayAccess); PSR-11 — только через Pimple\Psr11\Container (🔌)
Nette PSR-11 через адаптер; нативно — getByType() / getService()

CloudCastle, Symfony, Nette — чистый has() без создания.

См. также: Фабрики и singleton · Справочник API.

Clone this wiki locally