Zend Framework 2.0 Patterns Tutorial

Post on 08-Sep-2014

69.448 views 1 download

Tags:

description

Tutorial for PHP Community Conference, 2011 edition, covering new components and patterns found within Zend Framework 2.0 development.

Transcript of Zend Framework 2.0 Patterns Tutorial

In the beginning. . .

ZF2 Patterns 3

A Brief History of ZF

Initial Announcements and Work

ZF2 Patterns 4

• October 2005: Announced the project

Initial Announcements and Work

ZF2 Patterns 4

• October 2005: Announced the project• March 2006: First public preview release, 0.1.0

Initial Announcements and Work

ZF2 Patterns 4

• October 2005: Announced the project• March 2006: First public preview release, 0.1.0• Fall 2006: Rewrite of the MVC

1.0.0 - July 2007

ZF2 Patterns 5

• First stable release• Basic MVC system, with plugins, action helpers,

automated view rendering etc.

1.0.0 - July 2007

ZF2 Patterns 5

• First stable release• Basic MVC system, with plugins, action helpers,

automated view rendering etc.• Many web service API consumers

1.0.0 - July 2007

ZF2 Patterns 5

• 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

1.5.0 - March 2008

ZF2 Patterns 6

• First minor release

1.5.0 - March 2008

ZF2 Patterns 6

• First minor release• Zend_Form introduced

1.5.0 - March 2008

ZF2 Patterns 6

• First minor release• Zend_Form introduced• Zend_Layout introduced

1.5.0 - March 2008

ZF2 Patterns 6

• First minor release• Zend_Form introduced• Zend_Layout introduced• Layout-aware view helper system introduced

1.6.0 - September 2008

ZF2 Patterns 7

• Dojo integration

1.6.0 - September 2008

ZF2 Patterns 7

• Dojo integration• PHPUnit scaffolding for testing controllers

1.6.0 - September 2008

ZF2 Patterns 7

• Dojo integration• PHPUnit scaffolding for testing controllers• Introduction of the ContextSwitch action helper

1.7.0 - November 2008

ZF2 Patterns 8

• AMF support

1.7.0 - November 2008

ZF2 Patterns 8

• AMF support• Performance improvements

1.8.0 - April 2009

ZF2 Patterns 9

• Introduction of Zend_Tool

1.8.0 - April 2009

ZF2 Patterns 9

• Introduction of Zend_Tool• Introduction of Zend_Application

1.8.0 - April 2009

ZF2 Patterns 9

• Introduction of Zend_Tool• Introduction of Zend_Application• First widely usable release of ZF

1.9.0 - August 2009

ZF2 Patterns 10

• Addition of Zend_Feed_Reader

1.9.0 - August 2009

ZF2 Patterns 10

• Addition of Zend_Feed_Reader• PHP 5.3 support/compatibility

1.9.0 - August 2009

ZF2 Patterns 10

• Addition of Zend_Feed_Reader• PHP 5.3 support/compatibility• Primarily community-led additions

1.9.0 - August 2009

ZF2 Patterns 10

• Addition of Zend_Feed_Reader• PHP 5.3 support/compatibility• Primarily community-led additions• Beginning of monthly bug hunts in October

1.10.0 - January 2010

ZF2 Patterns 11

• Integration of ControllerTestCase withZend_Application

1.10.0 - January 2010

ZF2 Patterns 11

• Integration of ControllerTestCase withZend_Application

• Addition of Zend_Feed_Writer, markingcompletion of Zend_Feed refactoring

1.10.0 - January 2010

ZF2 Patterns 11

• Integration of ControllerTestCase withZend_Application

• Addition of Zend_Feed_Writer, markingcompletion of Zend_Feed refactoring

• Documentation changes: adoption of PhD to renderend-user manual, introduction of comment system,and new “Learning Zend Framework section”

1.10.0 - January 2010

ZF2 Patterns 11

• Integration of ControllerTestCase withZend_Application

• Addition of Zend_Feed_Writer, markingcompletion of Zend_Feed refactoring

• Documentation changes: adoption of PhD to renderend-user manual, introduction of comment system,and new “Learning Zend Framework section”

• Primarily community-led additions

1.11.0 November 2010

ZF2 Patterns 12

• Mobile support via Zend_Http_UserAgent

1.11.0 November 2010

ZF2 Patterns 12

• Mobile support via Zend_Http_UserAgent• SimpleCloud API via Zend_Cloud

