Application
Dependency Injection
Assemble a small application composition root with a PSR-11 container when a consumer does not provide its own framework container.
The container is a small Application-level PSR-11 implementation for an explicit composition root. Use it where a consumer needs direct portable wiring; when a framework supplies its own container, keep framework configuration and Adapter registration in that consumer rather than treating this class as a framework integration.
Ownership. Application\Service\Container and its PSR-11 exception are Application code. A
consumer owns service IDs, factories, framework bindings, secrets, lifecycle policy, and the choice
to use a framework container instead. The package does not provide autowiring, reflection-based
resolution, or an Adapter configuration format.
Dependencies. The container requires PHP 8.5+ and the required psr/container package. No
framework dependency is required.
Install.
Start with an explicit application composition root.
use Fight\Common\Application\Service\Container;
$container = new Container();
$container->set('clock', fn (): SystemClock => new SystemClock());
$container->factory('request-id', fn (): string => bin2hex(random_bytes(16)));
$clock = $container->get('clock'); // shared within this registration
$requestId = $container->get('request-id'); // regenerated on every get()
get() only resolves registered services: a missing ID throws
Fight\Common\Application\Service\Exception\NotFoundException, which implements PSR-11's
NotFoundExceptionInterface. ArrayAccess and parameter methods are a separate key-value store,
not aliases for services. Keep credentials out of source and provide them through the consuming
application's secure configuration boundary.
Reference¶
A lightweight PSR-11-compatible Application container for explicit composition roots. It is not an Adapter or framework configuration layer: a consumer using Symfony or another framework should make its own composition choice and keep that framework's bindings at its boundary.
Application\Service
├── Container (implements PSR-11 ContainerInterface + ArrayAccess)
└── Exception\NotFoundException (implements PSR-11 NotFoundExceptionInterface)
Table of Contents¶
- When to Use This Container
- PSR-11 Compliance
- Services (Shared)
- Factories (Prototype)
- Parameters
- ArrayAccess
- Complete Example
- NotFoundException
When to Use This Container¶
- Stand-alone CLI scripts or daemons that need simple, explicit wiring
- Small application roots that choose not to use a framework container
- Test or integration composition where direct factories make dependencies clear
- Not a replacement for consumer-owned framework configuration — a framework application may use its own container instead
PSR-11 Compliance¶
Fight\Common\Application\Service\Container implements Psr\Container\ContainerInterface:
get() throws NotFoundException (implements Psr\Container\NotFoundExceptionInterface) when the requested service ID has not been registered.
Services (Shared)¶
set(string $id, callable $factory): void
The factory receives the container as its only argument. The return value is cached — subsequent get($id) calls return the same instance.
use Fight\Common\Application\Service\Container;
$container = new Container();
$container->set('logger', function (Container $c): LoggerInterface {
return new Monolog\Logger('app');
});
$logger = $container->get('logger'); // same instance every time
Use set() for objects that should be singletons within the container: database connections, repositories, routers, mailers, etc.
Factories (Prototype)¶
factory(string $id, callable $factory): void
Same signature as set(), but the factory is invoked fresh on every get() call.
$container->factory('request', fn ($c) => Request::fromGlobals());
$req1 = $container->get('request'); // new instance
$req2 = $container->get('request'); // different instance
Use factory() for objects that must not be shared: request objects, command instances, or any stateful value that should be created anew each time.
Parameters¶
Simple key-value store completely separate from services. Parameters do not overlap with get()/has().
$container->setParameter('db.host', 'localhost');
$container->setParameter('db.name', 'myapp');
$container->setParameter('db.port', 3306);
$container->getParameter('db.host'); // 'localhost'
$container->getParameter('db.port', 5432); // 3306 (default unused)
$container->getParameter('db.user', 'root'); // 'root' (no such parameter)
$container->hasParameter('db.host'); // true
$container->removeParameter('db.host');
ArrayAccess¶
Container implements ArrayAccess, which delegates to the parameter store:
$container['db.host'] = 'localhost';
echo $container['db.host']; // 'localhost'
isset($container['db.host']); // true
unset($container['db.host']);
This is a convenience shorthand for parameter access — it does not interact with set()/get() services.
Complete Example¶
A stand-alone application wired through the container:
use Fight\Common\Application\Service\Container;
$c = new Container();
// --- Parameters ---
$c->setParameter('db.host', 'localhost');
$c->setParameter('db.name', 'myapp');
$c->setParameter('db.user', 'root');
$c->setParameter('db.pass', 'secret');
// --- Shared services ---
$c->set('pdo', function (Container $c): PDO {
return new PDO(
sprintf(
'mysql:host=%s;dbname=%s',
$c->getParameter('db.host'),
$c->getParameter('db.name')
),
$c->getParameter('db.user'),
$c->getParameter('db.pass')
);
});
$c->set('user_repository', function (Container $c): UserRepository {
return new UserRepository($c->get('pdo'));
});
$c->set('mailer', function (Container $c): MailerInterface {
return new SmtpMailer('smtp.example.com', 587);
});
// --- Prototype factories ---
$c->factory('request', fn (Container $c) => Request::fromGlobals());
// --- Usage ---
$repo = $c->get('user_repository');
$users = $repo->findAll();
$mailer = $c->get('mailer');
$mailer->send(new WelcomeEmail($users[0]));
NotFoundException¶
Fight\Common\Application\Service\Exception\NotFoundException
Extends \Exception and implements Psr\Container\NotFoundExceptionInterface. Thrown when get() is called with a service ID that has not been registered via set() or factory().
use Fight\Common\Application\Service\Exception\NotFoundException;
try {
$c->get('nonexistent');
} catch (NotFoundException $e) {
echo $e->getMessage(); // "Service 'nonexistent' not found."
}
Note that getParameter() does not throw — it returns the provided default (or null) for missing keys.