ZF2 Modules and Services (And DI)
Maurice Kherlakian
Zend Technologies




                        © All rights reserved. Zend Technologies, Inc.
Who am I?
    • Zend Professional Services consultant
    • Worked with PHP for about 10 years
    • Based in Montreal




     Linkedin: http://ca.linkedin.com/in/mkherlakian
     Twitter: http://twitter.com/mkherlakian
     Email: maurice.k@zend.com




2   Zend Professional Services     © All rights reserved. Zend Technologies, Inc.
ZF2 Modules – What are they?

    • Discrete blocks of code
    • Allow for code re-use (not only within ZF2 but with other
      frameworks – Sf2 anyone?)
    • Physically a namespaced directory
    • If written well, distributable and re-usable
    • Examples:
        User module (authentication – login/logout…)

        ACL module

        Blog module



3   Zend Professional Services   © All rights reserved. Zend Technologies, Inc.
Why are they better than ZF1’s modules

    • ZF1 modules had serious problems
    • Tightly coupled with MVC
    • Could not be easily reused
    • Bootstrapping expensive
    • Not self-contained
    • No dependency management


       ZF2 addresses all of these problems




4   Zend Professional Services   © All rights reserved. Zend Technologies, Inc.
Requirements for a module – Module class
    • A namespaced class called Module

          <?php

          namespace Mymod;
          class Module {}


     • That’s it!




5   Zend Professional Services   © All rights reserved. Zend Technologies, Inc.
Requirements for a module – Directory
    structure
    • There is, of course, a recommended directory structure




6   Zend Professional Services   © All rights reserved. Zend Technologies, Inc.
Requirements for a module - Registration
    • In order for the application (Zend/Application) to run your module, it has
      to be registered
         <?php                                               //application.config.php

         return array(
            'modules' => array(                      //Which modules are we loading?
               'Application',
               'Mymod',
            ),
            'module_listener_options' => array(
               'config_glob_paths' => array(
                  'config/autoload/{,*.}{global,local}.php',
               ),
               'config_cache_enabled' => false,
               'cache_dir'        => 'data/cache',
               'module_paths' => array(                 //What paths are we loading modules from?
                  './module',                    //We will look for a Module.php file in subdirs
                  './vendor',                   //one level down from this dir *by default*
               ),
            ),
            'service_manager' => array(                   //More on that later
               'use_defaults' => true,
               'factories' => array(
               ),
            ),
         );




7   Zend Professional Services                          © All rights reserved. Zend Technologies, Inc.
Optional for a module – getConfig()
    • Called automatically to get the module’s configuration and aggregate it
      with the other modules
    • Best is to include a config file that returns a php array (best practice in
      ZF2 in general)
       <?php
       namespace Mymod;

       use ZendModuleManagerModuleManager,
         ZendMvcMvcEvent;

       class Module {
          public function getConfig() {
            return include(__DIR__.'/config/module.config.php');
          }
       }




8   Zend Professional Services                        © All rights reserved. Zend Technologies, Inc.
Optional for a module – init()
    • A module can have an init() method that fires upon initialization
    • The ModuleManager is passed to the init function as an argument
         <?php
         namespace Mymod;

         use ZendModuleManagerModuleManager,
           ZendMvcMvcEvent;

         class Module {
           public function init(ModuleManager $e) {
             $e->events()->attach(
                       'loadModules.post',
                       function($e) {
                          echo 'postLoad from MyMod';
                       },
                       100);
             $e->events()->getSharedManager()->attach(
                       __NAMESPACE__,
                       'dispatch',
                       function($e) {
                          echo "Only if dispatched to Mymod";
                       },
                       100);
           }
         }




9   Zend Professional Services                         © All rights reserved. Zend Technologies, Inc.
Optional for a module – onBootstrap()
     • And if init() is too early, ModuleManager automatically registers the
       onBootstrap method if found in the module
     • An MvcEvent is passed as the argument
        <?php
        namespace Mymod;

        use ZendModuleManagerModuleManager,
          ZendMvcMvcEvent;

        class Module {
           public function onBootstrap(MvcEvent $e) {
             $request = $e->getRequest();
           }
        }




