Upload
mkherlakian
View
2.736
Download
2
Tags:
Embed Size (px)
DESCRIPTION
ZF2 Modules and Service locators....
Citation preview
©All rights reserved. Zend Technologies, Inc.
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
2 Zend Professional Services
Linkedin: http://ca.linkedin.com/in/mkherlakian
Twitter: http://twitter.com/mkherlakian
Email: [email protected]
©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
4 Zend Professional Services
ZF2 addresses all of these problems
©All rights reserved. Zend Technologies, Inc.
Requirements for a module – Module class
• A namespaced class called Module
5 Zend Professional Services
<?php
namespace Mymod;class Module {}
• That’s it!
©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
7 Zend Professional Services
<?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( ), ),);
©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)
8 Zend Professional Services
<?phpnamespace Mymod;
use Zend\ModuleManager\ModuleManager, Zend\Mvc\MvcEvent;
class Module { public function getConfig() { return include(__DIR__.'/config/module.config.php'); }}
©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
9 Zend Professional Services
<?phpnamespace Mymod;
use Zend\ModuleManager\ModuleManager, Zend\Mvc\MvcEvent;
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); }}
©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
10 Zend Professional Services
<?phpnamespace Mymod;
use Zend\ModuleManager\ModuleManager, Zend\Mvc\MvcEvent;
class Module { public function onBootstrap(MvcEvent $e) { $request = $e->getRequest(); }}
©All rights reserved. Zend Technologies, Inc.
Optional for a module – Dep solving
• Thru getProvides() and getDependencies()
• Off by default, can be turned on
11 Zend Professional Services
<?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', ), ); }}
©All rights reserved. Zend Technologies, Inc.
ModuleManager
• It’s automatically instantiated in your Zend\Application
• 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
13 Zend Professional Services
<?php //module.config.phpreturn array( 'router' => array( 'routes' => array( 'Mymod' => array( 'type' => 'Zend\Mvc\Router\Http\Literal', 'options' => array( 'route' => '/mymod', 'defaults' => array( 'controller' => 'mymod', 'action' => 'index', ), ), ), ), ), 'controller' => array( 'classes' => array( 'mymod' => 'Mymod\Controller\IndexController', ), ), 'view_manager' => array( 'template_path_stack' => array( __DIR__ . '/../view', ), ),);
©All rights reserved. Zend Technologies, Inc.
The missing link - bootstrapping – index.php
14 Zend Professional Services
<?php // /public/index.php
require_once 'Loader/StandardAutoloader.php';use Zend\Loader\StandardAutoloader, Zend\ServiceManager\ServiceManager, Zend\Mvc\Service\ServiceManagerConfiguration;
$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 configurationchdir(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();
©All rights reserved. Zend Technologies, Inc.
The missing link - bootstrapping – index.php
15 Zend Professional Services
• 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)
©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, locationDI - short summary
• Since the presentation was originally about Zend\Di 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
18 Zend Professional Services
<?php //No DIclass 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); }}
©All rights reserved. Zend Technologies, Inc.
Location, location, locationDI - short summary
19 Zend Professional Services
<?php //DIclass 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.
©All rights reserved. Zend Technologies, Inc.
Location, location, locationDI - short summary
20 Zend Professional Services
<?php
$db = new DbAdapter();$gateway = new DbGateway($db);$cache = new Cache();$log = new Log();
$author = new Author($db, $gateway, $cache, $log);$authors = $author->fetchAll();
• Consider the case that an Author needs a DbGateway, which needs a DbAdapter, and a logging and caching class
• 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 – Zend\Di is one of them, so is the Symfony DiC, and a number of others
©All rights reserved. Zend Technologies, Inc.
Location, location, locationDiC – Zend\Di\Di()
21 Zend Professional Services
<?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 Zend\Di\Di();$di->configure(new Zend\Di\Configuration($config));
$author = $di->get('Author');
©All rights reserved. Zend Technologies, Inc.
Location, location, locationDiC – Zend\Di\Di()
• 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
• Zend\Di, 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, locationLocation 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 Zend\ServiceManager\ServiceManager()
• Service manager uses factories to generate its services
• Factory can be a class implementing Zend\ServiceManager\FactoryInterface or a php closure
24 Zend Professional Services
©All rights reserved. Zend Technologies, Inc.
Location, location, locationZend\ServiceManager\ServiceManager()
25 Zend Professional Services
<?phpclass 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');
©All rights reserved. Zend Technologies, Inc.
Location, location, locationZend\ServiceManager\ServiceManager()
• 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:
26 Zend Professional Services
<?phpuse Zend\ServiceManager\FactoryInterface;
//The factory classclass AuthorFactory implements FactoryInterface { public function createService(Zend\ServiceManager\ServiceLocatorInterface $sl) { $dbGateway = $sl->get('db_gateway'); $cache = $sl->get('cache'); $log = $sl->get('log'); return new Author($dbGateway, $cache, $log); }}
//And the configuration classclass ServiceConfiguration extends Configuration{ public function configureServiceManager(ServiceManager $serviceManager) { //... $serviceManager->setFactory('author', 'AuthorFactory'); //... }}
©All rights reserved. Zend Technologies, Inc.
Location, location, locationZend\ServiceManager\ServiceManager()
• 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:
27 Zend Professional Services
<?phpuse Zend\ServiceManager\FactoryInterface;
//The factory classclass AuthorFactory implements FactoryInterface { public function createService(Zend\ServiceManager\ServiceLocatorInterface $sl) { $dbGateway = $sl->get('db_gateway'); $cache = $sl->get('cache'); $log = $sl->get('log'); return new Author($dbGateway, $cache, $log); }}
//And the configuration classclass ServiceConfiguration extends Configuration{ public function configureServiceManager(ServiceManager $serviceManager) { //... $serviceManager->setFactory('author', 'AuthorFactory'); //... }}
©All rights reserved. Zend Technologies, Inc.
Location, location, locationSetting and retrieving the locator
• In most MVC components (e.g.Controllers) the ServiceManager component is composed automatically by the MVC stack
• The interface Zend\ServiceManager\ServiceLocatorAwareInterface 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, locationSetting and retrieving the locator
• Controllers implement ServiceLocatorAwareInterface therefore
29 Zend Professional Services
<?php
namespace Mymod\Controller;
use Zend\Mvc\Controller\ActionController, Zend\View\Model\ViewModel;
class IndexController extends ActionController{ public function indexAction() { $sl = $this->getServiceLocator(); $sl->get('author'); return new ViewModel(); }}
©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, Zend\ServiceManager\Di\DiAbstractFactory 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