Dependency Injection
Purpose
The purpose of the design pattern
To implement a loosely coupled architecture in order to get better testable, maintainable and extendable code.
Examples
Examples of how the design pattern can be used
- The Doctrine2 ORM uses dependency injection e.g. for configuration that is injected into a Connection object. For testing purposes, one can easily create a mock object of the configuration and inject that into the Connection object
- Many frameworks already have containers for DI that create objects via a configuration array and inject them where needed (i.e. in Controllers)
UML
UML design pattern diagram

Code
Code snippets
DatabaseConfiguration
The configuration class - ideally this needs to implement an Interface with the Interface being type hinted on the DatabaseConnection class.
namespace DesignPatterns\Structural\DependencyInjection;
class DatabaseConfiguration
{
public function __construct(
private string $host,
private int $port,
private string $username,
private string $password
) { }
public function getHost(): string
{
return $this->host;
}
public function getPort(): int
{
return $this->port;
}
public function getUsername(): string
{
return $this->username;
}
public function getPassword(): string
{
return $this->password;
}
}
DatabaseConnection
All you need to look at here really is this line:
public function __construct(private DatabaseConfiguration $configuration)
The database configuration is being injected into the database connection class, which means we could potentially pass in a different configuration if needed (which is useful for testing etc). If we didn’t do this, we’d have to amend the DatabaseConnection some other way, and it can get messy!
Note - we're using PHP 8 property promotion.
public function __construct(private DatabaseConfiguration $configuration)
The database configuration is being injected into the database connection class, which means we could potentially pass in a different configuration if needed (which is useful for testing etc). If we didn’t do this, we’d have to amend the DatabaseConnection some other way, and it can get messy!
Note - we're using PHP 8 property promotion.
namespace DesignPatterns\Structural\DependencyInjection;
class DatabaseConnection
{
public function __construct(private DatabaseConfiguration $configuration)
{
}
public function getDsn(): string
{
// this is just for the sake of demonstration, not a real DSN
// notice that only the injected config is used here, so there is
// a real separation of concerns here
return sprintf(
'%s:%s@%s:%d',
$this->configuration->getUsername(),
$this->configuration->getPassword(),
$this->configuration->getHost(),
$this->configuration->getPort()
);
}
}
Ready to bring your vision to life?
We believe in excellence, empathy, integrity, and transparency throughout the process. Our goal is to build fast, responsive websites that not only perform but also reflect your values and vision.