Initial Announcements and Work• October 2005: Announced the project ZF2 Patterns 4
Initial Announcements and Work• October 2005: Announced the project• March 2006: First public preview release, 0.1.0 ZF2 Patterns 4
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 4
1.0.0 - July 2007• First stable release• Basic MVC system, with plugins, action helpers, automated view rendering etc. ZF2 Patterns 5
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 5
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 5
1.5.0 - March 2008• First minor release ZF2 Patterns 6
1.5.0 - March 2008• First minor release• Zend_Form introduced ZF2 Patterns 6
1.5.0 - March 2008• First minor release• Zend_Form introduced• Zend_Layout introduced ZF2 Patterns 6
1.5.0 - March 2008• First minor release• Zend_Form introduced• Zend_Layout introduced• Layout-aware view helper system introduced ZF2 Patterns 6
1.6.0 - September 2008• Dojo integration ZF2 Patterns 7
1.6.0 - September 2008• Dojo integration• PHPUnit scaffolding for testing controllers ZF2 Patterns 7
1.6.0 - September 2008• Dojo integration• PHPUnit scaffolding for testing controllers• Introduction of the ContextSwitch action helper ZF2 Patterns 7
1.8.0 - April 2009• Introduction of Zend_Tool ZF2 Patterns 9
1.8.0 - April 2009• Introduction of Zend_Tool• Introduction of Zend_Application ZF2 Patterns 9
1.8.0 - April 2009• Introduction of Zend_Tool• Introduction of Zend_Application• First widely usable release of ZF ZF2 Patterns 9
1.9.0 - August 2009• Addition of Zend_Feed_Reader ZF2 Patterns 10
1.9.0 - August 2009• Addition of Zend_Feed_Reader• PHP 5.3 support/compatibility 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
1.10.0 - January 2010• Integration of ControllerTestCase with Zend_Application 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
1.11.0 November 2010• Mobile support via Zend_Http_UserAgent ZF2 Patterns 12
1.11.0 November 2010• Mobile support via Zend_Http_UserAgent• SimpleCloud API via Zend_Cloud ZF2 Patterns 12
Where do we go from here?
Zend Framework 2.0’s focusis on improving consistency and performance
Incremental Improvements
Baby steps• Convert code from vendor prefixes (e.g. “Zend_Foo”) to PHP 5.3 namespaces ZF2 Patterns 18
Baby steps• Convert code from vendor prefixes (e.g. “Zend_Foo”) to PHP 5.3 namespaces• Refactor exceptions ZF2 Patterns 18
Baby steps• Convert code from vendor prefixes (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 prefixes (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
The Problem• Lengthy class names • Difficult to refactor ZF2 Patterns 21
The Problem• Lengthy class names • Difficult to refactor • Difficult to retain semantics with shorter names ZF2 Patterns 21
Basics• Every class file declares a namespace ZF2 Patterns 22
Basics• Every class file declares a namespace• One namespace per file ZF2 Patterns 22
Basics• Every class file declares a namespace• One namespace per file• Any class used that is not in the current namespace (or a subnamespace) is imported and typically aliased ZF2 Patterns 22
Basics• Every class file declares a namespace• One namespace per file• 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 namespace ZendEventManager; use ZendStdlibCallbackHandler; class EventManager implements EventCollection { /* ... */ } ZF2 Patterns 23
Using Aliases namespace ZendMvc; use ZendStdlibDispatchable, ZendDiServiceLocator as Locator; class FrontController implements Dispatchable { public function __construct(Locator $locator) { $this->serviceLocator = $locator; } } ZF2 Patterns 24
Recommendation for Migration Use imports instead of require_once calls in your code! ZF2 Patterns 25
Importing ZF1 Classes use Zend_Controller_Action as Controller; class FooController extends Controller {} // Later, this might become: use ZendControllerAction as Controller; class FooController extends Controller {} ZF2 Patterns 26
Naming• All code in the project is in the “Zend” namespace ZF2 Patterns 27
Naming• All code in the project is in the “Zend” namespace• Each component defines a unique namespace ZF2 Patterns 27
Naming• All code in the project is in the “Zend” namespace• Each component defines 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 defines 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
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
The Problem• All exceptions derived from a common class 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 Approach• We eliminated Zend_Exception entirely ZF2 Patterns 34
ZF2 Approach• We eliminated Zend_Exception entirely• Each component defines a marker Exception interface ZF2 Patterns 34
ZF2 Approach• We eliminated Zend_Exception entirely• Each component defines a marker Exception interface• Concrete exceptions live in an Exception subnamespace, and extend SPL exceptions ZF2 Patterns 34
What the solution provides• Catch specific exception types ZF2 Patterns 35
What the solution provides• Catch specific exception types• Catch SPL exception types ZF2 Patterns 35
What the solution provides• Catch specific exception types• Catch SPL exception types• Catch component-level exceptions ZF2 Patterns 35
What the solution provides• Catch specific exception types• Catch SPL exception types• Catch component-level exceptions• Catch based on global exception type ZF2 Patterns 35
The Problem• Performance issues • Many classes are used JIT, and shouldn’t be loaded until needed ZF2 Patterns 39
The Problem• Performance issues • Many classes are used JIT, and shouldn’t be loaded until needed• Missing require_once calls lead to errors ZF2 Patterns 39
ZF2 Approach• No more require_once calls! ZF2 Patterns 40
Class-Maps? Won’t those require work?• Yes, they will. But we already have a tool, bin/classmap_generator.php. ZF2 Patterns 44
Class-Maps? Won’t 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/prefixes with specific 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/prefixes with specific 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/prefix 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/prefix paths for common code • ZF1/PSR0-style fallback autoloader for development ZF2 Patterns 46
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
The Problem• Varying approaches to dynamically discovering plugin classes ZF2 Patterns 51
The Problem• Varying approaches to dynamically discovering plugin classes • Paths relative to the calling class • Prefix-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 • Prefix-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 • Prefix-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 Approach: Plugin Broker• Separate Plugin Location interface • Allows varying implementation of plugin lookup ZF2 Patterns 52
ZF2 Approach: Plugin Broker• Separate Plugin Location interface • Allows varying implementation of plugin lookup• Separate Plugin Broker interface • Composes a Plugin Locator ZF2 Patterns 52
Plugin Locator Interface namespace ZendLoader; interface ShortNameLocator { public function isLoaded($name); public function getClassName($name); public function load($name); } ZF2 Patterns 53
Plugin broker interface namespace ZendLoader; interface Broker { public function load($plugin, array $options = null); public function getPlugins(); public function isLoaded($name); public function register($name, $plugin); public function unregister($name); public function setClassLoader(ShortNameLocator $loader); public function getClassLoader(); } ZF2 Patterns 54
How do I use it?• Create a default plugin loader 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, define static configuration 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, define static configuration• Optionally, pass broker and loader configuration 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, define static configuration• Optionally, pass broker and loader configuration• Optionally, register plugins with locator and/or broker ZF2 Patterns 55
Plugin broker implementation class HelperBroker extends PluginBroker protected $defaultClassLoader = ’ZendViewHelperLoader’; 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 ZendViewHelperLoader; 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 specific to most specific)• Map defined in concrete plugin loader ZF2 Patterns 59
Locator Precedence(From least specific to most specific)• Map defined in concrete plugin loader• Static maps (latest registration having precedence) ZF2 Patterns 59
Locator Precedence(From least specific to most specific)• Map defined in concrete plugin loader• Static maps (latest registration having precedence)• Mapping passed via instantiation ZF2 Patterns 59
Locator Precedence(From least specific to most specific)• Map defined in concrete plugin loader• Static maps (latest registration having precedence)• Mapping passed via instantiation• Explicit mapping provided programmatically ZF2 Patterns 59
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
Registering a plugin with the broker use MyHelperUrl; // 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
What about lazy-loading?• Often you need to configure plugins ZF2 Patterns 68
What about lazy-loading?• Often you need to configure plugins• But you don’t want an instance hanging around until it’s actually requested ZF2 Patterns 68
What about lazy-loading?• Often you need to configure plugins• But you don’t want an instance hanging around until it’s actually requested• Enter the ZendLoaderLazyLoadingBroker ZF2 Patterns 68
LazyLoadingBroker Interface namespace ZendLoader; 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
Using the LazyLoadingBroker• Register “specs” with the broker 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 Via Configuration use ZendViewHelperBroker; $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
New Components• ZendEventManager• ZendDi ZF2 Patterns 75
The EventManager
The Problem• How do we introduce logging/debug points in 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? 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, filtering, 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, filtering, ACL checks, etc., without needing to extend framework code?• How do we allow users to manipulate the order in which plugins, intercepting filters, 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, filtering, ACL checks, etc., without needing to extend framework code?• How do we allow users to manipulate the order in which plugins, intercepting filters, events, etc., trigger?• How can we provide tools for userland code to benefit from the above? ZF2 Patterns 77
Solution: Aspect Oriented Programming• Code defines various “aspects” that may be interesting to observe and/or attach to from a consumer ZF2 Patterns 78
Solution: Aspect Oriented Programming• Code defines various “aspects” that may be interesting to observe and/or attach to from a consumer• Basically, all of the solutions we’ll look at can be used to implement AOP in a code base. ZF2 Patterns 78
Requirements• Reasonably easy to understand design 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• Allow interruption of execution 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 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
Solution: Subject-Observer• Pros • Simple to understand ZF2 Patterns 80
Solution: Subject-Observer• Pros • Simple to understand • SPL interfaces are well-known (but limited) ZF2 Patterns 80
Solution: Subject-Observer• Pros • Simple to understand • SPL interfaces are well-known (but limited)• Cons • Typically, cannot interrupt execution of remaining observers ZF2 Patterns 80
Solution: Subject-Observer• Pros • Simple to understand • SPL interfaces are well-known (but limited)• Cons • Typically, cannot interrupt execution of remaining observers • Requires a system for each component and/or class ZF2 Patterns 80
Solution: Subject-Observer• Pros • Simple to understand • SPL interfaces are well-known (but limited)• 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
Solution: PubSub/Events• Pros • Subscribe to arbitrary notices • Typically per-component + global usage; in many languages, a single, global aggregator ZF2 Patterns 81
Solution: PubSub/Events• Pros • Subscribe to arbitrary notices • Typically per-component + global usage; in many languages, a single, global aggregator • Well-known paradigm in UI programming (think: JavaScript) ZF2 Patterns 81
Solution: PubSub/Events• Pros • Subscribe to arbitrary notices • Typically per-component + global usage; in many languages, a single, global aggregator • Well-known paradigm in UI programming (think: JavaScript) • Tends to be Turing complete ZF2 Patterns 81
Solution: PubSub/Events• Pros • Subscribe to arbitrary notices • Typically per-component + global usage; in many languages, a single, global aggregator • Well-known paradigm in UI programming (think: JavaScript) • Tends to be Turing complete• Cons • Often, need to test the Event provided to ensure you can handle it ZF2 Patterns 81
Solution: PubSub/Events• Pros • Subscribe to arbitrary notices • Typically per-component + global usage; in many languages, a single, global aggregator • Well-known paradigm in UI programming (think: JavaScript) • Tends to be Turing complete• 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
Solution: PubSub/Events• Pros • Subscribe to arbitrary notices • Typically per-component + global usage; in many languages, a single, global aggregator • Well-known paradigm in UI programming (think: JavaScript) • Tends to be Turing complete• 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
Solution: PubSub/Events• Pros • Subscribe to arbitrary notices • Typically per-component + global usage; in many languages, a single, global aggregator • Well-known paradigm in UI programming (think: JavaScript) • Tends to be Turing complete• 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
Solution: PubSub/Events• Pros • Subscribe to arbitrary notices • Typically per-component + global usage; in many languages, a single, global aggregator • Well-known paradigm in UI programming (think: JavaScript) • Tends to be Turing complete• 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• more on this later. . . ZF2 Patterns 81
Solution: SignalSlots• Pros • Well-known in computer science circles • Code emits signals, which are intercepted by slots (aka handlers) ZF2 Patterns 82
Solution: SignalSlots• Pros • Well-known in computer science circles • Code emits signals, which are intercepted by slots (aka handlers) • Typically, compose a signal manager into a class, but can integrate with a global manager as well ZF2 Patterns 82
Solution: SignalSlots• Pros • Well-known in computer science circles • Code emits signals, which are intercepted by slots (aka handlers) • Typically, compose a signal manager into a class, but can integrate with a global manager as well • Usually has some abilities for prioritizing handlers ZF2 Patterns 82
Solution: SignalSlots• Pros • Well-known in computer science circles • Code emits signals, which are intercepted by slots (aka handlers) • Typically, compose a signal manager into a class, but can integrate with a global manager as well • Usually has some abilities for prioritizing handlers• Cons • Verbiage is not well-known amongst PHP developers ZF2 Patterns 82
Solution: SignalSlots• Pros • Well-known in computer science circles • Code emits signals, which are intercepted by slots (aka handlers) • Typically, compose a signal manager into a class, but can integrate with a global manager as well • Usually has some abilities for prioritizing handlers• Cons • Verbiage is not well-known amongst PHP developers • Arguments will vary between signals ZF2 Patterns 82
Solution: SignalSlots• Pros • Well-known in computer science circles • Code emits signals, which are intercepted by slots (aka handlers) • Typically, compose a signal manager into a class, but can integrate with a global manager as well • Usually has some abilities for prioritizing handlers• 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
Solution: Intercepting Filters• Pros • Similar to previous solutions, except that each handler receives the filter chain as an argument, and is responsible for calling the next in the chain ZF2 Patterns 83
Solution: Intercepting Filters• Pros • Similar to previous solutions, except that each handler receives the filter chain as an argument, and is responsible for calling the next in the chain • Often, the entire “work” of a method is simply a filter ZF2 Patterns 83
Solution: Intercepting Filters• Pros • Similar to previous solutions, except that each handler receives the filter chain as an argument, and is responsible for calling the next in the chain • Often, the entire “work” of a method is simply a filter • Depending on the design, can allow static/global access ZF2 Patterns 83
Solution: Intercepting Filters• Pros • Similar to previous solutions, except that each handler receives the filter chain as an argument, and is responsible for calling the next in the chain • Often, the entire “work” of a method is simply a filter • Depending on the design, can allow static/global access• Cons • Sometimes difficult to accomplish complex workflows ZF2 Patterns 83
Solution: Intercepting Filters• Pros • Similar to previous solutions, except that each handler receives the filter chain as an argument, and is responsible for calling the next in the chain • Often, the entire “work” of a method is simply a filter • Depending on the design, can allow static/global access• Cons • Sometimes difficult to accomplish complex workflows • Same issues with composition per-class and static usage as seen in event systems ZF2 Patterns 83
Solution: Intercepting Filters• Pros • Similar to previous solutions, except that each handler receives the filter chain as an argument, and is responsible for calling the next in the chain • Often, the entire “work” of a method is simply a filter • Depending on the design, can allow static/global access• Cons • Sometimes difficult to accomplish complex workflows • Same issues with composition per-class and static usage as seen in event systems • Easy to forget to invoke next filter in chain ZF2 Patterns 83
Solution: Intercepting Filters• Pros • Similar to previous solutions, except that each handler receives the filter chain as an argument, and is responsible for calling the next in the chain • Often, the entire “work” of a method is simply a filter • Depending on the design, can allow static/global access• Cons • Sometimes difficult to accomplish complex workflows • Same issues with composition per-class and static usage as seen in event systems • Easy to forget to invoke next filter in chain • Typically, no ability to prioritize filters 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 ZendEventManager; use ZendStdlibCallbackHandler; 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 ZendEventManagerEventManager; $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 with priority $handler = $events->attach(’some-event’, function($e) use ($log) { /* same as before */ }, 100); // Prioritize! (higher numbers win) ZF2 Patterns 88
Interrupting execution: via handlers $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 ZendEventManagerEventCollection as Events, ZendEventManagerEventManager; 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 ZendEventManagerEventCollection as Events, ZendEventManagerEventManager; 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
Connecting handlers statically use ZendEventManagerStaticEventManager; $events = StaticEventManager::getInstance(); $events->connect(’Foo’, ’some-event’, function ($e) { /* ... */ }); ZF2 Patterns 93
Recommendations• Name your events using __FUNCTION__ • If triggering multiple events in the same method, suffix with a “.(pre|post|etc.)” ZF2 Patterns 94
Recommendations• Name your events using __FUNCTION__ • If triggering multiple events in the same method, suffix with a “.(pre|post|etc.)”• Provide the EventManager constructor with both the class name and one or more “service” names, to make static attachment more semantic ZF2 Patterns 94
Recommendations• Name your events using __FUNCTION__ • If triggering multiple events in the same method, suffix with a “.(pre|post|etc.)”• 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
What is Dependency Injection?Quite simply: defining ways to passdependencies into an object. ZF2 Patterns 96
What is Dependency Injection?Quite simply: defining ways to passdependencies into an object. namespace MyHelper; class Url { public function __construct(Request $request) { $this->request = $request; } public function setRouter(Router $router) { $this->router = $router; } } ZF2 Patterns 96
So, why do people fear it?• They don’t. ZF2 Patterns 97
So, why do people fear it?• They don’t.• They fear Dependency Injection Containers. ZF2 Patterns 97
What’s a Dependency Injection Container? Put simply: an object graph for mapping dependency relations between objects. ZF2 Patterns 98
Again, why do people fear it? It looks like magic. ZF2 Patterns 99
Object with Dependencies namespace MyHelper; class Url { public function __construct(Request $request) { $this->request = $request; } public function setRouter(Router $router) { $this->router = $router; } } ZF2 Patterns 100
Another Object with Dependencies namespace mwopMvc; class Router { public function addRoute(Route $route) { $this->routes->push($route); } } ZF2 Patterns 101
Grabbing an object and using it $urlHelper = $di->get(’url-helper’); echo $url->generate(’/css/site.css’); echo $url->generate(array(’id’ => $id), array(’name’ => ’blog’)); ZF2 Patterns 102
The questions• How can I be sure I have my dependencies? ZF2 Patterns 103
The questions• How can I be sure I have my dependencies? • You define them explicitly. ZF2 Patterns 103
The questions• How can I be sure I have my dependencies? • You define them explicitly. • You retrieve the object via the container, which ensures the definitions are used. ZF2 Patterns 103
The questions• How can I be sure I have my dependencies? • You define them explicitly. • You retrieve the object via the container, which ensures the definitions are used.• Where do I define these? ZF2 Patterns 103
The questions• How can I be sure I have my dependencies? • You define them explicitly. • You retrieve the object via the container, which ensures the definitions are used.• Where do I define these? • Either programmatically, via configuration, or using a tool. ZF2 Patterns 103
The questions• How can I be sure I have my dependencies? • You define them explicitly. • You retrieve the object via the container, which ensures the definitions are used.• Where do I define these? • Either programmatically, via configuration, or using a tool.• If I call $object = new Foo(), how do I force using different dependencies? ZF2 Patterns 103
The questions• How can I be sure I have my dependencies? • You define them explicitly. • You retrieve the object via the container, which ensures the definitions are used.• Where do I define these? • Either programmatically, via configuration, or using a tool.• If I call $object = new Foo(), how do I force using different dependencies? • Calling new doesn’t use the container. In fact, nothing forces you to use one! ZF2 Patterns 103
Why use one?If instantiation of your object is not under yourdirect control (e.g. controllers), how do youretain control over your dependencies? ZF2 Patterns 104
Why use one?If instantiation of your object is not under yourdirect control (e.g. controllers), how do youretain control over your dependencies?• Different data access based on application environment ZF2 Patterns 104
Why use one?If instantiation of your object is not under yourdirect control (e.g. controllers), how do youretain control over your dependencies?• Different data access based on application environment• Substituting mock/stub implementations during testing ZF2 Patterns 104
ZF2 Approach• Standardize on a Service Locator interface 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 definitions during development ZF2 Patterns 105
Service Locator interface namespace ZendDi; interface ServiceLocation { public function set($name, $service); public function get($name, array $params = null); } ZF2 Patterns 106
Dependency Injector interface namespace ZendDi; 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
Definitions namespace ZendDi; 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 ZendDi; interface DependencyReference { public function __construct($serviceName); public function getServiceName(); } ZF2 Patterns 109
Class Definition use ZendDiDefinition, ZendDiReference; $mongo = new Definition(’Mongo’); $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 ZendDiDefinition, ZendDiReference; $service = new Definition(’mwopServiceResources’); $service->addMethod(’setResource’, array( new Reference(’resource’) )); $di->setDefinition(’resources’, $service); $resources = $di->get(’resources’); ZF2 Patterns 111
Making it faster• Specify constructor parameter maps in definitions ZF2 Patterns 112
Making it faster• Specify constructor parameter maps in definitions• 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
Generating a Service Locator from DI use ZendDiContainerBuilder as DiBuilder; $builder = new DiBuilder($injector); $builder->setContainerClass(’AppContext’); $container = $builder->getCodeGenerator( __DIR__ . ’/../application/AppContext.php’ ); // Returns instance of ZendCodeGeneratorPhpPhpFile $container->write(); // Write to disk ZF2 Patterns 114
Example of a generated locator use ZendDiDependencyInjectionContainer; class AppContext extends DependencyInjectionContainer { public function get($name, array $params = array()) { switch ($name) { case ’request’: case ’ZendHttpRequest’: return $this->getZendHttpRequest(); default: return parent::get($name, $params); } } public function getZendHttpRequest() { if (isset($this->services[’ZendHttpRequest’])) { return $this->services[’ZendHttpRequest’]; } $object = new ZendHttpRequest(); $this->services[’ZendHttpRequest’] = $object; return $object; } } ZF2 Patterns 115
Using a generated locator $context = new AppContext(); $request = $context->get(’request’); // Same as using a Service Locator or DI Container! ZF2 Patterns 116
Making it simpler• Use configuration files ZF2 Patterns 117
Making it simpler• Use configuration files• Can use any format supported by ZendConfig ZF2 Patterns 117
What are the use cases in ZF2? One big one. ZF2 Patterns 119
What are the use cases in ZF2? One big one. Pulling MVC controllers from the container ZF2 Patterns 119
An Action Controller namespace BlogController; 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
The Front Controller 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
Benefits to using DI this way• Performance ZF2 Patterns 122
Benefits to using DI this way• Performance• Code de-coupling ZF2 Patterns 122
Benefits to using DI this way• Performance• Code de-coupling• Simplification of controller code ZF2 Patterns 122
More to come!• First-run compilation ZF2 Patterns 123
More to come!• First-run compilation• Tools for scanning classes or namespaces to build definitions ZF2 Patterns 123
More to come!• First-run compilation• Tools for scanning classes or namespaces to build definitions• Interface injection ZF2 Patterns 123
More to come!• First-run compilation• Tools for scanning classes or namespaces to build definitions• Interface injection• . . . and likely more. ZF2 Patterns 123
MVC Patterns
The Problems• How do controllers get dependencies? ZF2 Patterns 125
The Problems• How do controllers get dependencies?• How do we accommodate different controller patterns? • What if we want to fine-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 fine-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 fine-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 don’t like the “Action” suffix 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 fine-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 don’t like the “Action” suffix in action methods? • What if . . . ?• How can we better use server components within the MVC? ZF2 Patterns 125
The Problems• How do controllers get dependencies?• How do we accommodate different controller patterns? • What if we want to fine-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 don’t like the “Action” suffix 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
The basic structure of a web application is that of aRequest/Response lifecycle ZF2 Patterns 126
The Dispatchable Interface namespace ZendStdlib; 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-specific 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
Anything dispatchable can attach to the MVCDispatchable is simply a formalization ofthe Command pattern ZF2 Patterns 129
Anything dispatchable can attach to the MVCDispatchable is simply a formalization ofthe Command pattern• Controllers ZF2 Patterns 129
Anything dispatchable can attach to the MVCDispatchable is simply a formalization ofthe Command pattern• Controllers• Servers ZF2 Patterns 129
Anything dispatchable can attach to the MVCDispatchable is simply a formalization ofthe Command pattern• Controllers• Servers• Whatever you may dream of! Just implement Dispatchable! ZF2 Patterns 129
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• Official repo: git://git.zendframework.com/zf.git http://git.zendframework.com/• You still need to sign a CLA! ZF2 Patterns 134