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. Dependency Injection

    View Slide

  2. inject dependencies!

    View Slide

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

    View Slide

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

    View Slide

  5. class Foo
    {
    private $db;
    private $memcache;
    public function __construct(
    DatabaseConnectionInterface $db,
    CacheClientInterface $memcache
    )
    {
    $this->db = $db;
    $this->memcache = $memcache;
    }
    }

    View Slide

  6. Optional dependencies

    View Slide

  7. 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;
    }
    }

    View Slide

  8. 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!

    View Slide

  9. That's it!

    View Slide

  10. Advantages

    View Slide

  11. $foo = new Foo(); // fatal error
    $db = new DatabaseConnection();
    $memcache = new MemcacheClient();
    $foo = new Foo($db, $memcache); // works

    View Slide

  12. $db = new PostgreSqlConnection();
    $memcache = new MemcacheClient();
    $foo = new Foo($db, $memcache);

    View Slide

  13. $db = new DatabaseConnectionMock();
    $memcache = new CacheClientMock();
    $foo = new Foo($db, $memcache);

    View Slide

  14. 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

    View Slide

  15. Problems?

    View Slide

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

    View Slide

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

    View Slide

  18. // 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);

    View Slide

  19. // 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);

    View Slide

  20. Problems?

    Construction can turn into a major PITA.

    View Slide

  21. DI Container
    To the rescue!

    View Slide

  22. DIC Example: Pimple

    http://pimple.sensiolabs.org

    MIT License

    Single-class solution

    About 50 SLOC

    View Slide

  23. $dic = new Pimple();
    $dic['mysqli.config'] = getDbConfig();

    View Slide

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

    View Slide

  25. Lazy initialisation
    Watch closely, here's where the magic happens.

    View Slide

  26. View Slide

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

    View Slide

  28. // 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

    View Slide

  29. 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

    View Slide

  30. 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
    });

    View Slide

  31. 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

    View Slide

  32. Cool!
    So now I can do this:

    View Slide

  33. class Controller
    {
    private $dic;
    public function __construct(Pimple $dic)
    {
    $this->dic = $dic;
    }
    public function doSomething()
    {
    $foo = $this->dic['foo'];
    // Do something here
    }
    }

    View Slide

  34. NOPE!

    View Slide

  35. $dic = new Pimple();
    $controller = new Controller($dic);
    $controller->doSomething();
    // InvalidArgumentException:
    // - Identifier "foo" is not defined.

    View Slide

  36. Stop hiding your
    dependencies!

    View Slide

  37. class Controller
    {
    private $foo;
    public function __construct(Foo $foo)
    {
    $this->foo = $foo;
    }
    public function doSomething()
    {
    // Do something here
    }
    }

    View Slide

  38. $dic = new Pimple();
    // DIC setup here...
    $name = determineControllerFromRequest();
    $controller = $dic[$name];
    echo $controller->doSomething();

    View Slide

  39. 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.

    View Slide

  40. Thank you!
    Any questions?

    View Slide