Escolar Documentos
Profissional Documentos
Cultura Documentos
A Brief History of ZF
ZF2 Patterns
ZF2 Patterns
Initial Announcements and Work October 2005: Announced the project March 2006: First public preview release, 0.1.0
ZF2 Patterns
Initial Announcements and Work October 2005: Announced the project March 2006: First public preview release, 0.1.0 Fall 2006: Rewrite of the MVC
ZF2 Patterns
1.0.0 - July 2007 First stable release Basic MVC system, with plugins, action helpers, automated view rendering etc.
ZF2 Patterns
1.0.0 - July 2007 First stable release Basic MVC system, with plugins, action helpers, automated view rendering etc. Many web service API consumers
ZF2 Patterns
1.0.0 - July 2007 First stable release Basic MVC system, with plugins, action helpers, automated view rendering etc. Many web service API consumers Server classes for XML-RPC, REST
ZF2 Patterns
ZF2 Patterns
ZF2 Patterns
1.5.0 - March 2008 First minor release Zend_Form introduced Zend_Layout introduced
ZF2 Patterns
1.5.0 - March 2008 First minor release Zend_Form introduced Zend_Layout introduced Layout-aware view helper system introduced
ZF2 Patterns
ZF2 Patterns
1.6.0 - September 2008 Dojo integration PHPUnit scaffolding for testing controllers
ZF2 Patterns
1.6.0 - September 2008 Dojo integration PHPUnit scaffolding for testing controllers Introduction of the ContextSwitch action helper
ZF2 Patterns
ZF2 Patterns
ZF2 Patterns
ZF2 Patterns
ZF2 Patterns
1.8.0 - April 2009 Introduction of Zend_Tool Introduction of Zend_Application First widely usable release of ZF
ZF2 Patterns
ZF2 Patterns
10
ZF2 Patterns
10
1.9.0 - August 2009 Addition of Zend_Feed_Reader PHP 5.3 support/compatibility Primarily community-led additions
ZF2 Patterns
10
1.9.0 - August 2009 Addition of Zend_Feed_Reader PHP 5.3 support/compatibility Primarily community-led additions Beginning of monthly bug hunts in October
ZF2 Patterns
10
ZF2 Patterns
11
1.10.0 - January 2010 Integration of ControllerTestCase with Zend_Application Addition of Zend_Feed_Writer, marking completion of Zend_Feed refactoring
ZF2 Patterns
11
1.10.0 - January 2010 Integration of ControllerTestCase with Zend_Application Addition of Zend_Feed_Writer, marking completion of Zend_Feed refactoring Documentation changes: adoption of PhD to render end-user manual, introduction of comment system, and new Learning Zend Framework section
ZF2 Patterns
11
1.10.0 - January 2010 Integration of ControllerTestCase with Zend_Application Addition of Zend_Feed_Writer, marking completion of Zend_Feed refactoring Documentation changes: adoption of PhD to render end-user manual, introduction of comment system, and new Learning Zend Framework section Primarily community-led additions
ZF2 Patterns
11
ZF2 Patterns
12
1.11.0 November 2010 Mobile support via Zend_Http_UserAgent SimpleCloud API via Zend_Cloud
ZF2 Patterns
12
Incremental Improvements
Baby steps Convert code from vendor prexes (e.g. Zend_Foo) to PHP 5.3 namespaces
ZF2 Patterns
18
Baby steps Convert code from vendor prexes (e.g. Zend_Foo) to PHP 5.3 namespaces Refactor exceptions
ZF2 Patterns
18
Baby steps Convert code from vendor prexes (e.g. Zend_Foo) to PHP 5.3 namespaces Refactor exceptions Switch ZF to be autoload-only
ZF2 Patterns
18
Baby steps Convert code from vendor prexes (e.g. Zend_Foo) to PHP 5.3 namespaces Refactor exceptions Switch ZF to be autoload-only Improve and standardize the plugin system
ZF2 Patterns
18
Namespaces
ZF2 Patterns
21
The Problem Lengthy class names Difcult to refactor Difcult to retain semantics with shorter names
ZF2 Patterns
21
ZF2 Patterns
22
ZF2 Patterns
22
Basics Every class le declares a namespace One namespace per le Any class used that is not in the current namespace (or a subnamespace) is imported and typically aliased
ZF2 Patterns
22
Basics Every class le declares a namespace One namespace per le Any class used that is not in the current namespace (or a subnamespace) is imported and typically aliased Global resolution is discouraged, except in the case of classes referenced in strings
ZF2 Patterns
22
Namespace Example
ZF2 Patterns
23
Using Aliases
namespace Zend\Mvc; use Zend\Stdlib\Dispatchable, Zend\Di\ServiceLocator as Locator; class FrontController implements Dispatchable { public function __construct(Locator $locator) { $this->serviceLocator = $locator; } }
ZF2 Patterns
24
ZF2 Patterns
25
use Zend_Controller_Action as Controller; class FooController extends Controller {} // Later, this might become: use Zend\Controller\Action as Controller; class FooController extends Controller {}
ZF2 Patterns
26
ZF2 Patterns
27
Naming All code in the project is in the Zend namespace Each component denes a unique namespace
ZF2 Patterns
27
Naming All code in the project is in the Zend namespace Each component denes a unique namespace Classes within a component all live in that namespace or a subnamespace
ZF2 Patterns
27
Naming All code in the project is in the Zend namespace Each component denes a unique namespace Classes within a component all live in that namespace or a subnamespace Typically, a class named after the component is the gateway class
ZF2 Patterns
27
Naming Examples
ZF2 Patterns
28
Naming Examples
ZF2 Patterns
28
Interfaces Interfaces are named after nouns or adjectives, and describe what they provide
ZF2 Patterns
29
Interfaces Interfaces are named after nouns or adjectives, and describe what they provide In most cases, concrete implementations of interfaces live in a subnamespace named after the interface
ZF2 Patterns
29
Interfaces Interfaces are named after nouns or adjectives, and describe what they provide In most cases, concrete implementations of interfaces live in a subnamespace named after the interface More solid Contract-Oriented paradigm
ZF2 Patterns
29
Interface Examples
ZF2 Patterns
30
Interface Examples
namespace Zend\Session; use Traversable, ArrayAccess, Serializable, Countable; interface Storage extends Traversable, ArrayAccess, Serializable, Countable { /* ... */ }
ZF2 Patterns
30
Concrete Implementation
namespace Zend\Session\Storage; use ArrayObject, Zend\Session\Storage, Zend\Session\Exception; class ArrayStorage extends ArrayObject implements Storage { /* ... */ }
ZF2 Patterns
31
Exceptions
ZF2 Patterns
33
The Problem All exceptions derived from a common class Inability to extend semantic exception types offered in the SPL
ZF2 Patterns
33
The Problem All exceptions derived from a common class Inability to extend semantic exception types offered in the SPL Limited catching strategies
ZF2 Patterns
33
The Problem All exceptions derived from a common class Inability to extend semantic exception types offered in the SPL Limited catching strategies Hard dependency for each and every component
ZF2 Patterns
33
ZF2 Patterns
34
ZF2 Approach We eliminated Zend_Exception entirely Each component denes a marker Exception interface
ZF2 Patterns
34
ZF2 Approach We eliminated Zend_Exception entirely Each component denes a marker Exception interface Concrete exceptions live in an Exception subnamespace, and extend SPL exceptions
ZF2 Patterns
34
ZF2 Patterns
35
What the solution provides Catch specic exception types Catch SPL exception types
ZF2 Patterns
35
What the solution provides Catch specic exception types Catch SPL exception types Catch component-level exceptions
ZF2 Patterns
35
What the solution provides Catch specic exception types Catch SPL exception types Catch component-level exceptions Catch based on global exception type
ZF2 Patterns
35
Exception Denitions
ZF2 Patterns
36
Exception Denitions
ZF2 Patterns
36
Exception Denitions
namespace Zend\EventManager\Exception; use Zend\EventManager\Exception; class InvalidArgumentException extends \InvalidArgumentException implements Exception {}
ZF2 Patterns
36
Catching Exceptions
namespace Zend\EventManager\Exception; use Zend\EventManager\Exception; try { $events->trigger(foo.bar, $object); } catch (InvalidArgumentException $e) { } catch (Exception $e) { } catch (\InvalidArgumentException $e) { } catch (\Exception $e) { }
ZF2 Patterns
37
Autoloading
The Problem Performance issues Many classes are used JIT, and shouldnt be loaded until needed
ZF2 Patterns
39
The Problem Performance issues Many classes are used JIT, and shouldnt be loaded until needed Missing require_once calls lead to errors
ZF2 Patterns
39
ZF2 Patterns
40
ZF2 Approach No more require_once calls! Deliver multiple autoloading approaches ZF1-style include_path autoloader Per- namespace/vendor prex autoloading Class-Map autoloading
ZF2 Patterns
40
ZF1-Style Autoloading
require_once Zend/Loader/StandardAutoloader.php; $loader = new Zend\Loader\StandardAutoloader(array( fallback_autoloader => true, )); $loader->register();
ZF2 Patterns
41
require_once Zend/Loader/StandardAutoloader.php; $loader = new Zend\Loader\StandardAutoloader(); $loader->registerNamespace(My, __DIR__ . /../library/My) ->registerPrefix(Phly_, __DIR__ . /../library/Phly); $loader->register();
ZF2 Patterns
42
Class-Map Autoloading
ZF2 Patterns
43
Class-Map Autoloading
ZF2 Patterns
43
Class-Maps? Wont those require work? Yes, they will. But we already have a tool, bin/classmap_generator.php.
ZF2 Patterns
44
Class-Maps? Wont those require work? Yes, they will. But we already have a tool, bin/classmap_generator.php. Usage is trivial:
prompt> cd your/library prompt> php /path/to/classmap_generator.php -w # Class-Map now exists in .classmap.php
ZF2 Patterns
44
Why? Class-Maps show a 25% improvement on the ZF1 autoloader when no acceleration is present
ZF2 Patterns
45
Why? Class-Maps show a 25% improvement on the ZF1 autoloader when no acceleration is present
and 60-85% improvements when an opcode cache is in place!
ZF2 Patterns
45
Why? Class-Maps show a 25% improvement on the ZF1 autoloader when no acceleration is present
and 60-85% improvements when an opcode cache is in place!
Pairing namespaces/prexes with specic paths shows >10% gains with no acceleration
ZF2 Patterns
45
Why? Class-Maps show a 25% improvement on the ZF1 autoloader when no acceleration is present
and 60-85% improvements when an opcode cache is in place!
Pairing namespaces/prexes with specic paths shows >10% gains with no acceleration
and 40% improvements when an opcode cache is in place!
ZF2 Patterns
45
Autoloader Factory With multiple strategies comes the need for a factory
ZF2 Patterns
46
Autoloader Factory With multiple strategies comes the need for a factory Choose several strategies
ZF2 Patterns
46
Autoloader Factory With multiple strategies comes the need for a factory Choose several strategies Class-Map for fastest lookup
ZF2 Patterns
46
Autoloader Factory With multiple strategies comes the need for a factory Choose several strategies Class-Map for fastest lookup Namespace/prex paths for common code
ZF2 Patterns
46
Autoloader Factory With multiple strategies comes the need for a factory Choose several strategies Class-Map for fastest lookup Namespace/prex paths for common code ZF1/PSR0-style fallback autoloader for development
ZF2 Patterns
46
require_once Zend/Loader/AutoloaderFactory.php; use Zend\Loader\AutoloaderFactory; AutoloaderFactory::factory(array( Zend\Loader\ClassMapAutoloader => array( __DIR__ . /../library/.classmap.php, __DIR__ . /../application/.classmap.php, ), Zend\Loader\StandardAutoloader => array( namespaces => array( Zend => __DIR__ . /../library/Zend, ), fallback_autoloader => true, ), ));
ZF2 Patterns
47
Start Migrating
You can use the ZF2 autoloaders and class-map generation facilities today; start migrating now!
ZF2 Patterns
48
Plugin Loading
Terminology For our purposes, a plugin is any class that is determined at runtime.
ZF2 Patterns
50
Terminology For our purposes, a plugin is any class that is determined at runtime. Action and view helpers Adapters Filters and validators
ZF2 Patterns
50
ZF2 Patterns
51
The Problem Varying approaches to dynamically discovering plugin classes Paths relative to the calling class Prex-path stacks (most common) Setters to indicate classes
ZF2 Patterns
51
The Problem Varying approaches to dynamically discovering plugin classes Paths relative to the calling class Prex-path stacks (most common) Setters to indicate classes Most common approach is terrible
ZF2 Patterns
51
The Problem Varying approaches to dynamically discovering plugin classes Paths relative to the calling class Prex-path stacks (most common) Setters to indicate classes Most common approach is terrible Bad performance Hard to debug No caching of discovered plugins
ZF2 Patterns
51
ZF2 Patterns
52
ZF2 Patterns
52
namespace Zend\Loader; interface ShortNameLocator { public function isLoaded($name); public function getClassName($name); public function load($name); }
ZF2 Patterns
53
namespace Zend\Loader; interface Broker { public function public function public function public function public function public function public function }
load($plugin, array $options = null); getPlugins(); isLoaded($name); register($name, $plugin); unregister($name); setClassLoader(ShortNameLocator $loader); getClassLoader();
ZF2 Patterns
54
ZF2 Patterns
55
How do I use it? Create a default plugin loader Create a default plugin broker
ZF2 Patterns
55
How do I use it? Create a default plugin loader Create a default plugin broker Compose a broker into your class
ZF2 Patterns
55
How do I use it? Create a default plugin loader Create a default plugin broker Compose a broker into your class Optionally, dene static conguration
ZF2 Patterns
55
How do I use it? Create a default plugin loader Create a default plugin broker Compose a broker into your class Optionally, dene static conguration Optionally, pass broker and loader conguration
ZF2 Patterns
55
How do I use it? Create a default plugin loader Create a default plugin broker Compose a broker into your class Optionally, dene static conguration Optionally, pass broker and loader conguration Optionally, register plugins with locator and/or broker
ZF2 Patterns
55
namespace Zend\View; use Zend\Loader\PluginClassLoader; class HelperLoader extends PluginClassLoader { /** * @var array Pre-aliased view helpers */ protected $plugins = array( action => Zend\View\Helper\Action, base_url => Zend\View\Helper\BaseUrl, /* ... */ ); }
ZF2 Patterns
56
class HelperBroker extends PluginBroker protected $defaultClassLoader = Zend\View\HelperLoader; public function load($plugin, array $options = null) { $helper = parent::load($plugin, $options); if (null !== ($view = $this->getView())) { $helper->setView($view); } return $helper; } protected function validatePlugin($plugin) { if (! $plugin instanceof Helper) { throw new InvalidHelperException(); } return true; } }
ZF2 Patterns
57
Composing a broker
use Zend\View\HelperLoader; class Foo { protected $broker; public function broker($spec = null, array $options = array()) { if ($spec instanceof Broker) { $this->broker = $spec; return $spec; } elseif (null === $this->broker) { $this->broker = new PluginBroker(); } if (null === $spec) { return $this->broker; } elseif (!is_string($spec)) { throw new \Exception(); } return $this->broker->load($spec, $options); } }
ZF2 Patterns
58
Locator Precedence (From least specic to most specic) Map dened in concrete plugin loader
ZF2 Patterns
59
Locator Precedence (From least specic to most specic) Map dened in concrete plugin loader Static maps (latest registration having precedence)
ZF2 Patterns
59
Locator Precedence (From least specic to most specic) Map dened in concrete plugin loader Static maps (latest registration having precedence) Mapping passed via instantiation
ZF2 Patterns
59
Locator Precedence (From least specic to most specic) Map dened in concrete plugin loader Static maps (latest registration having precedence) Mapping passed via instantiation Explicit mapping provided programmatically
ZF2 Patterns
59
use Zend\View\HelperLoader; HelperLoader::addStaticMap(array( url => My\Helper\Url, base_url => Project\Helper\BaseUrl, )); $loader = new HelperLoader(); $class = $loader->load(url); // "My\Helper\Url"
ZF2 Patterns
60
use Zend\View\HelperLoader; $config = array( url => My\Helper\Url, base_url => Project\Helper\BaseUrl, ); $loader = new HelperLoader($config); $class = $loader->load(url); // "My\Helper\Url"
ZF2 Patterns
61
use Zend\View\HelperLoader, Zend\Loader\PluginClassLoader; class HelperMap extends PluginClassLoader { protected $plugins = array( url => My\Helper\Url, base_url => Project\Helper\BaseUrl, ); } $helpers = new HelperMap(); $loader = new HelperLoader($helpers); $class = $loader->load(url); // "My\Helper\Url"
ZF2 Patterns
62
Extending loaders
use Zend\View\HelperLoader; class HelperMap extends HelperLoader { public function __construct($options = null) { // Addes to and/or overrides map in HelperLoader $this->registerPlugins(array( url => My\Helper\Url, base_url => Project\Helper\BaseUrl, )); parent::__construct($options); } } $helpers = new HelperMap(); $class = $loader->load(url); // "My\Helper\Url"
ZF2 Patterns
63
use Zend\View\HelperBroker; $broker = new HelperBroker(array( class_loader => array( class => HelperMap, options => array( base_url => App\Helper\BaseUrl, ), ), )); $plugin = $broker->load(base_url); // "App\Helper\BaseUrl"
ZF2 Patterns
64
use Zend\View\HelperLoader; $loader = new HelperLoader(); $loader->registerPlugin(url, My\Helper\Url) ->registerPlugins(array( base_url => Project\Helper\BaseUrl, )); $class = $loader->load(url); // "My\Helper\Url"
ZF2 Patterns
65
Managing plugins via a broker By default, it consults the loader for a classname, and instantiates that class with the given arguments
ZF2 Patterns
66
Managing plugins via a broker By default, it consults the loader for a classname, and instantiates that class with the given arguments Optionally, you can also seed the broker, manually registering plugin objects under a given name
ZF2 Patterns
66
use My\Helper\Url; // // // // Assumes: - $request == Request object - $router == Router object - $broker == HelperBroker
$url = new Url($request, $router); $broker->registerPlugin(url, $url); // OR: $broker->registerPlugins(array( url => $url, )); $url = $broker->load(url); // === $url from above
ZF2 Patterns
67
ZF2 Patterns
68
What about lazy-loading? Often you need to congure plugins But you dont want an instance hanging around until its actually requested
ZF2 Patterns
68
What about lazy-loading? Often you need to congure plugins But you dont want an instance hanging around until its actually requested Enter the Zend\Loader\LazyLoadingBroker
ZF2 Patterns
68
LazyLoadingBroker Interface
namespace Zend\Loader; interface LazyLoadingBroker extends Broker { public function registerSpec($name, array $spec = null); public function registerSpecs($specs); public function unregisterSpec($name); public function getRegisteredPlugins(); public function hasPlugin($name); }
ZF2 Patterns
69
ZF2 Patterns
70
Using the LazyLoadingBroker Register specs with the broker When that plugin is requested, the provided options will be used unless new options are provided
ZF2 Patterns
70
Using the LazyLoadingBroker Register specs with the broker When that plugin is requested, the provided options will be used unless new options are provided In all other ways, it behaves like other brokers, including allowing explicit registration of plugins
ZF2 Patterns
70
LazyLoadingBroker Usage
$broker->registerSpec(url, array($request, $router)); $broker->registerSpecs(array( url => array($request, $router), )); if (!$broker->hasPlugin(url)) { // no spec! } $plugins = $broker->getRegisteredPlugins(); // array(url) $url = $broker->load(url); // With $request, $router injected
ZF2 Patterns
71
use Zend\View\HelperBroker; $config = array( specs => array( url => array($request, $rourter), ), ); $broker = new HelperBroker($config); $url = $broker->load(url); // With $request, $router injected
ZF2 Patterns
72
New Components
ZF2 Patterns
75
The EventManager
ZF2 Patterns
77
The Problem How do we introduce logging/debug points in framework code? How do we allow users to introduce caching without needing to extend framework code?
ZF2 Patterns
77
The Problem How do we introduce logging/debug points in framework code? How do we allow users to introduce caching without needing to extend framework code? How do we allow users to introduce validation, ltering, ACL checks, etc., without needing to extend framework code?
ZF2 Patterns
77
The Problem How do we introduce logging/debug points in framework code? How do we allow users to introduce caching without needing to extend framework code? How do we allow users to introduce validation, ltering, ACL checks, etc., without needing to extend framework code? How do we allow users to manipulate the order in which plugins, intercepting lters, events, etc., trigger?
ZF2 Patterns
77
The Problem How do we introduce logging/debug points in framework code? How do we allow users to introduce caching without needing to extend framework code? How do we allow users to introduce validation, ltering, ACL checks, etc., without needing to extend framework code? How do we allow users to manipulate the order in which plugins, intercepting lters, events, etc., trigger? How can we provide tools for userland code to benet from the above?
ZF2 Patterns 77
Solution: Aspect Oriented Programming Code denes various aspects that may be interesting to observe and/or attach to from a consumer
ZF2 Patterns
78
Solution: Aspect Oriented Programming Code denes various aspects that may be interesting to observe and/or attach to from a consumer Basically, all of the solutions well look at can be used to implement AOP in a code base.
ZF2 Patterns
78
ZF2 Patterns
79
Requirements Reasonably easy to understand design Allow static or per-instance attachment of handlers, preferably both
ZF2 Patterns
79
Requirements Reasonably easy to understand design Allow static or per-instance attachment of handlers, preferably both
Preferably while retaining non-global state or allowing overriding
ZF2 Patterns
79
Requirements Reasonably easy to understand design Allow static or per-instance attachment of handlers, preferably both
Preferably while retaining non-global state or allowing overriding
ZF2 Patterns
79
Requirements Reasonably easy to understand design Allow static or per-instance attachment of handlers, preferably both
Preferably while retaining non-global state or allowing overriding
ZF2 Patterns
79
Requirements Reasonably easy to understand design Allow static or per-instance attachment of handlers, preferably both
Preferably while retaining non-global state or allowing overriding
Allow interruption of execution Allow prioritization of handlers Predictability of arguments passed to handlers
ZF2 Patterns
79
Requirements Reasonably easy to understand design Allow static or per-instance attachment of handlers, preferably both
Preferably while retaining non-global state or allowing overriding
Allow interruption of execution Allow prioritization of handlers Predictability of arguments passed to handlers Ability to attach to many event-emitting components at once
ZF2 Patterns
79
ZF2 Patterns
80
ZF2 Patterns
80
Cons
Typically, cannot interrupt execution of remaining observers
ZF2 Patterns
80
Cons
Typically, cannot interrupt execution of remaining observers Requires a system for each component and/or class
ZF2 Patterns
80
Cons
Typically, cannot interrupt execution of remaining observers Requires a system for each component and/or class Typically, no ability to prioritize handlers
ZF2 Patterns
80
ZF2 Patterns
81
ZF2 Patterns
81
ZF2 Patterns
81
ZF2 Patterns
81
Cons
Often, need to test the Event provided to ensure you can handle it
ZF2 Patterns
81
Cons
Often, need to test the Event provided to ensure you can handle it Global usage means static aggregation and/or static dependencies
ZF2 Patterns
81
Cons
Often, need to test the Event provided to ensure you can handle it Global usage means static aggregation and/or static dependencies . . . but per-component means boiler-plate to compose in each class using it
ZF2 Patterns
81
Cons
Often, need to test the Event provided to ensure you can handle it Global usage means static aggregation and/or static dependencies . . . but per-component means boiler-plate to compose in each class using it Typically, no ability to prioritize handlers
ZF2 Patterns
81
Cons
Often, need to test the Event provided to ensure you can handle it Global usage means static aggregation and/or static dependencies . . . but per-component means boiler-plate to compose in each class using it Typically, no ability to prioritize handlers
ZF2 Patterns
82
ZF2 Patterns
82
ZF2 Patterns
82
ZF2 Patterns
82
Cons
Verbiage is not well-known amongst PHP developers
ZF2 Patterns
82
Cons
Verbiage is not well-known amongst PHP developers Arguments will vary between signals
ZF2 Patterns
82
Cons
Verbiage is not well-known amongst PHP developers Arguments will vary between signals Same issues with composition per-class and static usage as seen in event systems
ZF2 Patterns
82
ZF2 Patterns
83
ZF2 Patterns
83
ZF2 Patterns
83
Cons
Sometimes difcult to accomplish complex workows
ZF2 Patterns
83
Cons
Sometimes difcult to accomplish complex workows Same issues with composition per-class and static usage as seen in event systems
ZF2 Patterns
83
Cons
Sometimes difcult to accomplish complex workows Same issues with composition per-class and static usage as seen in event systems Easy to forget to invoke next lter in chain
ZF2 Patterns
83
Cons
Sometimes difcult to accomplish complex workows Same issues with composition per-class and static usage as seen in event systems Easy to forget to invoke next lter in chain Typically, no ability to prioritize lters
ZF2 Patterns
83
ZF2: EventManager Component Cherry-picks from each of PubSub, SignalSlot, and Intercepting Filters to provide a comprehensive solution
ZF2 Patterns
84
ZF2: EventManager Component Cherry-picks from each of PubSub, SignalSlot, and Intercepting Filters to provide a comprehensive solution Cannot completely solve the composition/static usage issues
We can solve the composition problem in PHP 5.4 via Traits
ZF2 Patterns
84
ZF2: EventManager Component Cherry-picks from each of PubSub, SignalSlot, and Intercepting Filters to provide a comprehensive solution Cannot completely solve the composition/static usage issues
We can solve the composition problem in PHP 5.4 via Traits There are some elegant ways to handle static usage
ZF2 Patterns
84
EventCollection Interface
namespace Zend\EventManager; use Zend\Stdlib\CallbackHandler; interface EventCollection { public function trigger($event, $context, $argv = array()); public function triggerUntil($event, $context, $argv, $callback); public function attach($event, $callback, $priority = 1); public function detach(CallbackHandler $handle); public function getEvents(); public function getHandlers($event); public function clearHandlers($event); }
ZF2 Patterns
85
Triggering Events
use Zend\EventManager\EventManager; $events = new EventManager(); $events->trigger($eventName, $object, $params); /* Where: * - $eventName is the name of the event; usually the current method name * * - $object is the object triggering the event * - $params are the parameters the handler might need to access, usually the method arguments * */
ZF2 Patterns
86
CallbackHandler
$handler = $events->attach(some-event, function($e) use ($log) { $event = $e->getName(); $context = get_class($e->getTarget()); $params = json_encode($e->getParams()); $log->info(sprintf("%s: %s: %s", $event, $context, $params)); });
ZF2 Patterns
87
$handler = $events->attach(some-event, function($e) use ($log) { /* same as before */ }, 100); // Prioritize! (higher numbers win)
ZF2 Patterns
88
$results = $events->triggerUntil(some-event, $o, $argv, function($result) { return ($result instanceof SomeType); }); if ($results->stopped()) { return $results->last(); }
ZF2 Patterns
89
$events->attach(some-event, function($e) { $result = new Result; $e->stopPropagation(true); return $result; }); $results = $events->trigger(some-event, $object, $params); if ($results->stopped()) { return $results->last(); }
ZF2 Patterns
90
Composing an EventManager
use Zend\EventManager\EventCollection as Events, Zend\EventManager\EventManager; class Foo { protected $events; public function events(Events $events = null) { if (null !== $events) { $this->events = $events; } elseif (null === $this->events) { $this->events = new EventManager(__CLASS__); } return $this->events; } public function doSomething($param1, $param2) { $params = compact(param1, param2); $this->events()->trigger(__FUNCTION__, $this, $params); } } ZF2 Patterns 91
Using a Trait!
use Zend\EventManager\EventCollection as Events, Zend\EventManager\EventManager; trait Eventful { public function events(Events $events = null) { if (null !== $events) { $this->events = $events; } elseif (null === $this->events) { $this->events = new EventManager(__CLASS__); } return $this->events; } } class Foo { use Eventful; protected $events; }
ZF2 Patterns
92
use Zend\EventManager\StaticEventManager; $events = StaticEventManager::getInstance(); $events->connect(Foo, some-event, function ($e) { /* ... */ });
ZF2 Patterns
93
ZF2 Patterns
94
Provide the EventManager constructor with both the class name and one or more service names, to make static attachment more semantic
ZF2 Patterns
94
Provide the EventManager constructor with both the class name and one or more service names, to make static attachment more semantic
This allows a single callback to listen to many components!
ZF2 Patterns
94
Dependency Injection
ZF2 Patterns
96
ZF2 Patterns
96
ZF2 Patterns
97
So, why do people fear it? They dont. They fear Dependency Injection Containers.
ZF2 Patterns
97
Put simply: an object graph for mapping dependency relations between objects.
ZF2 Patterns
98
ZF2 Patterns
99
namespace My\Helper; class Url { public function __construct(Request $request) { $this->request = $request; } public function setRouter(Router $router) { $this->router = $router; } }
ZF2 Patterns
100
ZF2 Patterns
101
$urlHelper = $di->get(url-helper); echo $url->generate(/css/site.css); echo $url->generate(array(id => $id), array(name => blog));
ZF2 Patterns
102
ZF2 Patterns
103
ZF2 Patterns
103
ZF2 Patterns
103
ZF2 Patterns
103
ZF2 Patterns
103
ZF2 Patterns
103
ZF2 Patterns
103
If instantiation of your object is not under your direct control (e.g. controllers), how do you retain control over your dependencies?
ZF2 Patterns
104
If instantiation of your object is not under your direct control (e.g. controllers), how do you retain control over your dependencies?
Different data access based on application environment
ZF2 Patterns
104
If instantiation of your object is not under your direct control (e.g. controllers), how do you retain control over your dependencies?
Different data access based on application environment Substituting mock/stub implementations during testing
ZF2 Patterns
104
ZF2 Patterns
105
ZF2 Approach Standardize on a Service Locator interface Provide a performant DI solution, and integrate it into a Service Locator
ZF2 Patterns
105
ZF2 Approach Standardize on a Service Locator interface Provide a performant DI solution, and integrate it into a Service Locator Provide tooling to aid in creating DI denitions during development
ZF2 Patterns
105
namespace Zend\Di; interface ServiceLocation { public function set($name, $service); public function get($name, array $params = null); }
ZF2 Patterns
106
namespace Zend\Di; interface DependencyInjection { public function get($name, array $params = null); public function newInstance($name, array $params = null); public function setDefinitions($definitions); public function setDefinition( DependencyDefinition $definition, $serviceName = null); public function setAlias($alias, $serviceName); public function getDefinitions(); public function getAliases(); }
ZF2 Patterns
107
Denitions
namespace Zend\Di; interface DependencyDefinition { public function __construct($className); public function getClass(); public function setConstructorCallback($callback); public function getConstructorCallback(); public function hasConstructorCallback(); public function setParam($name, $value); public function setParams(array $params); public function setParamMap(array $map); public function getParams(); public function setShared($flag = true); public function isShared(); public function addTag($tag); public function addTags(array $tags); public function getTags(); public function hasTag($tag); public function addMethodCall($name, array $args); public function getMethodCalls(); } ZF2 Patterns 108
References
namespace Zend\Di; interface DependencyReference { public function __construct($serviceName); public function getServiceName(); }
ZF2 Patterns
109
Class Denition
$mongoDB = new Definition(MongoDB); $mongoDB->setParam(conn, new Reference(mongo)) ->setParam(name, test); $coll = new Definition(MongoCollection); $coll->setParam(db, new Reference(mongodb)) ->setParam(name, resource); $di->setDefinitions(array( mongo => $mongo, mongodb => $mongoDB, resource => $coll, )); $resource = $di->get(resource);
ZF2 Patterns
110
Setter Injection
use Zend\Di\Definition, Zend\Di\Reference; $service = new Definition(mwop\Service\Resources); $service->addMethod(setResource, array( new Reference(resource) )); $di->setDefinition(resources, $service); $resources = $di->get(resources);
ZF2 Patterns
111
ZF2 Patterns
112
Making it faster Specify constructor parameter maps in denitions Generate Service Locators from a DI container
ZF2 Patterns
112
Parameter maps
$mongoDB->setParam(conn, new Reference(mongo)) ->setParam(name, test) ->setParamMap(array( conn => 0, name => 1, )); // Ensures parameters are in order, without needing // to resort to Reflection API.
ZF2 Patterns
113
use Zend\Di\ContainerBuilder as DiBuilder; $builder = new DiBuilder($injector); $builder->setContainerClass(AppContext); $container = $builder->getCodeGenerator( __DIR__ . /../application/AppContext.php ); // Returns instance of Zend\CodeGenerator\Php\PhpFile $container->write(); // Write to disk
ZF2 Patterns
114
use Zend\Di\DependencyInjectionContainer; class AppContext extends DependencyInjectionContainer { public function get($name, array $params = array()) { switch ($name) { case request: case Zend\Http\Request: return $this->getZendHttpRequest(); default: return parent::get($name, $params); } } public function getZendHttpRequest() { if (isset($this->services[Zend\Http\Request])) { return $this->services[Zend\Http\Request]; } $object = new \Zend\Http\Request(); $this->services[Zend\Http\Request] = $object; return $object; } } ZF2 Patterns 115
$context = new AppContext(); $request = $context->get(request); // Same as using a Service Locator or DI Container!
ZF2 Patterns
116
ZF2 Patterns
117
Making it simpler Use conguration les Can use any format supported by Zend\Config
ZF2 Patterns
117
Conguration (JSON)
{ "production": { "definitions": [ { "class": "Mongo" }, { "class": "MongoDB", "params": { "conn": {"__reference": "mongocxn"}, "name": "mwoptest" }, "param_map": { "conn": 0, "name": 1 } }, { "class": "MongoCollection", "params": { "db": {"__reference": "MongoDB"}, "name": "entries" }, "param_map": { "db": 0, "name": 1 } } ], "aliases": { "mongocxn": "Mongo", "mongo-collection-entries": "MongoCollection" } } } ZF2 Patterns 118
ZF2 Patterns
119
ZF2 Patterns
119
An Action Controller
namespace Blog\Controller; class Entry implements Dispatchable { public function setResource(Resource $resource) { $this->resource = $resource; } public function dispatch(Request $request, Response $response = null) { /* ... */ $entry = $this->resource->get($id); /* ... */ } }
ZF2 Patterns
120
class FrontController implements Dispatchable { public function __construct(DependencyInjection $di) { $this->di = $di; } public function dispatch(Request $request, Response $response = null) { /* ... */ $controller = $this->di->get($controllerName); $result = $controller->dispatch($request, $response); /* ... */ } }
ZF2 Patterns
121
ZF2 Patterns
122
ZF2 Patterns
122
Benets to using DI this way Performance Code de-coupling Simplication of controller code
ZF2 Patterns
122
ZF2 Patterns
123
More to come! First-run compilation Tools for scanning classes or namespaces to build denitions
ZF2 Patterns
123
More to come! First-run compilation Tools for scanning classes or namespaces to build denitions Interface injection
ZF2 Patterns
123
More to come! First-run compilation Tools for scanning classes or namespaces to build denitions Interface injection . . . and likely more.
ZF2 Patterns
123
MVC Patterns
ZF2 Patterns
125
The Problems How do controllers get dependencies? How do we accommodate different controller patterns?
What if we want to ne-tune action selection after routing, based on other data in the request environment?
ZF2 Patterns
125
The Problems How do controllers get dependencies? How do we accommodate different controller patterns?
What if we want to ne-tune action selection after routing, based on other data in the request environment? What if we want to pass arguments to action names, or prevalidate arguments?
ZF2 Patterns
125
The Problems How do controllers get dependencies? How do we accommodate different controller patterns?
What if we want to ne-tune action selection after routing, based on other data in the request environment? What if we want to pass arguments to action names, or prevalidate arguments? What if we dont like the Action sufx in action methods?
ZF2 Patterns
125
The Problems How do controllers get dependencies? How do we accommodate different controller patterns?
What if we want to ne-tune action selection after routing, based on other data in the request environment? What if we want to pass arguments to action names, or prevalidate arguments? What if we dont like the Action sufx in action methods? What if . . . ?
ZF2 Patterns
125
The Problems How do controllers get dependencies? How do we accommodate different controller patterns?
What if we want to ne-tune action selection after routing, based on other data in the request environment? What if we want to pass arguments to action names, or prevalidate arguments? What if we dont like the Action sufx in action methods? What if . . . ?
How can we better use server components within the MVC? How can we make the MVC more performant?
ZF2 Patterns
125
ZF2 Patterns
126
namespace Zend\Stdlib; interface Dispatchable { public function dispatch( Request $request, Response $response = null ); }
ZF2 Patterns
127
Request and Response Both the Request and Response simply aggregate metadata and content
ZF2 Patterns
128
Request and Response Both the Request and Response simply aggregate metadata and content The Response also has the ability to send itself
ZF2 Patterns
128
Request and Response Both the Request and Response simply aggregate metadata and content The Response also has the ability to send itself HTTP-specic variants will be the core of the MVC
To provide convenience around superglobals, cookies, and common tasks such as determining Accept and Content-Type headers
ZF2 Patterns
128
ZF2 Patterns
129
ZF2 Patterns
129
ZF2 Patterns
129
ZF2 Patterns
129
public function dispatch(Request $request, Response $response = null) { $params = compact(request, response); $this->events()->trigger(__FUNCTION__ . .route.pre, $this, $params); $result = $this->getRouter()->route($request); if (!$result) { $result = array(controller => page, page => 404); } $params[routing] = (object) $result; $this->events()->trigger(__FUNCTION__ . .route.post, $params); $controller = $this->di->get($params[routing]->controller); if (!$controller instanceof Dispatchable) { $controller = new NotFoundController(); } $result = $controller->dispatch($request, $response); $params[__RESULT__] = $result; $this->events()->trigger(__FUNCTION__ . .dispatch.post, $params); return $response; } ZF2 Patterns 130
Getting Involved
Contribute to ZF2! ZF2 wiki: http://bit.ly/zf2wiki zf-contributors mailing list: zf-contributors-subscribe@lists.zend.com IRC: #zftalk.dev on Freenode
ZF2 Patterns
133
ZF2 Git Repository Git guide: http://bit.ly/zf2gitguide GitHub: http://github.com/zendframework/zf2 Ofcial repo: git://git.zendframework.com/zf.git http://git.zendframework.com/ You still need to sign a CLA!
ZF2 Patterns
134
References ZF2 Dependency Injection Proposal http://bit.ly/zf2diproposal http://bit.ly/zf2diprototype ZF2 DI/MVC prototype sandbox http://bit.ly/zf2sandbox (mobile-layout branch is latest)
ZF2 Patterns
135
ZF2 Patterns
136