Where do we go from here?

Zend Framework 2.0’s focusis on improving consistency

and performance

Incremental Improvements

Baby steps

ZF2 Patterns 18

• Convert code from vendor prefixes (e.g.“Zend_Foo”) to PHP 5.3 namespaces

Baby steps

ZF2 Patterns 18

• Convert code from vendor prefixes (e.g.“Zend_Foo”) to PHP 5.3 namespaces

• Refactor exceptions

Baby steps

ZF2 Patterns 18

• Convert code from vendor prefixes (e.g.“Zend_Foo”) to PHP 5.3 namespaces

• Refactor exceptions• Switch ZF to be autoload-only

Baby steps

ZF2 Patterns 18

• 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

Namespaces

The Problem

ZF2 Patterns 21

• Lengthy class names

• Difficult to refactor

The Problem

ZF2 Patterns 21

• Lengthy class names

• Difficult to refactor• Difficult to retain semantics with shorter names

Basics

ZF2 Patterns 22

• Every class file declares a namespace

Basics

ZF2 Patterns 22

• Every class file declares a namespace• One namespace per file

Basics

ZF2 Patterns 22

• 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 typicallyaliased

Basics

ZF2 Patterns 22

• 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 typicallyaliased

• Global resolution is discouraged, except in the caseof classes referenced in strings

Namespace Example

ZF2 Patterns 23

namespace Zend\EventManager;

use Zend\Stdlib\CallbackHandler;

class EventManager implements EventCollection{

/* ... */}

Using Aliases

ZF2 Patterns 24

namespace Zend\Mvc;use Zend\Stdlib\Dispatchable,

Zend\Di\ServiceLocator as Locator;

class FrontController implements Dispatchable{

public function __construct(Locator $locator){

$this->serviceLocator = $locator;}

}

Recommendation for Migration

ZF2 Patterns 25

Use imports instead ofrequire_once calls in your

code!

Importing ZF1 Classes

ZF2 Patterns 26

use Zend_Controller_Action as Controller;class FooController extends Controller {}

// Later, this might become:use Zend\Controller\Action as Controller;class FooController extends Controller {}

Naming

ZF2 Patterns 27

• All code in the project is in the “Zend” namespace

Naming

ZF2 Patterns 27

• All code in the project is in the “Zend” namespace• Each component defines a unique namespace

Naming

ZF2 Patterns 27

• 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

Naming

ZF2 Patterns 27

• 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

Naming Examples

ZF2 Patterns 28

Zend/EventManager|-- EventCollection.php

‘-- EventManager.php

Naming Examples

ZF2 Patterns 28

Zend/EventManager|-- EventCollection.php

‘-- EventManager.php

namespace Zend\EventManager;

class EventManager implementsEventCollection{}

Interfaces

ZF2 Patterns 29

• Interfaces are named after nouns or adjectives, anddescribe what they provide

Interfaces

ZF2 Patterns 29

• Interfaces are named after nouns or adjectives, anddescribe what they provide

• In most cases, concrete implementations ofinterfaces live in a subnamespace named after theinterface

Interfaces

ZF2 Patterns 29

• Interfaces are named after nouns or adjectives, anddescribe what they provide

• In most cases, concrete implementations ofinterfaces live in a subnamespace named after theinterface

• More solid Contract-Oriented paradigm

Interface Examples

ZF2 Patterns 30

Zend/Session|-- Storage.php‘-- Storage

|-- ArrayStorage.php

‘-- SessionStorage.php

Interface Examples

ZF2 Patterns 30

Zend/Session|-- Storage.php‘-- Storage

|-- ArrayStorage.php

‘-- SessionStorage.php

namespace Zend\Session;

use Traversable, ArrayAccess,Serializable, Countable;

interface Storageextends Traversable,

ArrayAccess, Serializable,Countable{

/* ... */}

Concrete Implementation

ZF2 Patterns 31

namespace Zend\Session\Storage;

use ArrayObject,Zend\Session\Storage,Zend\Session\Exception;

class ArrayStorage extends ArrayObject implements Storage{

/* ... */}

Exceptions

The Problem

ZF2 Patterns 33

• All exceptions derived from a common class

The Problem

ZF2 Patterns 33

• All exceptions derived from a common class• Inability to extend semantic exception types offered

in the SPL

The Problem

ZF2 Patterns 33

• All exceptions derived from a common class• Inability to extend semantic exception types offered

in the SPL• Limited catching strategies

