Your SlideShare is downloading. ×
0
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Dependency injection-zendcon-2010

29,407

Published on

Published in: Technology
5 Comments
32 Likes
Statistics
Notes
  • perfect!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • cheap beats by dreAT&T's proposed acquisition of T-Mobile hit yet another roadblock on Friday as a Department of Justice lawyer said his agency would file a motion that could delay the legal challenge of the deal for months. beats by dr dre cheap
    cheap dre beats studioJoseph Wayland told U.S. District Court Judge Ellen S. Huvelle that the case no longer needed to be expedited after AT&T and Deustche Telekom, which owns T-Mobile, requested their application be pulled from the Federal Communications Commission. The commission later granted that approval, though the two sides disagreed over whether it could reject it.
    dre beats cheapHuvelle had initially set a February start date, but the judge expressed concerns about whether the case should be heard at all. The withdrawal of the application means that AT&T and Deutsche Telekom are likely to significantly restructure the deal. dr dre beats
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • I prefer django's approach with something like get_session_backend() which returns a session implementation based on the settings (django will often use the __init__ functionality instead of a function so instead of get_cache() from django.core.cache import cache which is a bit less syntax, but makes it harder to monkey patch)
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Thanks, Wonderful presentation
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • touche!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
29,407
On Slideshare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
385
Comments
5
Likes
32
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Dependency Injection Fabien Potencier
  • 2. 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/
  • 3. Dependency Injection A real world « web » example
  • 4. In most web applications, you need to manage the user preferences –  The user language –  Whether the user is authenticated or not –  The user credentials –  …
  • 5. This can be done with a User object –  setLanguage(), getLanguage() –  setAuthenticated(), isAuthenticated() –  addCredential(), hasCredential() –  ...
  • 6. The User information need to be persisted between HTTP requests We use the PHP session for the Storage
  • 7. class SessionStorage { function __construct($cookieName = 'PHP_SESS_ID') { session_name($cookieName); session_start(); } function set($key, $value) { $_SESSION[$key] = $value; } // ... }
  • 8. 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
  • 9. 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
  • 10. That’s Dependency Injection Nothing more
  • 11. Let’s understand why the first example is not a good idea
  • 12. I want to change the session cookie name
  • 13. 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
  • 14. 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?
  • 15. class User { protected $storage; function __construct($sessionName) { $this->storage = new SessionStorage($sessionName); } } $user = new User('SESSION_ID'); Configure via User?
  • 16. 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?
  • 17. I want to change the session storage implementation Filesystem MySQL Memcached …
  • 18. 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?
  • 19. Now, the User depends on the Registry
  • 20. Instead of harcoding the Storage dependency inside the User class constructor Inject the Storage dependency in the User object
  • 21. class User { protected $storage; function __construct($storage) { $this->storage = $storage; } } $storage = new SessionStorage('SESSION_ID'); $user = new User($storage);
  • 22. What are the advantages?
  • 23. Use different Storage strategies
  • 24. class User { protected $storage; function __construct($storage) { $this->storage = $storage; } } $storage = new MySQLSessionStorage('SESSION_ID'); $user = new User($storage); Use a different Storage engine
  • 25. Configuration becomes natural
  • 26. class User { protected $storage; function __construct($storage) { $this->storage = $storage; } } $storage = new MySQLSessionStorage('SESSION_ID'); $user = new User($storage); Configuration is natural
  • 27. Wrap third-party classes (Interface / Adapter)
  • 28. class User { protected $storage; function __construct(SessionStorageInterface $storage) { $this->storage = $storage; } } interface SessionStorageInterface { function get($key); function set($key, $value); } Add an interface
  • 29. Mock the Storage object (for testing)
  • 30. 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
  • 31. 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
  • 32. « Dependency Injection is where components are given their dependencies through their constructors, methods, or directly into fields. » http://www.picocontainer.org/injection.html
  • 33. $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;
  • 34. A slightly more complex web example
  • 35. $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);
  • 36. 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();
  • 37. Back to square 1
  • 38. 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();
  • 39. We need a Container Describes objects and their dependencies Instantiates and configures objects on-demand
  • 40. A container SHOULD be able to manage ANY PHP object (POPO) The objects MUST not know that they are managed by a container
  • 41. •  Parameters –  The SessionStorageInterface implementation we want to use (the class name) –  The session name •  Objects –  SessionStorage –  User •  Dependencies –  User depends on a SessionStorageInterface implementation
  • 42. Let’s build a simple container with PHP 5.3
  • 43. DI Container Managing parameters
  • 44. class Container { protected $parameters = array(); public function setParameter($key, $value) { $this->parameters[$key] = $value; } public function getParameter($key) { return $this->parameters[$key]; } }
  • 45. $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
  • 46. 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
  • 47. $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
  • 48. DI Container Managing objects
  • 49. We need a way to describe how to create objects, without actually instantiating anything! Anonymous functions to the rescue!
  • 50. Anonymous Functions / Lambdas A lambda is a function defined on the fly with no name function () { echo 'Hello world!'; };
  • 51. Anonymous Functions / Lambdas A lambda can be stored in a variable $hello = function () { echo 'Hello world!'; };
  • 52. Anonymous Functions / Lambdas And then it can be used as any other PHP callable $hello(); call_user_func($hello);
  • 53. Anonymous Functions / Lambdas You can also pass a lambda as an argument to a function or method function foo(Closure $func) { $func(); } foo($hello);
  • 54. 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');
  • 55. DI Container Managing objects
  • 56. 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
  • 57. $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
  • 58. 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
  • 59. $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
  • 60. DI Container Scope
  • 61. For some objects, like the user, the container must always return the same instance
  • 62. spl_object_hash($container->user) !== spl_object_hash($container->user)
  • 63. $container->user = function ($c) { static $user; if (is_null($user)) { $user = new User($c->storage); } return $user; };
  • 64. spl_object_hash($container->user) === spl_object_hash($container->user)
  • 65. $container->user = $container->asShared(function ($c) { return new User($c->storage); });
  • 66. A closure is a lambda that remembers the context of its creation…
  • 67. 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'), );
  • 68. $mapper = function ($article) { return $article->getTitle(); }; $titles = array_map($mapper, $articles);
  • 69. $method = 'getTitle'; $mapper = function ($article) use($method) { return $article->$method(); }; $method = 'getAuthor'; $titles = array_map($mapper, $articles);
  • 70. $mapper = function ($method) { return function ($article) use($method) { return $article->$method(); }; };
  • 71. $titles = array_map($mapper('getTitle'), $articles); $authors = array_map($mapper('getAuthor'), $articles);
  • 72. $container->user = $container->asShared(function ($c) { return new User($c->storage); });
  • 73. function asShared(Closure $lambda) { return function ($container) use ($lambda) { static $object; if (is_null($object)) { $object = $lambda($container); } return $object; }; }
  • 74. 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
  • 75. 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
  • 76. I’m NOT advocating the usage of lambdas everywhere This presentation is about showing how they work on practical examples
  • 77. A DI Container does NOT manage ALL your objects
  • 78. Good rule of thumb: It manages “Global” objects Objects with only one instance (!= Singletons)
  • 79. LIKE a User, a Request, a database Connection, a Logger, …
  • 80. UNLIKE Model objects (a Product, a blog Post, …)
  • 81. Symfony Components Dependency Injection
  • 82. Rock-solid implementation of a DIC in PHP 5.3
  • 83. At the core of the Symfony 2.0 framework … which is one of the fastest framework
  • 84. Very flexible Configuration in PHP, XML, YAML, or INI
  • 85. $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!');
  • 86. services: output: { class: FancyOutput } message: class: Message arguments: - @output - { with_newline: true }
  • 87. <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>
  • 88. <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 }
  • 89. As fast as it can be The container can be “compiled” down to plain PHP code
  • 90. use SymfonyComponentsDependencyInjectionContainer; use SymfonyComponentsDependencyInjectionReference; use SymfonyComponentsDependencyInjectionParameter; 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; } }
  • 91. Semantic configuration Thanks to an extension mechanism
  • 92. <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>
  • 93. <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
  • 94. 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
  • 95. Everything is converted by the extension to plain services and parameters no overhead
  • 96. 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();
  • 97. 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/
  • 98. Remember, most of the time, you don’t need a Container to use Dependency Injection
  • 99. You can start to use and benefit from Dependency Injection today
  • 100. by implementing it in your projects by using externals libraries that already use DI without the need of a container
  • 101. Symfony Zend Framework ezComponents Doctrine Swift Mailer …
  • 102. Questions? My slides will be available on slideshare.com/fabpot
  • 103. 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/

×