10   Zend Professional Services                         © All rights reserved. Zend Technologies, Inc.
Optional for a module – Dep solving
     • Thru getProvides() and getDependencies()
     • Off by default, can be turned on
         <?php

         namespace Blog;
         class Module {
            public function getProvides()
            {
              return array(
                    'name' => 'Blog',
                    'version' => '0.1.0',
              );
            }

             public function getDependencies()
             {
               return array(
                     'php' => array(
                           'required' => true,
                           'version' => '>=5.3.1',
                     ),
                     'ext/mongo' => array(
                           'required' => true,
                           'version' => '>=1.2.0',
                     ),
               );
             }
         }




11   Zend Professional Services                      © All rights reserved. Zend Technologies, Inc.
ModuleManager

     • It’s automatically instantiated in your ZendApplication
     • It fulfills 3 functions:
         Aggregates enabled modules (allows you to iterate over them)

         Aggregates the configuration from each module

         Triggers the module initialization

     • There are many events for which the ModuleManager is
       passed as an argument
     • This allows you to provide listeners at practically any point
       in the code to get access to the manager



12   Zend Professional Services   © All rights reserved. Zend Technologies, Inc.
An MVC module
     • In most cases that’s what you will want
          <?php                                                               //module.config.php
          return array(
              'router' => array(
                 'routes' => array(
                 'Mymod' => array(
                    'type' => 'ZendMvcRouterHttpLiteral',
                 'options' => array(
                    'route' => '/mymod',
                    'defaults' => array(
                       'controller' => 'mymod',
                       'action' => 'index',
                       ),
                    ),
                 ),
              ),
            ),
            'controller' => array(
              'classes' => array(
                'mymod' => 'MymodControllerIndexController',
              ),
            ),
              'view_manager' => array(
                 'template_path_stack' => array(
                    __DIR__ . '/../view',
                 ),
              ),
          );



13   Zend Professional Services                         © All rights reserved. Zend Technologies, Inc.
The missing link - bootstrapping – index.php
          <?php                                                // /public/index.php

          require_once 'Loader/StandardAutoloader.php';
          use ZendLoaderStandardAutoloader,
            ZendServiceManagerServiceManager,
            ZendMvcServiceServiceManagerConfiguration;

          $loader = new StandardAutoloader();
          $loader->registerNamespace('Application', 'module/Application/src/Application');
          $loader->registerNamespace('Mymod', 'module/Mymod/src/Application');

          spl_autoload_register(array($loader, 'autoload'));

          // Get application stack configuration
          chdir(dirname(__DIR__));
          $configuration = include 'config/application.config.php';

          // Setup service manager
          $serviceManager = new ServiceManager(new ServiceManagerConfiguration($configuration['service_manager']));
          $serviceManager->setService('ApplicationConfiguration', $configuration);
          $serviceManager->get('ModuleManager')->loadModules();

          // Run application
          $serviceManager->get('Application')->bootstrap()->run()->send();




14   Zend Professional Services                           © All rights reserved. Zend Technologies, Inc.
The missing link - bootstrapping – index.php
     • Autoloading – it is the responsibility of index.php to set up autoloading
     • This is because modules have to be portable between different
       frameworks (reusability)
     • Any PSR-0 autoloader should work, it does not have to be ZF2’s
       autoloader
         For example Rob Allen’s Skeleton module uses Composer’s autoloader

     • The next lines are the ServiceManager’s setup (more on it later)




15   Zend Professional Services   © All rights reserved. Zend Technologies, Inc.
Modules distribution
     • Any source you can think of
         Pyrus

         Git

         Http

         Tar & send

         Composer

     • There is already a ZF2 modules site
                                  http://modules.zendframework.com/
     (We need help! Want to contribute?)




16   Zend Professional Services           © All rights reserved. Zend Technologies, Inc.
Modules packaging

     • Tar/Zip
     • Phar (being careful that writes occur in location external to
       the archive)




17   Zend Professional Services   © All rights reserved. Zend Technologies, Inc.
Location, location, location
     DI - short summary
     • Since the presentation was originally about ZendDi it’s only fair that we
       talk about it!
     • DI is the practice of reducing coupling between classes by injecting
       dependent classes in the subject instead of instantiating them

         <?php //No DI
         class DbAdapter {
            public function __construct($host, $user, $pass, $db) {
              //...
            }
         }

         class Author {
            protected $_db;

             public function __construct($host, $user, $pass, $db) {
               $this->_db = new DbAdapter($host, $user, $pass, $db);
             }
         }




18   Zend Professional Services                          © All rights reserved. Zend Technologies, Inc.
Location, location, location
     DI - short summary
         <?php //DI
         class DbAdapter {
            public function __construct($host, $user, $pass, $db) {
              //...
            }
         }

         class Author {
            protected $_db;

             public function __construct($db) {
               $this->_db = $db;
             }
         }

         $db = new DbAdapter();
         $author = new Author($db);




     • That’s all it is – Really! Makes it easy to substitute the DB class for mocking, or to change it all
       together for instance, and promotes decoupling and reusability. The D in SOLID.




19   Zend Professional Services                         © All rights reserved. Zend Technologies, Inc.
Location, location, location
     DI - short summary
     • Consider the case that an Author needs a DbGateway, which needs a DbAdapter, and a logging
       and caching class


         <?php

         $db = new DbAdapter();
         $gateway = new DbGateway($db);
         $cache = new Cache();
         $log = new Log();

         $author = new Author($db, $gateway, $cache, $log);
         $authors = $author->fetchAll();



       • 5 lines of code for initialization, but only one line for what we really need to do. Lots of
         boilerplate code
       • Enter Dependency Injection Containers, or DiC – ZendDi is one of them, so is the Symfony
         DiC, and a number of others




20    Zend Professional Services                      © All rights reserved. Zend Technologies, Inc.
Location, location, location
     DiC – ZendDiDi()
          <?php

          $config = array(
               'instance' => array(
                  'Author' => array(
                     'parameters' => array(
                         'gateway' => 'DbGateway',
                         'cache' => 'Cache',
                         'log' => 'Log'
                      )
                  ),
                  'DbGateway' => array(
                     'parameters' => array(
                        'db' => 'DbAdapter',
                     )
                  ),
                  'DbAdapter' => array(
                     'parameters' => array(
                        'host' => 'somehost',
                        'user' => 'someuser',
                        'pass' => 'somepass',
                        'db' => ‘somedb'
                     )
                  ),
                  'Cache' => array(
                  ),
                  'Log' => array(
                  )
               )
            );

          $di = new ZendDiDi();
          $di->configure(new ZendDiConfiguration($config));

          $author = $di->get('Author');




21   Zend Professional Services                                 © All rights reserved. Zend Technologies, Inc.
Location, location, location
     DiC – ZendDiDi()
     • The last line on the previous slide:
                    $author = $di->get('Author');
     Returns a fully configured Author object, following the definition we provided
     • DiC does instantiation and auto-wiring
     • ZendDi, although difficult to show here, is a complex solution for complex
       problems – it can easily be abused
     • For simpler problems, and more in line with the PHP philosophy, ServiceManager
       replaced it




22   Zend Professional Services          © All rights reserved. Zend Technologies, Inc.
Location, location, location
     Location v/s Injection
     • Service manager is a ServiceLocator !== DiC
     • BUT DiC === Service Locator
     • One main difference is that the subject requiring the dependencies is:
         Not aware of the DiC when using DI (the dependency is injected)

         Is aware of the locator in the case of SL, and asks the locator to obtain the dependency

     • ServiceManager is a service locator
     Think back to index.php:
                    $serviceManager->get('ModuleManager')->loadModules();


     • Martin Fowler is the subject matter expert on this:
       http://martinfowler.com/articles/injection.html




23   Zend Professional Services         © All rights reserved. Zend Technologies, Inc.
Location, location, location
     ZendServiceManagerServiceManager()
     • Service manager uses factories to generate its services
     • Factory can be a class implementing ZendServiceManagerFactoryInterface or a
       php closure




