Skip to content

Instantly share code, notes, and snippets.

@triplepoint
Last active July 24, 2019 12:06
Show Gist options
  • Save triplepoint/9258097 to your computer and use it in GitHub Desktop.
Save triplepoint/9258097 to your computer and use it in GitHub Desktop.
Simple demonstration of dependency injection with a container in PHP.
<?php
// --- Library classes that provide behaviors ---
/**
* In these examples:
* - The Controller needs a view renderer
* - The view renderer needs a Database and a Logger
*
* Note, that the interfaces aren't being defined here, just assume they're what you'd expect.
*/
class Database implements DatabaseInterface {
// Some stuff here that makes database stuff go.
}
class Logger implements LoggerInterface {
// Some stuff here that makes logging stuff go.
}
class ViewRenderer implements ViewRendererInterface {
protected $database; // Let's assume this needs a database as a property for something.
protected $logger; // Let's assume this needs a logger as a property for something.
public function __construct(DatabaseInterface $db, LoggerInterface $logger) {
$this->database = $db;
$this->logger = $logger;
}
// Some stuff here that makes view rendering stuff happen, and uses the database and logger properties when doing it.
}
class Controller implements ControllerInterface {
protected $viewRenderer; // Lets assume this needs the view renderer and nothing else, for whatever reason.
public function __construct(ViewRendererInterface $viewRenderer) {
$this->viewRenderer = $viewRenderer;
}
// Some stuff that makes application things happen, and uses the view renderer property.
}
// --- Later, in your application code ---
// This is a simple dependency injection container, that knows how to wire up all these dependencies.
// Note that each call on one of the get*() methods will return a new object, but it wouldn't be hard to imagine
// a process for returning a singleton object on subsequent calls on each method, if that's what you wanted.
class ProductionDependencyInjectionContainer
{
public function getController() {
$viewRenderer = $this->getViewRenderer();
return new Controller($viewRenderer);
}
public function getViewRenderer() {
$db = $this->getDatabase();
$logger = $this->getLogger();
return new ViewRenderer($db, $logger);
}
public function getDatabase() {
return new Database;
}
public function getLogger() {
return new Logger;
}
}
// And now, in the actual application code, we can instantitate the DI container, and get the controller,
// without caring at all about what the dependencies are or how they're handled.
$dic = new ProductionDependencyInjectionContainer;
$controller = $dic->getController();
// And here's where the application starts doing stuff.
$controller->doStuff();
/**
* So note how easy it would be to write another DI container that replaces the production logger with a mocked up
* logger, or the production database with a mocked up database. Or swaps out configuration values like database
* passwords or some such.
*
* The point is that the individual library classes don't know or care which classes are being used to fulfill their interface
* requirements; they're not doing anything like $db = new Database themselves. Their dependencies are being
* passed in from the outside at instantiation.
*
* This pays of in testability (mock dependencies can be used to isolate and instrument a class's behavior), and also
* in modularity (new loggers can be swapped in later, and all you end up changing is the wiring in the DI container).
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment