-
-
Notifications
You must be signed in to change notification settings - Fork 1
Feature Decorators
decorate($id, $decorator) оборачивает сервис в декоратор — добавляет поведение (логирование, кэш, метрики) не трогая исходный класс. Туториал — Tags-and-decorators.
decorate() регистрирует цепочку декораторов для id. При разрешении сервиса контейнер создаёт исходный экземпляр, затем последовательно оборачивает его каждым декоратором (callable, получающий внутренний сервис и контейнер) — снаружи наружу. Результат кэшируется как обычный singleton.
$c->set(PaymentGateway::class, fn () => new StripeGateway());
$c->decorate(PaymentGateway::class,
fn (PaymentGateway $inner, Container $c) => new LoggingGateway($inner, $c->get(Logger::class)),
);
$c->decorate(PaymentGateway::class,
fn (PaymentGateway $inner) => new RetryingGateway($inner),
);
$c->get(PaymentGateway::class); // Retrying(Logging(Stripe))- Open/Closed: добавляем поведение без изменения исходного класса.
- Композиция: несколько декораторов складываются в цепочку.
- Идеально для cross-cutting concerns: логи, кэш, ретраи, метрики, транзакции.
- Глубокие цепочки усложняют отладку стека вызовов.
- Декоратор должен реализовывать тот же интерфейс, что и внутренний сервис.
- Декорировать по интерфейсу — декоратор и оригинал взаимозаменяемы.
- Держать декораторы «тонкими» и однонаправленными (одна ответственность на декоратор).
- Прятать в декораторе бизнес-логику, меняющую контракт, — это уже не декоратор.
- Цепочки из множества декораторов, где порядок неочевиден и хрупок.
При сканировании каталога класс с #[AsDecorator('id')] автоматически регистрируется декоратором сервиса id — без ручного decorate() (аналог Symfony #[AsDecorator]):
#[AsDecorator('logger')]
final class TimingLogger implements LoggerInterface
{
public function __construct(private readonly LoggerInterface $inner) {}
public function log(string $message): void
{
$this->inner->log('[' . microtime(true) . '] ' . $message);
}
}Декорируемый (внутренний) сервис передаётся первым параметром конструктора; остальные параметры должны иметь значения по умолчанию. Для декораторов со сложными зависимостями используйте factory в конфигурации или программный decorate().
| Контейнер | Аналог |
|---|---|
| PHP-DI | через фабрику-обёртку ( |
| Symfony |
decorates: (эталонная поддержка) |
| Laravel |
extend() — обёртка сервиса |
| Pimple |
extend() (базовая обёртка) — но без autowiring ( |
| Nette | декорирование через конфиг |
Все кроме «чистого» Pimple; CloudCastle даёт цепочки декораторов с доступом к контейнеру.
См. также: Tags-and-decorators (гайд) · After-resolving hooks.
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