24   Zend Professional Services    © All rights reserved. Zend Technologies, Inc.
Location, location, location
     ZendServiceManagerServiceManager()
          <?php
          class ServiceConfiguration extends Configuration
          {
             public function configureServiceManager(ServiceManager $serviceManager)
             {
               $serviceManager->setFactory('author', function(ServiceManager $sm) {
                   $dbGateway = $sm->get('db_gateway');
                   $cache = $sm->get('cache');
                   $log = $sm->get('log');
                   return new Author($dbGateway, $cache, $log);
               } );
               $serviceManager->setShared('author', false);
               $serviceManager->setFactory('db_gateway', function(ServiceManager $sm){
                   $dbAdapter = $sm->get('db_adapter');
                   return new DbGateway($dbAdapter);
               } );
               $serviceManager->setFactory('db_adapter', function(ServiceManager $sm) {
                   //Using hard-coded values for the example, but normally you would either create a factory
                   //and inject the values from a config file
                   //or get the configuration from the ServiceManager and read the valuees in
                   return new DbAdapter('somehost', 'someuser', 'somepass', 'somedb');
               } );
               $serviceManager->setFactory('cache', function(ServiceManager $sm) { return new Cache(); } );
               $serviceManager->setFactory('log', function(ServiceManager $sm) { return new Log(); } );
             }
          }

          $config = new ServiceConfiguration();
          $sm = new ServiceManager();
          $config->configureServiceManager($sm);

          $author = $sm->get('author');




25   Zend Professional Services                             © All rights reserved. Zend Technologies, Inc.
Location, location, location
     ZendServiceManagerServiceManager()
     • The example above purposely uses closures to illustrate that a full-fledged
       factory is not needed
     • But in a lot of cases, implementing a factory makes sense - rewriting DbAdapter
       from the above example as a factory:

         <?php
         use ZendServiceManagerFactoryInterface;

         //The factory class
         class AuthorFactory implements FactoryInterface {
            public function createService(ZendServiceManagerServiceLocatorInterface $sl) {
              $dbGateway = $sl->get('db_gateway');
              $cache = $sl->get('cache');
              $log = $sl->get('log');
              return new Author($dbGateway, $cache, $log);
            }
         }

         //And the configuration class
         class ServiceConfiguration extends Configuration
         {
            public function configureServiceManager(ServiceManager $serviceManager)
            {
               //...
               $serviceManager->setFactory('author', 'AuthorFactory');
               //...
             }
         }



26   Zend Professional Services                             © All rights reserved. Zend Technologies, Inc.
Location, location, location
     ZendServiceManagerServiceManager()
     • The example above purposely uses closures to illustrate that a full-fledged
       factory is not needed
     • But in a lot of cases, implementing a factory makes sense - rewriting DbAdapter
       form the above example as a factory:

         <?php
         use ZendServiceManagerFactoryInterface;

         //The factory class
         class AuthorFactory implements FactoryInterface {
            public function createService(ZendServiceManagerServiceLocatorInterface $sl) {
              $dbGateway = $sl->get('db_gateway');
              $cache = $sl->get('cache');
              $log = $sl->get('log');
              return new Author($dbGateway, $cache, $log);
            }
         }

         //And the configuration class
         class ServiceConfiguration extends Configuration
         {
            public function configureServiceManager(ServiceManager $serviceManager)
            {
               //...
               $serviceManager->setFactory('author', 'AuthorFactory');
               //...
             }
         }



27   Zend Professional Services                             © All rights reserved. Zend Technologies, Inc.
Location, location, location
     Setting and retrieving the locator
     • In most MVC components (e.g.Controllers) the ServiceManager
       component is composed automatically by the MVC stack
     • The interface ZendServiceManagerServiceLocatorAwareInterface can
       be implemented to ensure that a service locator is composed in the
       subject class
     • Two methods, SetServiceLocator(ServiceLocatorInterface $locator) and
       getServiceLocator() must be implemented
     • Notice that the Component is ServiceManager(), but the interface is
       ServiceLocatorInterface. This is to allow you to provide an alternative
       implementation of service locator.




