dependency injection - phpday 2010
DESCRIPTION
TRANSCRIPT
![Page 1: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/1.jpg)
Dependency Injection with PHP 5.3
Fabien Potencier
![Page 2: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/2.jpg)
Fabien Potencier
Serial entrepreneur Developer by passion Founder of Sensio Creator and lead developer of Symfony On Twitter @fabpot On github http://www.github.com/fabpot Blog http://fabien.potencier.org/
![Page 3: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/3.jpg)
Dependency Injection
A real world « web » example
![Page 4: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/4.jpg)
In most web applications, you need to manage the user preferences
– The user language – Whether the user is authenticated or not – The user credentials – …
![Page 5: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/5.jpg)
This can be done with a User object
– setLanguage(), getLanguage() – setAuthenticated(), isAuthenticated() – addCredential(), hasCredential() – ...
![Page 6: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/6.jpg)
The User information need to be persisted
between HTTP requests
We use the PHP session for the Storage
![Page 7: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/7.jpg)
class SessionStorage { function __construct($cookieName = 'PHP_SESS_ID') { session_name($cookieName); session_start(); }
function set($key, $value) { $_SESSION[$key] = $value; }
// ... }
![Page 8: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/8.jpg)
class User { protected $storage;
function __construct() { $this->storage = new SessionStorage(); }
function setLanguage($language) { $this->storage->set('language', $language); }
// ... }
$user = new User();
Very easy to use
Very hard to
customize
![Page 9: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/9.jpg)
class User { protected $storage;
function __construct($storage) { $this->storage = $storage; } }
$storage = new SessionStorage(); $user = new User($storage);
Slightly more
difficult to use
Very easy to
customize
![Page 10: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/10.jpg)
That’s Dependency Injection
Nothing more
![Page 11: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/11.jpg)
Let’s understand why the first example is not a good idea
![Page 12: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/12.jpg)
I want to change the session cookie name
![Page 13: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/13.jpg)
class User { protected $storage;
function __construct() { $this->storage = new SessionStorage('SESSION_ID'); }
function setLanguage($language) { $this->storage->set('language', $language); }
// ... }
$user = new User();
Hardcode it in the
User class
![Page 14: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/14.jpg)
class User { protected $storage;
function __construct() { $this->storage = new SessionStorage(STORAGE_SESSION_NAME); } }
define('STORAGE_SESSION_NAME', 'SESSION_ID');
$user = new User();
Add a global
configuration?
![Page 15: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/15.jpg)
class User { protected $storage;
function __construct($sessionName) { $this->storage = new SessionStorage($sessionName); } }
$user = new User('SESSION_ID');
Configure via
User?
![Page 16: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/16.jpg)
class User { protected $storage;
function __construct($storageOptions) { $this->storage = new SessionStorage($storageOptions['session_name']);
$user = new User( array('session_name' => 'SESSION_ID') );
Configure with an
array?
![Page 17: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/17.jpg)
I want to change the session storage implementation
Filesystem MySQL
Memcached …
![Page 18: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/18.jpg)
class User { protected $storage;
function __construct() { $this->storage = Registry::get('session_storage'); } }
$storage = new SessionStorage(); Registry::set('session_storage', $storage); $user = new User();
Use a global
registry object?
![Page 19: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/19.jpg)
Now, the User depends on the Registry
![Page 20: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/20.jpg)
Instead of harcoding the Storage dependency
inside the User class constructor
Inject the Storage dependency in the User object
![Page 21: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/21.jpg)
class User { protected $storage;
function __construct($storage) { $this->storage = $storage; } }
$storage = new SessionStorage('SESSION_ID'); $user = new User($storage);
![Page 22: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/22.jpg)
What are the advantages?
![Page 23: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/23.jpg)
Use different Storage strategies
![Page 24: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/24.jpg)
class User { protected $storage;
function __construct($storage) { $this->storage = $storage; } }
$storage = new MySQLSessionStorage('SESSION_ID'); $user = new User($storage);
Use a different
Storage engine
![Page 25: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/25.jpg)
Configuration becomes natural
![Page 26: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/26.jpg)
class User { protected $storage;
function __construct($storage) { $this->storage = $storage; } }
$storage = new MySQLSessionStorage('SESSION_ID'); $user = new User($storage);
Configuration
is natural
![Page 27: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/27.jpg)
Wrap third-party classes (Interface / Adapter)
![Page 28: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/28.jpg)
class User { protected $storage;
function __construct(SessionStorageInterface $storage) { $this->storage = $storage; } }
interface SessionStorageInterface { function get($key);
function set($key, $value); }
Add an interface
![Page 29: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/29.jpg)
Mock the Storage object (for testing)
![Page 30: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/30.jpg)
class User { protected $storage;
function __construct(SessionStorageInterface $storage) { $this->storage = $storage; } }
class SessionStorageForTests implements SessionStorageInterface { protected $data = array();
static function set($key, $value) { self::$data[$key] = $value; } }
Mock the Session
![Page 31: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/31.jpg)
Use different Storage strategies
Configuration becomes natural
Wrap third-party classes (Interface / Adapter)
Mock the Storage object (for testing)
Easy without changing the User class
![Page 32: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/32.jpg)
« Dependency Injection is where components are given their dependencies
through their constructors, methods, or directly into fields. »
http://www.picocontainer.org/injection.html
![Page 33: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/33.jpg)
$storage = new SessionStorage();
// constructor injection $user = new User($storage);
// setter injection $user = new User(); $user->setStorage($storage);
// property injection $user = new User(); $user->storage = $storage;
![Page 34: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/34.jpg)
A slightly more complex web example
![Page 35: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/35.jpg)
$request = new Request(); $response = new Response();
$storage = new FileSessionStorage('SESSION_ID'); $user = new User($storage);
$cache = new FileCache( array('dir' => dirname(__FILE__).'/cache') ); $routing = new Routing($cache);
![Page 36: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/36.jpg)
class Application { function __construct() { $this->request = new WebRequest(); $this->response = new WebResponse();
$storage = new FileSessionStorage('SESSION_ID'); $this->user = new User($storage);
$cache = new FileCache( array('dir' => dirname(__FILE__).'/cache') ); $this->routing = new Routing($cache); } }
$application = new Application();
![Page 37: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/37.jpg)
Back to square 1
![Page 38: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/38.jpg)
class Application { function __construct() { $this->request = new WebRequest(); $this->response = new WebResponse();
$storage = new FileSessionStorage('SESSION_ID'); $this->user = new User($storage);
$cache = new FileCache( array('dir' => dirname(__FILE__).'/cache') ); $this->routing = new Routing($cache); } }
$application = new Application();
![Page 39: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/39.jpg)
We need a Container
Describes objects and their dependencies
Instantiates and configures objects on-demand
![Page 40: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/40.jpg)
A container SHOULD be able to manage
ANY PHP object (POPO)
The objects MUST not know that they are managed
by a container
![Page 41: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/41.jpg)
• Parameters – The SessionStorageInterface implementation we want to use (the class name) – The session name
• Objects – SessionStorage – User
• Dependencies – User depends on a SessionStorageInterface implementation
![Page 42: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/42.jpg)
Let’s build a simple container with PHP 5.3
![Page 43: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/43.jpg)
DI Container
Managing parameters
![Page 44: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/44.jpg)
class Container { protected $parameters = array();
public function setParameter($key, $value) { $this->parameters[$key] = $value; }
public function getParameter($key) { return $this->parameters[$key]; } }
![Page 45: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/45.jpg)
$container = new Container(); $container->setParameter('session_name', 'SESSION_ID'); $container->setParameter('storage_class', 'SessionStorage');
$class = $container->getParameter('storage_class'); $sessionStorage = new $class($container->getParameter('session_name')); $user = new User($sessionStorage);
Decoupling
Customization
Objects creation
![Page 46: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/46.jpg)
class Container { protected $parameters = array();
public function __set($key, $value) { $this->parameters[$key] = $value; }
public function __get($key) { return $this->parameters[$key]; } }
Using PHP
magic methods
![Page 47: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/47.jpg)
$container = new Container(); $container->session_name = 'SESSION_ID'; $container->storage_class = 'SessionStorage';
$sessionStorage = new $container->storage_class($container->session_name); $user = new User($sessionStorage);
Interface
is cleaner
![Page 48: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/48.jpg)
DI Container
Managing objects
![Page 49: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/49.jpg)
We need a way to describe how to create objects, without actually instantiating anything!
Anonymous functions to the rescue!
![Page 50: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/50.jpg)
Anonymous Functions / Lambdas
A lambda is a function defined on the fly
with no name
function () { echo 'Hello world!'; };
![Page 51: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/51.jpg)
Anonymous Functions / Lambdas
A lambda can be stored in a variable
$hello = function () { echo 'Hello world!'; };
![Page 52: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/52.jpg)
Anonymous Functions / Lambdas
And then it can be used as any other PHP callable
$hello();
call_user_func($hello);
![Page 53: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/53.jpg)
Anonymous Functions / Lambdas
You can also pass a lambda as an argument to a function or method
function foo(Closure $func) { $func(); }
foo($hello);
![Page 54: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/54.jpg)
Fonctions anonymes $hello = function ($name) { echo 'Hello '.$name; };
$hello('Fabien');
call_user_func($hello, 'Fabien');
function foo(Closure $func, $name) { $func($name); }
foo($hello, 'Fabien');
![Page 55: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/55.jpg)
DI Container
Managing objects
![Page 56: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/56.jpg)
class Container { protected $parameters = array(); protected $objects = array();
public function __set($key, $value) { $this->parameters[$key] = $value; }
public function __get($key) { return $this->parameters[$key]; }
public function setService($key, Closure $service) { $this->objects[$key] = $service; }
public function getService($key) { return $this->objects[$key]($this); } }
Store a lambda
able to create the
object on-demand
Ask the closure to create
the object and pass the
current Container
![Page 57: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/57.jpg)
$container = new Container(); $container->session_name = 'SESSION_ID'; $container->storage_class = 'SessionStorage'; $container->setService('user', function ($c) { return new User($c->getService('storage')); }); $container->setService('storage', function ($c) { return new $c->storage_class($c->session_name); });
$user = $container->getService('user');
Creating the User
is now as easy as before
Description
![Page 58: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/58.jpg)
class Container { protected $values = array();
function __set($id, $value) { $this->values[$id] = $value; }
function __get($id) { if (is_callable($this->values[$id])) { return $this->values[$id]($this); } else { return $this->values[$id]; } } }
Simplify the code
![Page 59: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/59.jpg)
$container = new Container(); $container->session_name = 'SESSION_ID'; $container->storage_class = 'SessionStorage'; $container->user = function ($c) { return new User($c->storage); }; $container->storage = function ($c) { return new $c->storage_class($c->session_name); };
$user = $container->user;
Unified interface
![Page 60: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/60.jpg)
DI Container
Scope
![Page 61: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/61.jpg)
For some objects, like the user, the container must always return the same instance
![Page 62: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/62.jpg)
spl_object_hash($container->user)
!== spl_object_hash($container->user)
![Page 63: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/63.jpg)
$container->user = function ($c) { static $user;
if (is_null($user)) { $user = new User($c->storage); }
return $user; };
![Page 64: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/64.jpg)
spl_object_hash($container->user)
=== spl_object_hash($container->user)
![Page 65: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/65.jpg)
$container->user = $container->asShared(function ($c) { return new User($c->storage); });
![Page 66: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/66.jpg)
A closure is a lambda that remembers the context
of its creation…
![Page 67: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/67.jpg)
class Article { public function __construct($title) { $this->title = $title; }
public function getTitle() { return $this->title; } }
$articles = array( new Article('Title 1'), new Article('Title 2'), );
![Page 68: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/68.jpg)
$mapper = function ($article) { return $article->getTitle(); };
$titles = array_map($mapper, $articles);
![Page 69: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/69.jpg)
$method = 'getTitle';
$mapper = function ($article) use($method) { return $article->$method(); };
$method = 'getAuthor';
$titles = array_map($mapper, $articles);
![Page 70: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/70.jpg)
$mapper = function ($method) { return function ($article) use($method) { return $article->$method(); }; };
![Page 71: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/71.jpg)
$titles = array_map($mapper('getTitle'), $articles);
$authors = array_map($mapper('getAuthor'), $articles);
![Page 72: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/72.jpg)
$container->user = $container->asShared(function ($c) { return new User($c->storage); });
![Page 73: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/73.jpg)
function asShared(Closure $lambda) { return function ($container) use ($lambda) { static $object;
if (is_null($object)) { $object = $lambda($container); } return $object; }; }
![Page 74: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/74.jpg)
class Container { protected $values = array();
function __set($id, $value) { $this->values[$id] = $value; }
function __get($id) { if (!isset($this->values[$id])) { throw new InvalidArgumentException(sprintf('Value "%s" is not defined.', $id)); }
if (is_callable($this->values[$id])) { return $this->values[$id]($this); } else { return $this->values[$id]; } } }
Error management
![Page 75: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/75.jpg)
class Container { protected $values = array();
function __set($id, $value) { $this->values[$id] = $value; }
function __get($id) { if (!isset($this->values[$id])) { throw new InvalidArgumentException(sprintf('Value "%s" is not defined.', $id)); }
if (is_callable($this->values[$id])) { return $this->values[$id]($this); } else { return $this->values[$id]; } }
function asShared($callable) { return function ($c) use ($callable) { static $object;
if (is_null($object)) { $object = $callable($c); } return $object; }; } }
40 LOC for a fully-
featured container
![Page 76: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/76.jpg)
I’m NOT advocating the usage of lambdas everywhere
This presentation is about showing how they work
on practical examples
![Page 77: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/77.jpg)
A DI Container does NOT manage ALL your objects
![Page 78: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/78.jpg)
Good rule of thumb: It manages “Global” objects
Objects with only one instance (!= Singletons)
![Page 79: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/79.jpg)
LIKE a User, a Request,
a database Connection, a Logger, …
![Page 80: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/80.jpg)
UNLIKE Model objects (a Product, a blog Post, …)
![Page 81: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/81.jpg)
Symfony Components Dependency Injection
![Page 82: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/82.jpg)
Rock-solid implementation of a DIC in PHP 5.3
![Page 83: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/83.jpg)
At the core of the Symfony 2.0 framework
… which is one of the fastest framework
![Page 84: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/84.jpg)
Very flexible
Configuration in PHP, XML, YAML, or INI
![Page 85: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/85.jpg)
$container = new Builder();
$container->register('output', 'FancyOutput'); $container-> register('message', 'Message')-> setArguments(array( new sfServiceReference('output'), array('with_newline' => true) )) ;
$container->message->say('Hello World!');
![Page 86: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/86.jpg)
services: output: { class: FancyOutput } message: class: Message arguments: - @output - { with_newline: true }
![Page 87: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/87.jpg)
<container xmlns="http://symfony-project.org/schema/dic/services"> <services> <service id="output" class="FancyOutput" />
<service id="message" class="Message"> <argument type="service" id="output" /> <argument type="collection"> <argument key="with_newline">true</argument> </argument> </service> </services> </container>
![Page 88: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/88.jpg)
<container xmlns="http://symfony-project.org/schema/dic/services"> <import resource="parameters.yml" /> <import resource="parameters.ini" /> <import resource="services.xml" /> </container>
imports: - { resource: parameters.yml } - { resource: parameters.ini } - { resource: services.xml }
![Page 89: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/89.jpg)
As fast as it can be
The container can be “compiled” down to plain PHP code
![Page 90: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/90.jpg)
use Symfony\Components\DependencyInjection\Container; use Symfony\Components\DependencyInjection\Reference; use Symfony\Components\DependencyInjection\Parameter;
class ProjectServiceContainer extends Container { protected $shared = array();
protected function getOutputService() { if (isset($this->shared['output'])) return $this->shared['output'];
$instance = new FancyOutput();
return $this->shared['output'] = $instance; }
protected function getMessageService() { if (isset($this->shared['message'])) return $this->shared['message'];
$instance = new Message($this->getOutputService(), array('with_newline' => true));
return $this->shared['message'] = $instance; } }
![Page 91: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/91.jpg)
Semantic configuration
Thanks to an extension mechanism
![Page 92: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/92.jpg)
<container xmlns="http://www.symfony-project.org/schema/dic/services">
<zend:logger priority="debug" path="%kernel.root_dir%/logs/%kernel.environment%.log" />
<doctrine:dbal dbname="dbname" username="root" password="" />
<swift:mailer transport="gmail"> <swift:username>fabien.potencier</swift:username> <swift:password>xxxxxx</swift:password> </swift:mailer>
</container>
![Page 93: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/93.jpg)
<container xmlns="http://www.symfony-project.org/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:swift="http://www.symfony-project.org/schema/dic/swiftmailer" xmlns:doctrine="http://www.symfony-project.org/schema/dic/doctrine" xmlns:zend="http://www.symfony-project.org/schema/dic/zend" xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd http://www.symfony-project.org/schema/dic/doctrine http://www.symfony-project.org/schema/dic/doctrine/doctrine-1.0.xsd http://www.symfony-project.org/schema/dic/swiftmailer http://www.symfony-project.org/schema/dic/swiftmailer/swiftmailer-1.0.xsd">
<zend:logger priority="debug" path="%kernel.root_dir%/logs/%kernel.environment%.log" />
<doctrine:dbal dbname="dbname" username="root" password="" />
<swift:mailer transport="gmail"> <swift:username>fabien.potencier</swift:username> <swift:password>xxxxxx</swift:password> </swift:mailer>
</container>
auto-completion
and validation with XSD
![Page 94: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/94.jpg)
zend.logger: level: debug path: %kernel.root_dir%/logs/%kernel.environment%.log
doctrine.dbal: dbname: dbname username: root password: ~
swift.mailer: transport: gmail username: fabien.potencier password: xxxxxxx
![Page 95: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/95.jpg)
Everything is converted by the extension to plain services and parameters
no overhead
![Page 96: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/96.jpg)
Loader::registerExtension(new SwiftMailerExtension()); Loader::registerExtension(new DoctrineExtension()); Loader::registerExtension(new ZendExtension());
$loader = new XmlFileLoader(__DIR__); $config = $loader->load('services.xml');
$container = new Builder(); $container->merge($config);
$container->mailer->...
$dumper = new PhpDumper($container); echo $dumper->dump();
![Page 97: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/97.jpg)
More about Dependency Injection http://fabien.potencier.org/article/17/on-php-5-3-lambda-functions-and-closures
http://components.symfony-project.org/dependency-injection/ (5.2)
http://github.com/fabpot/symfony/tree/master/src/Symfony/Components/DependencyInjection/(5.3)
http://github.com/fabpot/pimple
http://twittee.org/
![Page 98: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/98.jpg)
Remember, most of the time, you don’t need a Container
to use Dependency Injection
![Page 99: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/99.jpg)
You can start to use and benefit from Dependency Injection today
![Page 100: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/100.jpg)
by implementing it in your projects
by using externals libraries that already use DI
without the need of a container
![Page 101: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/101.jpg)
Symfony Zend Framework ezComponents
Doctrine Swift Mailer
…
![Page 102: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/102.jpg)
Questions?
My slides will be available on slideshare.com/fabpot
![Page 103: Dependency injection - phpday 2010](https://reader034.vdocuments.site/reader034/viewer/2022042813/54b7a0c94a79592d048b458f/html5/thumbnails/103.jpg)
Sensio S.A. 92-98, boulevard Victor Hugo
92 115 Clichy Cedex FRANCE
Tél. : +33 1 40 99 80 80
Contact Fabien Potencier
fabien.potencier at sensio.com
http://www.sensiolabs.com/
http://www.symfony-project.org/
http://fabien.potencier.org/