The Problem

ZF2 Patterns 33

• 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 Approach

ZF2 Patterns 34

• We eliminated Zend_Exception entirely

ZF2 Approach

ZF2 Patterns 34

• We eliminated Zend_Exception entirely• Each component defines a marker Exception

interface

ZF2 Approach

ZF2 Patterns 34

• We eliminated Zend_Exception entirely• Each component defines a marker Exception

interface• Concrete exceptions live in an Exception

subnamespace, and extend SPL exceptions

What the solution provides

ZF2 Patterns 35

• Catch specific exception types

What the solution provides

ZF2 Patterns 35

• Catch specific exception types• Catch SPL exception types

What the solution provides

ZF2 Patterns 35

• Catch specific exception types• Catch SPL exception types• Catch component-level exceptions

What the solution provides

ZF2 Patterns 35

• Catch specific exception types• Catch SPL exception types• Catch component-level exceptions• Catch based on global exception type

Exception Definitions

ZF2 Patterns 36

Zend/EventManager|-- Exception.php‘-- Exception

‘--InvalidArgumentException.php

Exception Definitions

ZF2 Patterns 36

Zend/EventManager|-- Exception.php‘-- Exception

‘--InvalidArgumentException.php

namespace Zend\EventManager;

interface Exception {}

Exception Definitions

ZF2 Patterns 36

Zend/EventManager|-- Exception.php‘-- Exception

‘--InvalidArgumentException.php

namespace Zend\EventManager;

interface Exception {}

namespace Zend\EventManager\Exception;

use Zend\EventManager\Exception;

class InvalidArgumentExceptionextends \InvalidArgumentExceptionimplements Exception

{}

Catching Exceptions

ZF2 Patterns 37

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) {}

Autoloading

The Problem

ZF2 Patterns 39

• Performance issues

• Many classes are used JIT, and shouldn’t beloaded until needed

The Problem

ZF2 Patterns 39

• Performance issues

• Many classes are used JIT, and shouldn’t beloaded until needed

• Missing require_once calls lead to errors

ZF2 Approach

ZF2 Patterns 40

• No more require_once calls!

ZF2 Approach

ZF2 Patterns 40

• No more require_once calls!• Deliver multiple autoloading approaches

• ZF1-style include_path autoloader• Per- namespace/vendor prefix autoloading• Class-Map autoloading

ZF1-Style Autoloading

ZF2 Patterns 41

require_once ’Zend/Loader/StandardAutoloader.php’;$loader = new Zend\Loader\StandardAutoloader(array(

’fallback_autoloader’ => true,));$loader->register();

ZF2 NS/Prefix Autoloading

ZF2 Patterns 42

require_once ’Zend/Loader/StandardAutoloader.php’;$loader = new Zend\Loader\StandardAutoloader();$loader->registerNamespace(’My’, __DIR__ . ’/../library/My’)

->registerPrefix(’Phly_’, __DIR__ . ’/../library/Phly’);$loader->register();

Class-Map Autoloading

ZF2 Patterns 43

return array(’My\Foo\Bar’ => __DIR__ . ’/Foo/Bar.php’,

);

Class-Map Autoloading

ZF2 Patterns 43

return array(’My\Foo\Bar’ => __DIR__ . ’/Foo/Bar.php’,

);

require_once ’Zend/Loader/ClassMapAutoloader.php’;$loader = new Zend\Loader\ClassMapAutoloader();$loader->registerAutoloadMap(__DIR__ . ’/../library/.classmap.php’);$loader->register();

Class-Maps? Won’t those require work?

ZF2 Patterns 44

• Yes, they will. But we already have a tool,bin/classmap_generator.php.

Class-Maps? Won’t those require work?

ZF2 Patterns 44

• Yes, they will. But we already have a tool,bin/classmap_generator.php.

• Usage is trivial:

prompt> cd your/libraryprompt> php /path/to/classmap_generator.php -w# Class-Map now exists in .classmap.php

Why?

ZF2 Patterns 45

• Class-Maps show a 25% improvement on the ZF1autoloader when no acceleration is present

Why?

ZF2 Patterns 45

• Class-Maps show a 25% improvement on the ZF1autoloader when no acceleration is present• and 60-85% improvements when an opcode cache is in place!

Why?

ZF2 Patterns 45

• Class-Maps show a 25% improvement on the ZF1autoloader when no acceleration is present• and 60-85% improvements when an opcode cache is in place!