28   Zend Professional Services   © All rights reserved. Zend Technologies, Inc.
Location, location, location
     Setting and retrieving the locator
     • Controllers implement ServiceLocatorAwareInterface therefore

         <?php

         namespace MymodController;

         use ZendMvcControllerActionController,
           ZendViewModelViewModel;

         class IndexController extends ActionController
         {
            public function indexAction()
            {
              $sl = $this->getServiceLocator();
              $sl->get('author');
              return new ViewModel();
            }
         }




29   Zend Professional Services                      © All rights reserved. Zend Technologies, Inc.
Locating the injector or injecting the
     locator?
     • Although ServiceManager is now the goto dependency management
       component in ZF2, Di actually still exists as a fallback (by default)
     • One can specify a collection AbstractFactories to ServiceManager on
       which it will fall back if it does not find the target class
     • Therefore, ZendServiceManagerDiDiAbstractFactory is an abstract
       factory to which the name of the object is passed if it is not found by
       ServiceManager (the order can be changed)
     • You can also, of course, provide your own service factory (Proxy class to
       other frameworks maybe?)




30   Zend Professional Services   © All rights reserved. Zend Technologies, Inc.
Some references
     • ZF2 module site: http://modules.zendframework.com/
     • Rob Allen’s Skeleton:
       https://github.com/zendframework/ZendSkeletonApplication
     • Matthew’s discussion on Modules: http://mwop.net/blog/267-Getting-
       started-writing-ZF2-modules.html
     • Ralph on the decisions that drove to ServiceManager: http://zend-
       framework-community.634137.n4.nabble.com/Services-Instances-
       Dependencies-in-ZF2-td4584632.html
     • SOLID OO design: http://en.wikipedia.org/wiki/SOLID_(object-
       oriented_design)




31   Zend Professional Services   © All rights reserved. Zend Technologies, Inc.
• Download ZF2 and happy coding!
     http://zendframework.com/zf2




                                                Thank You!
                                  http://twitter.com/mkherlakian




32   Zend Professional Services         © All rights reserved. Zend Technologies, Inc.

