zf2 phpquebec

32
© All rights reserved. Zend Technologies, Inc . ZF2 Modules and Services (And DI) Maurice Kherlakian Zend Technologies

Upload: mkherlakian

Post on 15-May-2015

2.733 views

Category:

Technology


2 download

DESCRIPTION

ZF2 Modules and Service locators....

TRANSCRIPT

Page 1: Zf2 phpquebec

©All rights reserved. Zend Technologies, Inc.

ZF2 Modules and Services (And DI)Maurice Kherlakian

Zend Technologies

Page 2: Zf2 phpquebec

©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]

Page 3: Zf2 phpquebec

©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

Page 4: Zf2 phpquebec

©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

Page 5: Zf2 phpquebec

©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!

Page 6: Zf2 phpquebec

©All rights reserved. Zend Technologies, Inc.

Requirements for a module – Directory structure

• There is, of course, a recommended directory structure

6 Zend Professional Services

Page 7: Zf2 phpquebec

©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( ), ),);

Page 8: Zf2 phpquebec

©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'); }}

Page 9: Zf2 phpquebec

©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); }}

Page 10: Zf2 phpquebec

©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(); }}

Page 11: Zf2 phpquebec

©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', ), ); }}

Page 12: Zf2 phpquebec

©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

Page 13: Zf2 phpquebec

©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', ), ),);

Page 14: Zf2 phpquebec

©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();

Page 15: Zf2 phpquebec

©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)

Page 16: Zf2 phpquebec

©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

Page 17: Zf2 phpquebec

©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

Page 18: Zf2 phpquebec

©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); }}

Page 19: Zf2 phpquebec

©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.

Page 20: Zf2 phpquebec

©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

Page 21: Zf2 phpquebec

©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');

Page 22: Zf2 phpquebec

©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

Page 23: Zf2 phpquebec

©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

Page 24: Zf2 phpquebec

©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

Page 25: Zf2 phpquebec

©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');

Page 26: Zf2 phpquebec

©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'); //... }}

Page 27: Zf2 phpquebec

©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'); //... }}

Page 28: Zf2 phpquebec

©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

Page 29: Zf2 phpquebec

©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(); }}

Page 30: Zf2 phpquebec

©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

Page 31: Zf2 phpquebec

©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

Page 32: Zf2 phpquebec

©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