• Pairing namespaces/prefixes with specific pathsshows >10% gains with no acceleration

Why?

ZF2 Patterns 45

• Class-Maps show a 25% improvement on the ZF1autoloader when no acceleration is present• and 60-85% improvements when an opcode cache is in place!

• Pairing namespaces/prefixes with specific pathsshows >10% gains with no acceleration• and 40% improvements when an opcode cache is in place!

Autoloader Factory

ZF2 Patterns 46

• With multiple strategies comes the need for a factory

Autoloader Factory

ZF2 Patterns 46

• With multiple strategies comes the need for a factory• Choose several strategies

Autoloader Factory

ZF2 Patterns 46

• With multiple strategies comes the need for a factory• Choose several strategies

• Class-Map for fastest lookup

Autoloader Factory

ZF2 Patterns 46

• With multiple strategies comes the need for a factory• Choose several strategies

• Class-Map for fastest lookup• Namespace/prefix paths for common code

Autoloader Factory

ZF2 Patterns 46

• 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

Autoloader Factory Example

ZF2 Patterns 47

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,

),));

Start Migrating

ZF2 Patterns 48

You can use the ZF2

autoloaders and class-map

generation facilities today;start migrating now!

Plugin Loading

Terminology

ZF2 Patterns 50

• For our purposes, a “plugin” is any class that isdetermined at runtime.

Terminology

ZF2 Patterns 50

• For our purposes, a “plugin” is any class that isdetermined at runtime.

• Action and view helpers• Adapters• Filters and validators

The Problem

ZF2 Patterns 51

• Varying approaches to dynamically discoveringplugin classes

The Problem

ZF2 Patterns 51

• Varying approaches to dynamically discoveringplugin classes

• Paths relative to the calling class• Prefix-path stacks (most common)• Setters to indicate classes

The Problem

ZF2 Patterns 51

• Varying approaches to dynamically discoveringplugin classes

• Paths relative to the calling class• Prefix-path stacks (most common)• Setters to indicate classes

• Most common approach is terrible

The Problem

ZF2 Patterns 51

• Varying approaches to dynamically discoveringplugin 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 Approach: Plugin Broker

ZF2 Patterns 52

• Separate Plugin Location interface• Allows varying implementation of plugin lookup

ZF2 Approach: Plugin Broker

ZF2 Patterns 52

• Separate Plugin Location interface• Allows varying implementation of plugin lookup

• Separate Plugin Broker interface• Composes a Plugin Locator

Plugin Locator Interface

ZF2 Patterns 53

namespace Zend\Loader;interface ShortNameLocator{

public function isLoaded($name);public function getClassName($name);public function load($name);

}

Plugin broker interface

ZF2 Patterns 54

namespace Zend\Loader;

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

}

How do I use it?

ZF2 Patterns 55

• Create a default plugin loader

How do I use it?

ZF2 Patterns 55

• Create a default plugin loader• Create a default plugin broker

How do I use it?

ZF2 Patterns 55

• Create a default plugin loader• Create a default plugin broker• Compose a broker into your class

How do I use it?

ZF2 Patterns 55

• Create a default plugin loader• Create a default plugin broker• Compose a broker into your class• Optionally, define static configuration

How do I use it?

ZF2 Patterns 55

• 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

How do I use it?

ZF2 Patterns 55

• 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

Plugin Locator Implementation

ZF2 Patterns 56

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’,/* ... */

);}

Plugin broker implementation

ZF2 Patterns 57

class HelperBroker extends PluginBrokerprotected $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;

}}

Composing a broker

ZF2 Patterns 58

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

}}

Locator Precedence

ZF2 Patterns 59

(From least specific to most specific)

• Map defined in concrete plugin loader

Locator Precedence

ZF2 Patterns 59

(From least specific to most specific)

• Map defined in concrete plugin loader• Static maps (latest registration having precedence)

Locator Precedence

ZF2 Patterns 59

(From least specific to most specific)

• Map defined in concrete plugin loader• Static maps (latest registration having precedence)• Mapping passed via instantiation

Locator Precedence

ZF2 Patterns 59

