PSR-11 defines a common interface for dependency injection containers in PHP. This standardization allows libraries to retrieve services from any container implementation, promoting better interoperability between different frameworks and libraries.
Understanding Dependency Injection Containers
A dependency injection container (DIC) is responsible for:
- Managing service definitions
- Creating service instances
- Resolving dependencies
- Managing object lifecycle
The Container Interface
<?php
namespace JonesRussell\PhpFigGuide\PSR11;
interface ContainerInterface
{
/**
* Finds an entry of the container by its identifier and returns it.
*
* @param string $id
* @return mixed
* @throws NotFoundExceptionInterface
* @throws ContainerExceptionInterface
*/
public function get($id);
/**
* Returns true if the container can return an entry for the given identifier.
*
* @param string $id
* @return bool
*/
public function has($id);
}
Basic Implementation
Here’s a simple implementation of a dependency injection container that adheres to PSR-11:
<?php
namespace JonesRussell\PhpFigGuide\PSR11;
class SimpleContainer implements ContainerInterface
{
private array $services = [];
public function set(string $id, $service): void
{
$this->services[$id] = $service;
}
public function get($id)
{
if (!$this->has($id)) {
throw new class extends \Exception implements NotFoundExceptionInterface {};
}
return $this->services[$id];
}
public function has($id): bool
{
return isset($this->services[$id]);
}
}
// Example usage
$container = new SimpleContainer();
$container->set('database', new DatabaseConnection());
$database = $container->get('database');
Best Practices
- Service Resolution
// Bad - Service locator pattern
class UserService
{
public function __construct(private ContainerInterface $container) {}
public function doSomething()
{
$dep = $this->container->get('some.service');
}
}
// Good - Explicit dependency injection
class UserService
{
public function __construct(
private SomeServiceInterface $someService
) {}
}
- Container Configuration
// Bad - Runtime service definition
if ($condition) {
$container->set('service', new ServiceA());
} else {
$container->set('service', new ServiceB());
}
// Good - Configuration-driven definition
$container->set('service', function (ContainerInterface $c) {
return $c->get('config')->get('use_service_a')
? new ServiceA()
: new ServiceB();
});
Next Steps
In our next post, we’ll explore PSR-14, which defines a standard event dispatcher interface. Check out our example repository for the implementation of these standards.
Resources
Baamaapii 👋