Last active
July 24, 2019 12:06
-
-
Save triplepoint/9258097 to your computer and use it in GitHub Desktop.
Simple demonstration of dependency injection with a container in PHP.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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