(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

Defining static maps

ZF2 Patterns 60

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"

Passing maps via configuration

ZF2 Patterns 61

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"

Passing maps to maps!

ZF2 Patterns 62

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"

Extending loaders

ZF2 Patterns 63

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"

Passing maps via broker

ZF2 Patterns 64

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"

Creating maps manually

ZF2 Patterns 65

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"

Managing plugins via a broker

ZF2 Patterns 66

• By default, it consults the loader for a classname,and instantiates that class with the given arguments

Managing plugins via a broker

ZF2 Patterns 66

• By default, it consults the loader for a classname,and instantiates that class with the given arguments

• Optionally, you can also seed the broker, manuallyregistering plugin objects under a given name

Registering a plugin with the broker

ZF2 Patterns 67

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

What about lazy-loading?

ZF2 Patterns 68

• Often you need to configure plugins

What about lazy-loading?

ZF2 Patterns 68

• Often you need to configure plugins• But you don’t want an instance hanging around until

it’s actually requested

What about lazy-loading?

ZF2 Patterns 68

• Often you need to configure plugins• But you don’t want an instance hanging around until

it’s actually requested• Enter the Zend\Loader\LazyLoadingBroker

LazyLoadingBroker Interface

ZF2 Patterns 69

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

}

Using the LazyLoadingBroker

ZF2 Patterns 70

• Register “specs” with the broker

Using the LazyLoadingBroker

ZF2 Patterns 70

• Register “specs” with the broker• When that plugin is requested, the provided options

will be used unless new options are provided

Using the LazyLoadingBroker

ZF2 Patterns 70

• 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

LazyLoadingBroker Usage

ZF2 Patterns 71

$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

LazyLoadingBroker Usage Via Configuration

ZF2 Patterns 72

use Zend\View\HelperBroker;

$config = array(’specs’ => array(

’url’ => array($request, $rourter),),

);

$broker = new HelperBroker($config);$url = $broker->load(’url’); // With $request, $router injected

New Components

New Components

ZF2 Patterns 75

• Zend\EventManager• Zend\Di

The EventManager

The Problem

ZF2 Patterns 77

• How do we introduce logging/debug points inframework code?

The Problem

ZF2 Patterns 77

• How do we introduce logging/debug points inframework code?

• How do we allow users to introduce caching withoutneeding to extend framework code?

The Problem

ZF2 Patterns 77

• How do we introduce logging/debug points inframework code?

• How do we allow users to introduce caching withoutneeding to extend framework code?

• How do we allow users to introduce validation,filtering, ACL checks, etc., without needing to extendframework code?

The Problem

ZF2 Patterns 77

• How do we introduce logging/debug points inframework code?

• How do we allow users to introduce caching withoutneeding to extend framework code?

• How do we allow users to introduce validation,filtering, ACL checks, etc., without needing to extendframework code?

• How do we allow users to manipulate the order inwhich plugins, intercepting filters, events, etc.,trigger?

The Problem

ZF2 Patterns 77

• How do we introduce logging/debug points inframework code?

• How do we allow users to introduce caching withoutneeding to extend framework code?

• How do we allow users to introduce validation,filtering, ACL checks, etc., without needing to extendframework code?

• How do we allow users to manipulate the order inwhich plugins, intercepting filters, events, etc.,trigger?

• How can we provide tools for userland code tobenefit from the above?

Solution: Aspect Oriented Programming

ZF2 Patterns 78

• Code defines various “aspects” that may beinteresting to observe and/or attach to from aconsumer

Solution: Aspect Oriented Programming

ZF2 Patterns 78

• Code defines various “aspects” that may beinteresting to observe and/or attach to from aconsumer

• Basically, all of the solutions we’ll look at can beused to implement AOP in a code base.

Requirements

ZF2 Patterns 79

• Reasonably easy to understand design

Requirements

ZF2 Patterns 79

• Reasonably easy to understand design• Allow static or per-instance attachment of handlers,

preferably both

Requirements

ZF2 Patterns 79

• Reasonably easy to understand design• Allow static or per-instance attachment of handlers,

preferably both• Preferably while retaining non-global state or allowing overriding

Requirements

ZF2 Patterns 79

• 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

Requirements

ZF2 Patterns 79

• 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

Requirements

ZF2 Patterns 79

• 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

Requirements

ZF2 Patterns 79

• 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

Solution: Subject-Observer

ZF2 Patterns 80

• Pros• Simple to understand

Solution: Subject-Observer

ZF2 Patterns 80

• Pros• Simple to understand• SPL interfaces are well-known (but limited)

Solution: Subject-Observer

ZF2 Patterns 80

• Pros• Simple to understand• SPL interfaces are well-known (but limited)

• Cons• Typically, cannot interrupt execution of remaining observers

