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.

Avatar for Tobias Gies

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.