IoC with PHP     Chris Weldon  Dallas TechFest 2011
Before We Begin                                            http://bit.ly/rf1pxRgit://github.com/neraath/ioc-php-talk.git
Your Guide: Chris Weldon•   Fightin’ Texas Aggie•   .Net and PHP Developer•   UNIX and Windows Sysadmin•   Senior Consulta...
Agile, Microsoft, Open Technologies, UXApplied Training, Coaching, MentoringCertified ConsultingRural SourcingRecruiting Se...
Before We Get to IoC...<?phpclass Authenticator {    private $_repository;    public function __construct() {        $this...
What are the problems?
What are the problems?•   Strongly coupled to DataAccessLayer
What are the problems?•   Strongly coupled to DataAccessLayer                                                    Authentic...
What are the problems?•   Strongly coupled to DataAccessLayer•                                                    Authenti...
What are the problems?•   Strongly coupled to DataAccessLayer•                                                    Authenti...
What are the problems?•   Strongly coupled to DataAccessLayer•                                                    Authenti...
What are the problems?•   Strongly coupled to DataAccessLayer•                                                    Authenti...
Let’s solve it•   What are our goals?    •   Decrease coupling    •   Increase configurability
<?phpinterface IUserRepository {    function findByUsernameAndPassword($username, $password);}class DataAccessLayer implem...
Our Updated Authenticator<?phpclass Authenticator {    private $_repository;    public function __construct(IUserRepositor...
Time to Consume<?phpclass LoginController {    public function login($username, $password) {        $configuration = Zend_...
Goal Recap•   What were our goals?    •   Decrease coupling    •   Increase configurability
Goal Recap•   What were our goals?    •   Decrease coupling    •   Increase configurability
Goal Recap•   What were our goals?    •   Decrease coupling    •   Increase configurability
What You Saw Was IoC•   Inversion of Control changes direction of responsibility    •   Someone else responsible for creat...
Dependency Inversion•   “High-level modules should not depend upon low level modules. They    should depend upon abstracti...
Let’s Draw
Let’s Draw          Authenticator      authenticate() : bool        DataAccessLayerfindByUsernameAndPassword : array
Let’s Draw          Authenticator      authenticate() : bool        DataAccessLayerfindByUsernameAndPassword : array
Let’s Draw                                                  Authenticator                                              aut...
Benefit: Flexibility<?phpclass WebServiceUserRepository implements IUserRepository {    public function findByUsernameAndPa...
Benefit: Testable<?phpclass WhenAuthenticating extends PHPUnit_Framework_TestCase {    public function testGivenInvalidUser...
Dependency Injection•   Now we can inject our dependencies to our consumer classes•   Still requires some other class to b...
<?phpclass UserRepositoryContainer {    /** @return IUserRepository **/    public function getRepository() {        $conta...
Container Woes•   No uniform interface by which to access services•   Still tightly coupled with dependencies•   Configurab...
Symfony Dependency Injection                 Container•   Two Ways to Setup and Use sfServiceContainer    •   Create subcl...
sfServiceContainer Subclass<?phpclass UserRepositoryContainer extends sfServiceContainer {    protected function getUserRe...
Consuming the Container<?phpclass LoginController {    public function login($username, $password) {        $configuration...
That’s Pretty Nice•   Configurability a lot easier•   Uniform interface for accessing services•   How does this scale when ...
The Builder•   Provides a uniform way of describing services, without custom    containers•   For each service description...
How to Describe a Service•   Code-based or Config-based    •   Code-based allows for run-time changing of injection paramet...
Code-Based Description<?php// Imagine this is a bootstrap file.$configuration = Zend_Registry::get(dbconfig);$builder = ne...
Config-Based Description<?xml version="1.0" ?><container xmlns="http://symfony-project.org/2.0/container">  <parameters>   ...
Loading the Config<?php// Imagine this is a bootstrap file.$builder = new sfServiceContainerBuilder();$loader = new sfServi...
Using the Container<?phpclass LoginController {    public function login($username, $password) {        $container = Zend_...
High Level Picture        DataAccessLayerfindByUsernameAndPassword : array         IUserRepositoryfindByUsernameAndPassword ...
When to Use a DI Container
When to Use a DI Container•   Not for model objects (e.g. Orders, Documents, etc.)
When to Use a DI Container•   Not for model objects (e.g. Orders, Documents, etc.)•   Great for resource requirements (e.g...
When to Use a DI Container•   Not for model objects (e.g. Orders, Documents, etc.)•   Great for resource requirements (e.g...
When to Use a DI Container•   Not for model objects (e.g. Orders, Documents, etc.)•   Great for resource requirements (e.g...
Other Considerations
Other Considerations•   Learning curve
Other Considerations•   Learning curve•   Tracking dependencies
Other Considerations•   Learning curve•   Tracking dependencies•   Dependency changes
Other Considerations•   Learning curve•   Tracking dependencies•   Dependency changes•   Setter/method vs. constructor inj...
Service Lifetimes•   setShared() allows you to specify context persistence    •   If shared, acts like a singleton    •   ...
Let’s Code
Thank You!                                            http://bit.ly/rf1pxRgit://github.com/neraath/ioc-php-talk.git
Upcoming SlideShare
Loading in …5
×

IoC with PHP

8,652
-1

Published on

Published in: Technology
0 Comments
13 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
8,652
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
92
Comments
0
Likes
13
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • IoC with PHP

    1. 1. IoC with PHP Chris Weldon Dallas TechFest 2011
    2. 2. Before We Begin http://bit.ly/rf1pxRgit://github.com/neraath/ioc-php-talk.git
    3. 3. Your Guide: Chris Weldon• Fightin’ Texas Aggie• .Net and PHP Developer• UNIX and Windows Sysadmin• Senior Consultant at Improving Enterprises• Contact Me: chris@chrisweldon.net
    4. 4. Agile, Microsoft, Open Technologies, UXApplied Training, Coaching, MentoringCertified ConsultingRural SourcingRecruiting Services
    5. 5. Before We Get to IoC...<?phpclass Authenticator { private $_repository; public function __construct() { $this->_repository = new DataAccessLayer(); } public function authenticate($username, $password) { $hashedPassword = md5($password); $user = $this->_repository->findByUsernameAndPassword( $username, $hashedPassword); return $user === null; }}
    6. 6. What are the problems?
    7. 7. What are the problems?• Strongly coupled to DataAccessLayer
    8. 8. What are the problems?• Strongly coupled to DataAccessLayer Authenticator authenticate() : bool DataAccessLayer findByUsernameAndPassword : array
    9. 9. What are the problems?• Strongly coupled to DataAccessLayer• Authenticator Very inflexible authenticate() : bool DataAccessLayer findByUsernameAndPassword : array
    10. 10. What are the problems?• Strongly coupled to DataAccessLayer• Authenticator Very inflexible authenticate() : bool• How to configure DataAccessLayer? DataAccessLayer findByUsernameAndPassword : array
    11. 11. What are the problems?• Strongly coupled to DataAccessLayer• Authenticator Very inflexible authenticate() : bool• How to configure DataAccessLayer? • DataAccessLayer Let it read configs? findByUsernameAndPassword : array
    12. 12. What are the problems?• Strongly coupled to DataAccessLayer• Authenticator Very inflexible authenticate() : bool• How to configure DataAccessLayer? • DataAccessLayer Let it read configs? findByUsernameAndPassword : array• How to test the Authenticator?
    13. 13. Let’s solve it• What are our goals? • Decrease coupling • Increase configurability
    14. 14. <?phpinterface IUserRepository { function findByUsernameAndPassword($username, $password);}class DataAccessLayer implements IUserRepository { private $_configParams; private $_database; public function __construct(array $configParams) { $this->_configParams = $configParams; $this->_database = Zend_Db::factory(Pdo_Mysql, $this->_configParams); } public function findByUsernameAndPassword($username, $password) { $query = SELECT * FROM users WHERE username = ? AND password = ?; $result = $this->_database->fetchAll($query, $username, $password); return $result; }}
    15. 15. Our Updated Authenticator<?phpclass Authenticator { private $_repository; public function __construct(IUserRepository $repository) { $this->_repository = $repository; } public function authenticate($username, $password) { $hashedPassword = md5($password); $user = $this->_repository->findByUsernameAndPassword( $username, $hashedPassword); return $user === null; }}
    16. 16. Time to Consume<?phpclass LoginController { public function login($username, $password) { $configuration = Zend_Registry::get(dbconfig); $dal = new DataAccessLayer($configuration); $authenticator = new Authenticator($dal); if ($authenticator->authenticate($username, $password)) { // Do something to log the user in. } }}
    17. 17. Goal Recap• What were our goals? • Decrease coupling • Increase configurability
    18. 18. Goal Recap• What were our goals? • Decrease coupling • Increase configurability
    19. 19. Goal Recap• What were our goals? • Decrease coupling • Increase configurability
    20. 20. What You Saw Was IoC• Inversion of Control changes direction of responsibility • Someone else responsible for creating and providing my dependencies • Most commonly applied pattern: Dependency Injection • Follows Dependency Inversion Principle from SOLID• Culture War: IoC vs. DI vs. Naming vs. Principles vs. Ideology
    21. 21. Dependency Inversion• “High-level modules should not depend upon low level modules. They should depend upon abstractions.• “Abstractions should not depend upon details. Details should depend upon abstractions.” Robert Martin
    22. 22. Let’s Draw
    23. 23. Let’s Draw Authenticator authenticate() : bool DataAccessLayerfindByUsernameAndPassword : array
    24. 24. Let’s Draw Authenticator authenticate() : bool DataAccessLayerfindByUsernameAndPassword : array
    25. 25. Let’s Draw Authenticator authenticate() : bool Authenticator authenticate() : bool IUserRepository findByUsernameAndPassword : array DataAccessLayerfindByUsernameAndPassword : array DataAccessLayer findByUsernameAndPassword : array
    26. 26. Benefit: Flexibility<?phpclass WebServiceUserRepository implements IUserRepository { public function findByUsernameAndPassword($username, $password) { // Fetch our user through JSON or SOAP }}class OAuthRepository implements IUserRepository { public function findByUsernameAndPassword($username, $password) { // Connect to your favorite OAuth provider }}
    27. 27. Benefit: Testable<?phpclass WhenAuthenticating extends PHPUnit_Framework_TestCase { public function testGivenInvalidUsernameAndPasswordShouldReturnFalse() { $stub = $this->getMock(IUserRepository); $stub->expects($this->any()) ->method(findByUsernameAndPassword) ->will($this->returnValue(null)); $authenticator = new Authenticator($stub); $this->assertFalse($authenticator->authenticate(user, pass)); }}
    28. 28. Dependency Injection• Now we can inject our dependencies to our consumer classes• Still requires some other class to be tightly coupled to both of those• Need a container that can help abstract the relationship between the interface and implementation
    29. 29. <?phpclass UserRepositoryContainer { /** @return IUserRepository **/ public function getRepository() { $container = new DataAccessLayer(array( dsn => mysql://localhost/database, username => user, password => pass )); return $container; }}class LoginController { public function login($username, $password) { $container = new UserRepositoryContainer(); $repository = $container->getRepository(); $authenticator = new Authenticator($repository); // ... }}
    30. 30. Container Woes• No uniform interface by which to access services• Still tightly coupled with dependencies• Configurability of the container difficult• How to auto-inject dependency for configured consumer classes?
    31. 31. Symfony Dependency Injection Container• Two Ways to Setup and Use sfServiceContainer • Create subclass • Manual registration via code or config
    32. 32. sfServiceContainer Subclass<?phpclass UserRepositoryContainer extends sfServiceContainer { protected function getUserRepositoryService() { $container = new DataAccessLayer($this[repository.config]); return $container; }}
    33. 33. Consuming the Container<?phpclass LoginController { public function login($username, $password) { $configuration = Zend_Registry::get(dbconfig); $container = new UserRepositoryContainer(array( repository.config => $configuration )); $repository = $container->userRepository; $authenticator = new Authenticator($repository); if ($authenticator->authenticate($username, $password)) { // Do something to log the user in. } }}
    34. 34. That’s Pretty Nice• Configurability a lot easier• Uniform interface for accessing services• How does this scale when there are lots of dependencies?• Aren’t we still coupling the container to the implementation at compile time?
    35. 35. The Builder• Provides a uniform way of describing services, without custom containers• For each service description, we have the flexibility to configure an object: • At instantiation (Constructor Injection) • Post-instantiation (Setter/Method Injection)
    36. 36. How to Describe a Service• Code-based or Config-based • Code-based allows for run-time changing of injection parameters • Config-based provides a way to change parameters between environments with no code changes • Not mutually exclusive
    37. 37. Code-Based Description<?php// Imagine this is a bootstrap file.$configuration = Zend_Registry::get(dbconfig);$builder = new sfServiceContainerBuilder();$builder->register(user_repository, DataAccessLayer) ->addArgument($configuration) // OR ->addArgument(%repository.config%) from earlier ->setShared(false);$builder->register(authenticator, Authenticator) ->addArgument(new sfServiceReference(user_repository));Zend_Registry::set(di_container, $builder);
    38. 38. Config-Based Description<?xml version="1.0" ?><container xmlns="http://symfony-project.org/2.0/container"> <parameters> <parameter key="user_repository.dsn">mysql://localhost/database</parameter> <parameter key="user_repository.username">username</parameter> <parameter key="user_repository.password">password</parameter> </parameters> <services> <service id="user_repository" class="DataAccessLayer" shared="false"> <argument type="collection"> <argument key="dsn">%user_repository.dsn%</argument> <argument key="username">%user_repository.username%</argument> <argument key="password">%user_repository.password%</argument> </argument> </service> <service id="authenticator" class="Authenticator"> <argument type="service" id="user_repository" /> </service> </services></container>
    39. 39. Loading the Config<?php// Imagine this is a bootstrap file.$builder = new sfServiceContainerBuilder();$loader = new sfServiceContainerLoaderFileXml($builder);$loader->load(/pathTo/services.xml);Zend_Registry::set(di_container, $builder);
    40. 40. Using the Container<?phpclass LoginController { public function login($username, $password) { $container = Zend_Registry::get(di_container); $authenticator = $container->authenticator; if ($authenticator->authenticate($username, $password)) { // Do something to log the user in. } }}
    41. 41. High Level Picture DataAccessLayerfindByUsernameAndPassword : array IUserRepositoryfindByUsernameAndPassword : array sfServiceContainerBuilder getService : object setService : void hasService : bool Authenticatorauthenticate() : bool IAuthenticatorauthenticate() : bool sfServiceContainerInterface getService : object LoginController setService : void login() : void hasService : bool
    42. 42. When to Use a DI Container
    43. 43. When to Use a DI Container• Not for model objects (e.g. Orders, Documents, etc.)
    44. 44. When to Use a DI Container• Not for model objects (e.g. Orders, Documents, etc.)• Great for resource requirements (e.g. repositories, loggers, etc.)
    45. 45. When to Use a DI Container• Not for model objects (e.g. Orders, Documents, etc.)• Great for resource requirements (e.g. repositories, loggers, etc.)• Really great for plugin-type architecture
    46. 46. When to Use a DI Container• Not for model objects (e.g. Orders, Documents, etc.)• Great for resource requirements (e.g. repositories, loggers, etc.)• Really great for plugin-type architecture• But not necessary to use Dependency Injection!
    47. 47. Other Considerations
    48. 48. Other Considerations• Learning curve
    49. 49. Other Considerations• Learning curve• Tracking dependencies
    50. 50. Other Considerations• Learning curve• Tracking dependencies• Dependency changes
    51. 51. Other Considerations• Learning curve• Tracking dependencies• Dependency changes• Setter/method vs. constructor injection
    52. 52. Service Lifetimes• setShared() allows you to specify context persistence • If shared, acts like a singleton • Useful if construction is expensive or state persistence required
    53. 53. Let’s Code
    54. 54. Thank You! http://bit.ly/rf1pxRgit://github.com/neraath/ioc-php-talk.git
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×