Solution: Subject-Observer

ZF2 Patterns 80

• 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

Solution: Subject-Observer

ZF2 Patterns 80

• 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

Solution: PubSub/Events

ZF2 Patterns 81

• Pros• Subscribe to arbitrary notices

Solution: PubSub/Events

ZF2 Patterns 81

• Pros• Subscribe to arbitrary notices• Typically per-component + global usage; in many languages, a

single, global aggregator

Solution: PubSub/Events

ZF2 Patterns 81

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

Solution: PubSub/Events

ZF2 Patterns 81

• 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

Solution: PubSub/Events

ZF2 Patterns 81

• 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

Solution: PubSub/Events

ZF2 Patterns 81

• 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

Solution: PubSub/Events

ZF2 Patterns 81

• 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

Solution: PubSub/Events

ZF2 Patterns 81

• 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

Solution: PubSub/Events

ZF2 Patterns 81

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

Solution: SignalSlots

ZF2 Patterns 82

• Pros• Well-known in computer science circles

Solution: SignalSlots

ZF2 Patterns 82

• Pros• Well-known in computer science circles• Code emits signals, which are intercepted by slots (aka handlers)

Solution: SignalSlots

ZF2 Patterns 82

• 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 canintegrate with a global manager as well

Solution: SignalSlots

ZF2 Patterns 82

• 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 canintegrate with a global manager as well

• Usually has some abilities for prioritizing handlers

Solution: SignalSlots

ZF2 Patterns 82

• 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 canintegrate with a global manager as well

• Usually has some abilities for prioritizing handlers

• Cons• Verbiage is not well-known amongst PHP developers

Solution: SignalSlots

ZF2 Patterns 82

• 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 canintegrate 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

Solution: SignalSlots

ZF2 Patterns 82

• 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 canintegrate 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

Solution: Intercepting Filters

ZF2 Patterns 83

• Pros• Similar to previous solutions, except that each handler receives

the filter chain as an argument, and is responsible for calling thenext in the chain

Solution: Intercepting Filters

ZF2 Patterns 83

• Pros• Similar to previous solutions, except that each handler receives

the filter chain as an argument, and is responsible for calling thenext in the chain

• Often, the entire “work” of a method is simply a filter

Solution: Intercepting Filters

ZF2 Patterns 83

• Pros• Similar to previous solutions, except that each handler receives

the filter chain as an argument, and is responsible for calling thenext in the chain

• Often, the entire “work” of a method is simply a filter• Depending on the design, can allow static/global access

Solution: Intercepting Filters

ZF2 Patterns 83

• Pros• Similar to previous solutions, except that each handler receives

the filter chain as an argument, and is responsible for calling thenext 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

Solution: Intercepting Filters

ZF2 Patterns 83

• Pros• Similar to previous solutions, except that each handler receives

the filter chain as an argument, and is responsible for calling thenext 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

Solution: Intercepting Filters

ZF2 Patterns 83

• Pros• Similar to previous solutions, except that each handler receives

the filter chain as an argument, and is responsible for calling thenext 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

Solution: Intercepting Filters

ZF2 Patterns 83

• Pros• Similar to previous solutions, except that each handler receives

the filter chain as an argument, and is responsible for calling thenext 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: EventManager Component

ZF2 Patterns 84

• Cherry-picks from each of PubSub, SignalSlot, andIntercepting Filters to provide a comprehensivesolution

ZF2: EventManager Component

ZF2 Patterns 84

• Cherry-picks from each of PubSub, SignalSlot, andIntercepting Filters to provide a comprehensivesolution

• Cannot completely solve the composition/staticusage issues• We can solve the composition problem in PHP 5.4 via Traits

ZF2: EventManager Component

ZF2 Patterns 84

• Cherry-picks from each of PubSub, SignalSlot, andIntercepting Filters to provide a comprehensivesolution

• Cannot completely solve the composition/staticusage issues• We can solve the composition problem in PHP 5.4 via Traits• There are some elegant ways to handle static usage

EventCollection Interface

ZF2 Patterns 85

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

}

Triggering Events

ZF2 Patterns 86

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

*/

CallbackHandler

ZF2 Patterns 87

$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));

});

CallbackHandler with priority

ZF2 Patterns 88

$handler = $events->attach(’some-event’, function($e) use ($log) {/* same as before */

}, 100); // Prioritize! (higher numbers win)

Interrupting execution: testing results

ZF2 Patterns 89