Zf2 phpquebec

  • 1.
    ZF2 Modules andServices (And DI) Maurice Kherlakian Zend Technologies © All rights reserved. Zend Technologies, Inc.
  • 2.
    Who am I? • Zend Professional Services consultant • Worked with PHP for about 10 years • Based in Montreal Linkedin: http://ca.linkedin.com/in/mkherlakian Twitter: http://twitter.com/mkherlakian Email: maurice.k@zend.com 2 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 3.
    ZF2 Modules –What are they? • Discrete blocks of code • Allow for code re-use (not only within ZF2 but with other frameworks – Sf2 anyone?) • Physically a namespaced directory • If written well, distributable and re-usable • Examples:  User module (authentication – login/logout…)  ACL module  Blog module 3 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 4.
    Why are theybetter than ZF1’s modules • ZF1 modules had serious problems • Tightly coupled with MVC • Could not be easily reused • Bootstrapping expensive • Not self-contained • No dependency management ZF2 addresses all of these problems 4 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 5.
    Requirements for amodule – Module class • A namespaced class called Module <?php namespace Mymod; class Module {} • That’s it! 5 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 6.
    Requirements for amodule – Directory structure • There is, of course, a recommended directory structure 6 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 7.
    Requirements for amodule - Registration • In order for the application (Zend/Application) to run your module, it has to be registered <?php //application.config.php return array( 'modules' => array( //Which modules are we loading? 'Application', 'Mymod', ), 'module_listener_options' => array( 'config_glob_paths' => array( 'config/autoload/{,*.}{global,local}.php', ), 'config_cache_enabled' => false, 'cache_dir' => 'data/cache', 'module_paths' => array( //What paths are we loading modules from? './module', //We will look for a Module.php file in subdirs './vendor', //one level down from this dir *by default* ), ), 'service_manager' => array( //More on that later 'use_defaults' => true, 'factories' => array( ), ), ); 7 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 8.
    Optional for amodule – getConfig() • Called automatically to get the module’s configuration and aggregate it with the other modules • Best is to include a config file that returns a php array (best practice in ZF2 in general) <?php namespace Mymod; use ZendModuleManagerModuleManager, ZendMvcMvcEvent; class Module { public function getConfig() { return include(__DIR__.'/config/module.config.php'); } } 8 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 9.
    Optional for amodule – init() • A module can have an init() method that fires upon initialization • The ModuleManager is passed to the init function as an argument <?php namespace Mymod; use ZendModuleManagerModuleManager, ZendMvcMvcEvent; class Module { public function init(ModuleManager $e) { $e->events()->attach( 'loadModules.post', function($e) { echo 'postLoad from MyMod'; }, 100); $e->events()->getSharedManager()->attach( __NAMESPACE__, 'dispatch', function($e) { echo "Only if dispatched to Mymod"; }, 100); } } 9 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 10.
    Optional for amodule – onBootstrap() • And if init() is too early, ModuleManager automatically registers the onBootstrap method if found in the module • An MvcEvent is passed as the argument <?php namespace Mymod; use ZendModuleManagerModuleManager, ZendMvcMvcEvent; class Module { public function onBootstrap(MvcEvent $e) { $request = $e->getRequest(); } } 10 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 11.
    Optional for amodule – Dep solving • Thru getProvides() and getDependencies() • Off by default, can be turned on <?php namespace Blog; class Module { public function getProvides() { return array( 'name' => 'Blog', 'version' => '0.1.0', ); } public function getDependencies() { return array( 'php' => array( 'required' => true, 'version' => '>=5.3.1', ), 'ext/mongo' => array( 'required' => true, 'version' => '>=1.2.0', ), ); } } 11 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 12.
    ModuleManager • It’s automatically instantiated in your ZendApplication • It fulfills 3 functions:  Aggregates enabled modules (allows you to iterate over them)  Aggregates the configuration from each module  Triggers the module initialization • There are many events for which the ModuleManager is passed as an argument • This allows you to provide listeners at practically any point in the code to get access to the manager 12 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 13.
    An MVC module • In most cases that’s what you will want <?php //module.config.php return array( 'router' => array( 'routes' => array( 'Mymod' => array( 'type' => 'ZendMvcRouterHttpLiteral', 'options' => array( 'route' => '/mymod', 'defaults' => array( 'controller' => 'mymod', 'action' => 'index', ), ), ), ), ), 'controller' => array( 'classes' => array( 'mymod' => 'MymodControllerIndexController', ), ), 'view_manager' => array( 'template_path_stack' => array( __DIR__ . '/../view', ), ), ); 13 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 14.
    The missing link- bootstrapping – index.php <?php // /public/index.php require_once 'Loader/StandardAutoloader.php'; use ZendLoaderStandardAutoloader, ZendServiceManagerServiceManager, ZendMvcServiceServiceManagerConfiguration; $loader = new StandardAutoloader(); $loader->registerNamespace('Application', 'module/Application/src/Application'); $loader->registerNamespace('Mymod', 'module/Mymod/src/Application'); spl_autoload_register(array($loader, 'autoload')); // Get application stack configuration chdir(dirname(__DIR__)); $configuration = include 'config/application.config.php'; // Setup service manager $serviceManager = new ServiceManager(new ServiceManagerConfiguration($configuration['service_manager'])); $serviceManager->setService('ApplicationConfiguration', $configuration); $serviceManager->get('ModuleManager')->loadModules(); // Run application $serviceManager->get('Application')->bootstrap()->run()->send(); 14 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 15.
    The missing link- bootstrapping – index.php • Autoloading – it is the responsibility of index.php to set up autoloading • This is because modules have to be portable between different frameworks (reusability) • Any PSR-0 autoloader should work, it does not have to be ZF2’s autoloader  For example Rob Allen’s Skeleton module uses Composer’s autoloader • The next lines are the ServiceManager’s setup (more on it later) 15 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 16.
    Modules distribution • Any source you can think of  Pyrus  Git  Http  Tar & send  Composer • There is already a ZF2 modules site http://modules.zendframework.com/ (We need help! Want to contribute?) 16 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 17.
    Modules packaging • Tar/Zip • Phar (being careful that writes occur in location external to the archive) 17 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 18.
    Location, location, location DI - short summary • Since the presentation was originally about ZendDi it’s only fair that we talk about it! • DI is the practice of reducing coupling between classes by injecting dependent classes in the subject instead of instantiating them <?php //No DI class DbAdapter { public function __construct($host, $user, $pass, $db) { //... } } class Author { protected $_db; public function __construct($host, $user, $pass, $db) { $this->_db = new DbAdapter($host, $user, $pass, $db); } } 18 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 19.
    Location, location, location DI - short summary <?php //DI class DbAdapter { public function __construct($host, $user, $pass, $db) { //... } } class Author { protected $_db; public function __construct($db) { $this->_db = $db; } } $db = new DbAdapter(); $author = new Author($db); • That’s all it is – Really! Makes it easy to substitute the DB class for mocking, or to change it all together for instance, and promotes decoupling and reusability. The D in SOLID. 19 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 20.
    Location, location, location DI - short summary • Consider the case that an Author needs a DbGateway, which needs a DbAdapter, and a logging and caching class <?php $db = new DbAdapter(); $gateway = new DbGateway($db); $cache = new Cache(); $log = new Log(); $author = new Author($db, $gateway, $cache, $log); $authors = $author->fetchAll(); • 5 lines of code for initialization, but only one line for what we really need to do. Lots of boilerplate code • Enter Dependency Injection Containers, or DiC – ZendDi is one of them, so is the Symfony DiC, and a number of others 20 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 21.
    Location, location, location DiC – ZendDiDi() <?php $config = array( 'instance' => array( 'Author' => array( 'parameters' => array( 'gateway' => 'DbGateway', 'cache' => 'Cache', 'log' => 'Log' ) ), 'DbGateway' => array( 'parameters' => array( 'db' => 'DbAdapter', ) ), 'DbAdapter' => array( 'parameters' => array( 'host' => 'somehost', 'user' => 'someuser', 'pass' => 'somepass', 'db' => ‘somedb' ) ), 'Cache' => array( ), 'Log' => array( ) ) ); $di = new ZendDiDi(); $di->configure(new ZendDiConfiguration($config)); $author = $di->get('Author'); 21 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 22.
    Location, location, location DiC – ZendDiDi() • The last line on the previous slide: $author = $di->get('Author'); Returns a fully configured Author object, following the definition we provided • DiC does instantiation and auto-wiring • ZendDi, although difficult to show here, is a complex solution for complex problems – it can easily be abused • For simpler problems, and more in line with the PHP philosophy, ServiceManager replaced it 22 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 23.
    Location, location, location Location v/s Injection • Service manager is a ServiceLocator !== DiC • BUT DiC === Service Locator • One main difference is that the subject requiring the dependencies is:  Not aware of the DiC when using DI (the dependency is injected)  Is aware of the locator in the case of SL, and asks the locator to obtain the dependency • ServiceManager is a service locator Think back to index.php: $serviceManager->get('ModuleManager')->loadModules(); • Martin Fowler is the subject matter expert on this: http://martinfowler.com/articles/injection.html 23 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 24.
    Location, location, location ZendServiceManagerServiceManager() • Service manager uses factories to generate its services • Factory can be a class implementing ZendServiceManagerFactoryInterface or a php closure 24 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 25.
    Location, location, location ZendServiceManagerServiceManager() <?php class ServiceConfiguration extends Configuration { public function configureServiceManager(ServiceManager $serviceManager) { $serviceManager->setFactory('author', function(ServiceManager $sm) { $dbGateway = $sm->get('db_gateway'); $cache = $sm->get('cache'); $log = $sm->get('log'); return new Author($dbGateway, $cache, $log); } ); $serviceManager->setShared('author', false); $serviceManager->setFactory('db_gateway', function(ServiceManager $sm){ $dbAdapter = $sm->get('db_adapter'); return new DbGateway($dbAdapter); } ); $serviceManager->setFactory('db_adapter', function(ServiceManager $sm) { //Using hard-coded values for the example, but normally you would either create a factory //and inject the values from a config file //or get the configuration from the ServiceManager and read the valuees in return new DbAdapter('somehost', 'someuser', 'somepass', 'somedb'); } ); $serviceManager->setFactory('cache', function(ServiceManager $sm) { return new Cache(); } ); $serviceManager->setFactory('log', function(ServiceManager $sm) { return new Log(); } ); } } $config = new ServiceConfiguration(); $sm = new ServiceManager(); $config->configureServiceManager($sm); $author = $sm->get('author'); 25 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 26.
    Location, location, location ZendServiceManagerServiceManager() • The example above purposely uses closures to illustrate that a full-fledged factory is not needed • But in a lot of cases, implementing a factory makes sense - rewriting DbAdapter from the above example as a factory: <?php use ZendServiceManagerFactoryInterface; //The factory class class AuthorFactory implements FactoryInterface { public function createService(ZendServiceManagerServiceLocatorInterface $sl) { $dbGateway = $sl->get('db_gateway'); $cache = $sl->get('cache'); $log = $sl->get('log'); return new Author($dbGateway, $cache, $log); } } //And the configuration class class ServiceConfiguration extends Configuration { public function configureServiceManager(ServiceManager $serviceManager) { //... $serviceManager->setFactory('author', 'AuthorFactory'); //... } } 26 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 27.
    Location, location, location ZendServiceManagerServiceManager() • The example above purposely uses closures to illustrate that a full-fledged factory is not needed • But in a lot of cases, implementing a factory makes sense - rewriting DbAdapter form the above example as a factory: <?php use ZendServiceManagerFactoryInterface; //The factory class class AuthorFactory implements FactoryInterface { public function createService(ZendServiceManagerServiceLocatorInterface $sl) { $dbGateway = $sl->get('db_gateway'); $cache = $sl->get('cache'); $log = $sl->get('log'); return new Author($dbGateway, $cache, $log); } } //And the configuration class class ServiceConfiguration extends Configuration { public function configureServiceManager(ServiceManager $serviceManager) { //... $serviceManager->setFactory('author', 'AuthorFactory'); //... } } 27 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 28.
    Location, location, location Setting and retrieving the locator • In most MVC components (e.g.Controllers) the ServiceManager component is composed automatically by the MVC stack • The interface ZendServiceManagerServiceLocatorAwareInterface can be implemented to ensure that a service locator is composed in the subject class • Two methods, SetServiceLocator(ServiceLocatorInterface $locator) and getServiceLocator() must be implemented • Notice that the Component is ServiceManager(), but the interface is ServiceLocatorInterface. This is to allow you to provide an alternative implementation of service locator. 28 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 29.
    Location, location, location Setting and retrieving the locator • Controllers implement ServiceLocatorAwareInterface therefore <?php namespace MymodController; use ZendMvcControllerActionController, ZendViewModelViewModel; class IndexController extends ActionController { public function indexAction() { $sl = $this->getServiceLocator(); $sl->get('author'); return new ViewModel(); } } 29 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 30.
    Locating the injectoror injecting the locator? • Although ServiceManager is now the goto dependency management component in ZF2, Di actually still exists as a fallback (by default) • One can specify a collection AbstractFactories to ServiceManager on which it will fall back if it does not find the target class • Therefore, ZendServiceManagerDiDiAbstractFactory is an abstract factory to which the name of the object is passed if it is not found by ServiceManager (the order can be changed) • You can also, of course, provide your own service factory (Proxy class to other frameworks maybe?) 30 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 31.
    Some references • ZF2 module site: http://modules.zendframework.com/ • Rob Allen’s Skeleton: https://github.com/zendframework/ZendSkeletonApplication • Matthew’s discussion on Modules: http://mwop.net/blog/267-Getting- started-writing-ZF2-modules.html • Ralph on the decisions that drove to ServiceManager: http://zend- framework-community.634137.n4.nabble.com/Services-Instances- Dependencies-in-ZF2-td4584632.html • SOLID OO design: http://en.wikipedia.org/wiki/SOLID_(object- oriented_design) 31 Zend Professional Services © All rights reserved. Zend Technologies, Inc.
  • 32.
    • Download ZF2and happy coding! http://zendframework.com/zf2 Thank You! http://twitter.com/mkherlakian 32 Zend Professional Services © All rights reserved. Zend Technologies, Inc.