Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dependency Injection - PHPUG Cologne 2013/8

Dependency Injection - PHPUG Cologne 2013/8

In this talk, I introduce the concept of Dependency Injection as well as Pimple as an example for a Dependency Injection Container. I also talk about one thing that you absolutely do *not* want to do when using Dependency Injection Containers.

Tobias Gies

August 02, 2013
Tweet

More Decks by Tobias Gies

Other Decks in Programming

Transcript

  1. class Foo { private $db; private $memcache; public function __construct()

    { $this->db = new DatabaseConnection(); $this->memcache = new MemcacheClient(); } }
  2. class Foo { private $db; private $memcache; public function __construct(

    DatabaseConnection $db, MemcacheClient $memcache ) { $this->db = $db; $this->memcache = $memcache; } }
  3. class Foo { private $db; private $memcache; public function __construct(

    DatabaseConnectionInterface $db, CacheClientInterface $memcache ) { $this->db = $db; $this->memcache = $memcache; } }
  4. use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; class Bar { private $logger; public

    function __construct() { $this->logger = new NullLogger(); } public function setLogger(LoggerInterface $logger) { $this->logger = $logger; } }
  5. Dependency Injection • Use constructor injection for required dependencies –

    services your class needs to run • Use setter injection for optional dependencies – services your class may use if available • Program against interfaces, not concrete implementations!
  6. $foo = new Foo(); // fatal error $db = new

    DatabaseConnection(); $memcache = new MemcacheClient(); $foo = new Foo($db, $memcache); // works
  7. Advantages • Dependencies of classes are obvious, your IDE will

    point them out to you • Dependencies can easily be replaced • Easy replacement also means easier to test
  8. // Create a new instance of Foo $db = new

    DatabaseConnection(); $memcache = new MemcacheClient(); $foo = new Foo($db, $memcache);
  9. // Create a new instance of Foo $mysqli = new

    mysqli(); $db = new DatabaseConnection($mysqli); $memcache = new MemcacheClient(); $foo = new Foo($db, $memcache);
  10. // Create a new instance of Foo list($host, $user, $pw,

    $db) = getDbConfig(); $mysqli = new mysqli($host, $user, $pw, $db); $db = new DatabaseConnection($mysqli); $memcache = new MemcacheClient(); $foo = new Foo($db, $memcache);
  11. // Create a new instance of Foo list($host, $user, $pw,

    $db) = getDbConfig(); $mysqli = new mysqli($host, $user, $pw, $db); $db = new DatabaseConnection($mysqli); list($mcHost, $mcPort) = getMemcacheConfig(); $memcache = new MemcacheClient($mcHost, $mcPort); $foo = new Foo($db, $memcache);
  12. $dic = new Pimple(); $dic['mysql.config'] = getDbConfig(); $dic['mysqli'] = new

    mysqli(/* ... */); $dic['db'] = new DatabaseConnection($dic['mysqli']);
  13. $dic = new Pimple(); $dic['mysql.config'] = getDbConfig(); $dic['mysqli'] = function($dic)

    { return new mysqli(/* ... */); }; $dic['db'] = function($dic) { return new DatabaseConnection($dic['mysqli']); };
  14. // DIC setup for db and cache here... $dic['foo'] =

    function($dic) { return new Foo($dic['db'], $dic['cache']); }; // Later in your application: $foo = $dic['foo']; var_dump($foo instanceof Foo); // true
  15. Lazy initialisation in Pimple • Pimple::offsetGet() calls closure we saved

    as $dic['foo'] • Closure needs $dic['db'] and $dic['cache'], means more calls to Pimple::offsetGet() • Cascade closure / Pimple::offsetGet() calls until dependencies are resolved
  16. More Pimple features // To have one global instance of

    a class: $dic['example'] = $dic->share(function($dic) { return new Example(); }); // To return the closure instead of executing it: $dic['closure'] = $dic->protect(function() { // Do cool stuff here });
  17. DI Container • Knows the services that are available in

    your application • Knows how to create them using Builders • Creates them as soon as you need them
  18. class Controller { private $dic; public function __construct(Pimple $dic) {

    $this->dic = $dic; } public function doSomething() { $foo = $this->dic['foo']; // Do something here } }
  19. $dic = new Pimple(); $controller = new Controller($dic); $controller->doSomething(); //

    InvalidArgumentException: // - Identifier "foo" is not defined.
  20. class Controller { private $foo; public function __construct(Foo $foo) {

    $this->foo = $foo; } public function doSomething() { // Do something here } }
  21. $dic = new Pimple(); // DIC setup here... $name =

    determineControllerFromRequest(); $controller = $dic[$name]; echo $controller->doSomething();
  22. Don't abuse the DIC • Do not hide your classes'

    dependencies by passing the DIC (or any other Service Locator) to them • You get to pull exactly ONE object out of the DIC yourself: The controller that needs to be executed.