$results = $events->triggerUntil(’some-event’, $o, $argv,function($result) {

return ($result instanceof SomeType);});if ($results->stopped()) {

return $results->last();}

Interrupting execution: via handlers

ZF2 Patterns 90

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

Composing an EventManager

ZF2 Patterns 91

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

}}

Using a Trait!

ZF2 Patterns 92

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;

}

Connecting handlers statically

ZF2 Patterns 93

use Zend\EventManager\StaticEventManager;

$events = StaticEventManager::getInstance();$events->connect(’Foo’, ’some-event’, function ($e) {

/* ... */});

Recommendations

ZF2 Patterns 94

• Name your events using __FUNCTION__• If triggering multiple events in the same method, suffix with a

“.(pre|post|etc.)”

Recommendations

ZF2 Patterns 94

• 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 theclass name and one or more “service” names, tomake static attachment more semantic

Recommendations

ZF2 Patterns 94

• 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 theclass name and one or more “service” names, tomake static attachment more semantic• This allows a single callback to listen to many components!

Dependency Injection

What is Dependency Injection?

ZF2 Patterns 96

Quite simply: defining ways to passdependencies into an object.

What is Dependency Injection?

ZF2 Patterns 96

Quite simply: defining ways to passdependencies into an object.

namespace My\Helper;class Url{

public function __construct(Request $request){

$this->request = $request;}

public function setRouter(Router $router){

$this->router = $router;}

}

So, why do people fear it?

ZF2 Patterns 97

• They don’t.

So, why do people fear it?

ZF2 Patterns 97

• They don’t.• They fear Dependency Injection Containers.

What’s a Dependency Injection Container?

ZF2 Patterns 98

Put simply:

an object graph for mapping

dependency relationsbetween objects.

Again, why do people fear it?

ZF2 Patterns 99

It looks like magic.

Object with Dependencies

ZF2 Patterns 100

namespace My\Helper;class Url{

public function __construct(Request $request){

$this->request = $request;}

public function setRouter(Router $router){

$this->router = $router;}

}

Another Object with Dependencies

ZF2 Patterns 101

namespace mwop\Mvc;class Router{

public function addRoute(Route $route){

$this->routes->push($route);}

}

Grabbing an object and using it

ZF2 Patterns 102

$urlHelper = $di->get(’url-helper’);echo $url->generate(’/css/site.css’);echo $url->generate(array(’id’ => $id), array(’name’ => ’blog’));

The questions

ZF2 Patterns 103

• How can I be sure I have my dependencies?

The questions

ZF2 Patterns 103

• How can I be sure I have my dependencies?• You define them explicitly.

The questions

ZF2 Patterns 103

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

The questions

ZF2 Patterns 103

• 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?

The questions

ZF2 Patterns 103

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

The questions

ZF2 Patterns 103

• 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 forceusing different dependencies?

The questions

ZF2 Patterns 103

• 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 forceusing different dependencies?• Calling new doesn’t use the container. In fact, nothing forces you

to use one!

Why use one?

ZF2 Patterns 104

If instantiation of your object is not under yourdirect control (e.g. controllers), how do youretain control over your dependencies?

Why use one?

ZF2 Patterns 104

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

Why use one?

ZF2 Patterns 104

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 Approach

ZF2 Patterns 105

• Standardize on a Service Locator interface

ZF2 Approach

ZF2 Patterns 105

• Standardize on a Service Locator interface• Provide a performant DI solution, and integrate it into

a Service Locator

ZF2 Approach

ZF2 Patterns 105

• 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

Service Locator interface

ZF2 Patterns 106

namespace Zend\Di;

interface ServiceLocation{

public function set($name, $service);public function get($name, array $params = null);

}

Dependency Injector interface

ZF2 Patterns 107

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

}

Definitions

ZF2 Patterns 108

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

}

References

ZF2 Patterns 109

namespace Zend\Di;

interface DependencyReference{

public function __construct($serviceName);public function getServiceName();

}

Class Definition

ZF2 Patterns 110

use Zend\Di\Definition,Zend\Di\Reference;

$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’);

Setter Injection

ZF2 Patterns 111

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’);

Making it faster

ZF2 Patterns 112

• Specify constructor parameter maps in definitions

Making it faster

ZF2 Patterns 112

• Specify constructor parameter maps in definitions• Generate Service Locators from a DI container

Parameter maps

ZF2 Patterns 113

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

Generating a Service Locator from DI

