waffle-commons / container
PSR-11 dependency-injection container for Waffle Commons: autowiring, singleton lifecycles, and per-request reset semantics for resident-worker mode.
Requires
- php: ^8.5
- psr/container: ^2.0
- waffle-commons/contracts: 0.1.0-beta6
Requires (Dev)
- carthage-software/mago: ^1.29
- cyclonedx/cyclonedx-php-composer: ^6.2
- igor-php/igor-php: ^0.7.0
- php-mock/php-mock-phpunit: ^2.15
- phpunit/phpunit: ^12.5
- vimeo/psalm: ^6.16
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-08 20:24:32 UTC
README
Waffle Container Component
Release:
0.1.0-beta6Β |ΒCHANGELOG.mdPSR Compliance: PSR-11 (Psr\Container\ContainerInterface)
A strict PSR-11 service container with reflection-based autowiring, circular-dependency detection, and worker-mode resettability. Core services (the PSR-11 ContainerInterface itself) are locked from override after registration.
π¦ Installation
composer require waffle-commons/container
π§± Surface
| Class | Role |
|---|---|
Waffle\Commons\Container\Container |
The container. Implements Waffle\Commons\Contracts\Container\ContainerInterface (PSR-11 + ResettableInterface). |
Waffle\Commons\Container\Autowire |
Reflection-based autowiring helper used by Container::build() to resolve constructor parameters. |
Waffle\Commons\Container\Exception\ContainerException |
Thrown for retrieval / resolution failures. |
Waffle\Commons\Container\Exception\NotFoundException |
Thrown when get($id) cannot resolve the identifier. |
π Usage
use Waffle\Commons\Container\Container; $container = new Container([ // Direct instance LoggerInterface::class => new StreamLogger(), // Class string β autowired via reflection on first get() UserService::class => UserService::class, // Factory closure 'app.config' => static fn() => new Config(__DIR__ . '/config', 'prod'), ]); $logger = $container->get(LoggerInterface::class); $exists = $container->has(UserService::class); $container->set('db.cache', new ArrayCache());
The exact public signature, verbatim from Waffle\Commons\Contracts\Container\ContainerInterface:
public function get(string $id): mixed; public function has(string $id): bool; public function set(string $id, object|callable|string $concrete): void; public function reset(): void; // from ResettableInterface
π Worker-mode reset
Container implements ResettableInterface. After each request, the kernel calls reset() so the container drops its instance cache while keeping the registered definitions, preventing user-context leaks across FrankenPHP worker requests.
π‘οΈ Locked core services
The constant Container::CORE_SERVICES lists identifiers that must not be redefined once registered. The PSR-11 ContainerInterface itself is in that list β any attempt to override it after the container is built throws ContainerException.
π Circular-dependency detection
Container::get($id) tracks the resolution stack in $resolving and throws ContainerException if a cycle is detected before infinite recursion can occur.
π PHP 8.5 features used
final class Containerβ no subclassing.- Typed properties throughout.
- Typed constants for service registries:
private const CORE_SERVICES = [...];. #[\Override]on every method that overrides PSR-11.
π§ Architectural boundary (mago guard)
An active dependency perimeter is enforced on every CI run by vendor/bin/mago guard (bundled into composer mago; zero baselines). The rules live in mago.toml under [guard.perimeter] β a forbidden use statement fails the build, not a reviewer.
Production code under Waffle\Commons\Container may depend only on:
Waffle\Commons\Container\**β itselfWaffle\Commons\Contracts\**β the shared contracts package, the only Waffle dependency permittedPsr\**β PSR interfaces (PSR-11)@global+Psl\**β PHP core and the PHP Standard Library
Test code under WaffleTests\Commons\Container is unrestricted (@all). Structural rules are guarded too: interfaces must be named *Interface, Exception\** classes must end in *Exception, and any Enum\** namespace may hold only enum declarations.
Contract-first, component-agnostic by construction: components compose through waffle-commons/contracts, never directly through one another.
π§ͺ Testing
docker exec -w /waffle-commons/container waffle-dev composer tests
π Documentation
Full guides live in the central DiΓ‘taxis documentation tree:
π License
MIT β see LICENSE.md.