ZF2 Patterns 114

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

Example of a generated locator

ZF2 Patterns 115

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;

}}

Using a generated locator

ZF2 Patterns 116

$context = new AppContext();$request = $context->get(’request’);// Same as using a Service Locator or DI Container!

Making it simpler

ZF2 Patterns 117

• Use configuration files

Making it simpler

ZF2 Patterns 117

• Use configuration files• Can use any format supported by Zend\Config

Configuration (JSON)

ZF2 Patterns 118

{"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"

}}

}

What are the use cases in ZF2?

ZF2 Patterns 119

One big one.

What are the use cases in ZF2?

ZF2 Patterns 119

One big one.

Pulling MVC controllersfrom the container

An Action Controller

ZF2 Patterns 120

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);/* ... */

}}

The Front Controller

ZF2 Patterns 121

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);/* ... */

}}

Benefits to using DI this way

ZF2 Patterns 122

• Performance

Benefits to using DI this way

ZF2 Patterns 122

• Performance• Code de-coupling

Benefits to using DI this way

ZF2 Patterns 122

• Performance• Code de-coupling• Simplification of controller code

More to come!

ZF2 Patterns 123

• First-run compilation

More to come!

ZF2 Patterns 123

• First-run compilation• Tools for scanning classes or namespaces to build

definitions

More to come!

ZF2 Patterns 123

• First-run compilation• Tools for scanning classes or namespaces to build

definitions• Interface injection

More to come!

ZF2 Patterns 123

• First-run compilation• Tools for scanning classes or namespaces to build

definitions• Interface injection• . . . and likely more.

MVC Patterns

The Problems

ZF2 Patterns 125

• How do controllers get dependencies?

The Problems

ZF2 Patterns 125

• 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?

The Problems

ZF2 Patterns 125

• 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?

The Problems

ZF2 Patterns 125

• 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?

The Problems

ZF2 Patterns 125

• 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 withinthe MVC?

The Problems

ZF2 Patterns 125

• 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 withinthe MVC?

• How can we make the MVC more performant?

ZF2 Patterns 126

The basic structure of a webapplication is that of a

Request/Response lifecycle

The Dispatchable Interface

ZF2 Patterns 127

namespace Zend\Stdlib;

interface Dispatchable{

public function dispatch(Request $request, Response $response = null

);}

Request and Response

ZF2 Patterns 128

• Both the Request and Response simply aggregatemetadata and content

Request and Response

ZF2 Patterns 128

• Both the Request and Response simply aggregatemetadata and content

• The Response also has the ability to send itself

Request and Response

ZF2 Patterns 128

• Both the Request and Response simply aggregatemetadata 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, andcommon tasks such as determining Accept and Content-Typeheaders

Anything dispatchable can attach to the MVC

ZF2 Patterns 129

Dispatchable is simply a formalization ofthe Command pattern

Anything dispatchable can attach to the MVC

ZF2 Patterns 129

Dispatchable is simply a formalization ofthe Command pattern• Controllers

Anything dispatchable can attach to the MVC

ZF2 Patterns 129

Dispatchable is simply a formalization ofthe Command pattern• Controllers• Servers

Anything dispatchable can attach to the MVC

ZF2 Patterns 129

Dispatchable is simply a formalization ofthe Command pattern• Controllers• Servers• Whatever you may dream of! Just implement

Dispatchable!

Simple Front Controller Prototype

ZF2 Patterns 130

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

Getting Involved

Contribute to ZF2!

ZF2 Patterns 133

• ZF2 wiki:http://bit.ly/zf2wiki

• zf-contributors mailing list:zf-contributors-subscribe@lists.zend.com

• IRC:#zftalk.dev on Freenode

ZF2 Git Repository

ZF2 Patterns 134

• Git guide:http://bit.ly/zf2gitguide

• GitHub:http://github.com/zendframework/zf2

• Official repo:git://git.zendframework.com/zf.githttp://git.zendframework.com/

• You still need to sign a CLA!

References

ZF2 Patterns 135

• ZF2 Dependency Injection Proposalhttp://bit.ly/zf2diproposalhttp://bit.ly/zf2diprototype

• ZF2 DI/MVC prototype sandboxhttp://bit.ly/zf2sandbox(mobile-layout branch is latest)

Thank You!

ZF2 Patterns 136

• Feedback:http://joind.in/3339

• Twitter:http://twitter.com/weierophinney

• Zend Framework:http://framework.zend.com/