Você está na página 1de 277

In the beginning. . .

A Brief History of ZF

ZF2 Patterns

Initial Announcements and Work October 2005: Announced the project

ZF2 Patterns

Initial Announcements and Work October 2005: Announced the project March 2006: First public preview release, 0.1.0

ZF2 Patterns

Initial Announcements and Work October 2005: Announced the project March 2006: First public preview release, 0.1.0 Fall 2006: Rewrite of the MVC

ZF2 Patterns

1.0.0 - July 2007 First stable release Basic MVC system, with plugins, action helpers, automated view rendering etc.

ZF2 Patterns

1.0.0 - July 2007 First stable release Basic MVC system, with plugins, action helpers, automated view rendering etc. Many web service API consumers

ZF2 Patterns

1.0.0 - July 2007 First stable release Basic MVC system, with plugins, action helpers, automated view rendering etc. Many web service API consumers Server classes for XML-RPC, REST

ZF2 Patterns

1.5.0 - March 2008 First minor release

ZF2 Patterns

1.5.0 - March 2008 First minor release Zend_Form introduced

ZF2 Patterns

1.5.0 - March 2008 First minor release Zend_Form introduced Zend_Layout introduced

ZF2 Patterns

1.5.0 - March 2008 First minor release Zend_Form introduced Zend_Layout introduced Layout-aware view helper system introduced

ZF2 Patterns

1.6.0 - September 2008 Dojo integration

ZF2 Patterns

1.6.0 - September 2008 Dojo integration PHPUnit scaffolding for testing controllers

ZF2 Patterns

1.6.0 - September 2008 Dojo integration PHPUnit scaffolding for testing controllers Introduction of the ContextSwitch action helper

ZF2 Patterns

1.7.0 - November 2008 AMF support

ZF2 Patterns

1.7.0 - November 2008 AMF support Performance improvements

ZF2 Patterns

1.8.0 - April 2009 Introduction of Zend_Tool

ZF2 Patterns

1.8.0 - April 2009 Introduction of Zend_Tool Introduction of Zend_Application

ZF2 Patterns

1.8.0 - April 2009 Introduction of Zend_Tool Introduction of Zend_Application First widely usable release of ZF

ZF2 Patterns

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.0s focus is on improving consistency and performance

Incremental Improvements

Baby steps Convert code from vendor prexes (e.g. Zend_Foo) to PHP 5.3 namespaces

ZF2 Patterns

18

Baby steps Convert code from vendor prexes (e.g. Zend_Foo) to PHP 5.3 namespaces Refactor exceptions

ZF2 Patterns

18

Baby steps Convert code from vendor prexes (e.g. Zend_Foo) to PHP 5.3 namespaces Refactor exceptions Switch ZF to be autoload-only

ZF2 Patterns

18

Baby steps Convert code from vendor prexes (e.g. Zend_Foo) to PHP 5.3 namespaces Refactor exceptions Switch ZF to be autoload-only Improve and standardize the plugin system

ZF2 Patterns

18

Namespaces

The Problem Lengthy class names Difcult to refactor

ZF2 Patterns

21

The Problem Lengthy class names Difcult to refactor Difcult to retain semantics with shorter names

ZF2 Patterns

21

Basics Every class le declares a namespace

ZF2 Patterns

22

Basics Every class le declares a namespace One namespace per le

ZF2 Patterns

22

Basics Every class le declares a namespace One namespace per le Any class used that is not in the current namespace (or a subnamespace) is imported and typically aliased

ZF2 Patterns

22

Basics Every class le declares a namespace One namespace per le Any class used that is not in the current namespace (or a subnamespace) is imported and typically aliased Global resolution is discouraged, except in the case of classes referenced in strings

ZF2 Patterns

22

Namespace Example

namespace Zend\EventManager; use Zend\Stdlib\CallbackHandler; class EventManager implements EventCollection { /* ... */ }

ZF2 Patterns

23

Using Aliases

namespace Zend\Mvc; use Zend\Stdlib\Dispatchable, Zend\Di\ServiceLocator as Locator; class FrontController implements Dispatchable { public function __construct(Locator $locator) { $this->serviceLocator = $locator; } }

ZF2 Patterns

24

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 Zend\Controller\Action 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 denes a unique namespace

ZF2 Patterns

27

Naming All code in the project is in the Zend namespace Each component denes a unique namespace Classes within a component all live in that namespace or a subnamespace

ZF2 Patterns

27

Naming All code in the project is in the Zend namespace Each component denes a unique namespace Classes within a component all live in that namespace or a subnamespace Typically, a class named after the component is the gateway class

ZF2 Patterns

27

Naming Examples

Zend/EventManager |-- EventCollection.php -- EventManager.php

ZF2 Patterns

28

Naming Examples

Zend/EventManager |-- EventCollection.php -- EventManager.php

namespace Zend\EventManager; class EventManager implements EventCollection { }

ZF2 Patterns

28

Interfaces Interfaces are named after nouns or adjectives, and describe what they provide

ZF2 Patterns

29

Interfaces Interfaces are named after nouns or adjectives, and describe what they provide In most cases, concrete implementations of interfaces live in a subnamespace named after the interface

ZF2 Patterns

29

Interfaces Interfaces are named after nouns or adjectives, and describe what they provide In most cases, concrete implementations of interfaces live in a subnamespace named after the interface More solid Contract-Oriented paradigm

ZF2 Patterns

29

Interface Examples

Zend/Session |-- Storage.php -- Storage |-- ArrayStorage.php -- SessionStorage.php

ZF2 Patterns

30

Interface Examples

Zend/Session |-- Storage.php -- Storage |-- ArrayStorage.php -- SessionStorage.php

namespace Zend\Session; use Traversable, ArrayAccess, Serializable, Countable; interface Storage extends Traversable, ArrayAccess, Serializable, Countable { /* ... */ }

ZF2 Patterns

30

Concrete Implementation

namespace Zend\Session\Storage; use ArrayObject, Zend\Session\Storage, Zend\Session\Exception; class ArrayStorage extends ArrayObject implements Storage { /* ... */ }

ZF2 Patterns

31

Exceptions

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 denes a marker Exception interface

ZF2 Patterns

34

ZF2 Approach We eliminated Zend_Exception entirely Each component denes a marker Exception interface Concrete exceptions live in an Exception subnamespace, and extend SPL exceptions

ZF2 Patterns

34

What the solution provides Catch specic exception types

ZF2 Patterns

35

What the solution provides Catch specic exception types Catch SPL exception types

ZF2 Patterns

35

What the solution provides Catch specic exception types Catch SPL exception types Catch component-level exceptions

ZF2 Patterns

35

What the solution provides Catch specic exception types Catch SPL exception types Catch component-level exceptions Catch based on global exception type

ZF2 Patterns

35

Exception Denitions

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

ZF2 Patterns

36

Exception Denitions

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

namespace Zend\EventManager; interface Exception {}

ZF2 Patterns

36

Exception Denitions

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

namespace Zend\EventManager; interface Exception {}

namespace Zend\EventManager\Exception; use Zend\EventManager\Exception; class InvalidArgumentException extends \InvalidArgumentException implements Exception {}

ZF2 Patterns

36

Catching Exceptions

namespace Zend\EventManager\Exception; use Zend\EventManager\Exception; try { $events->trigger(foo.bar, $object); } catch (InvalidArgumentException $e) { } catch (Exception $e) { } catch (\InvalidArgumentException $e) { } catch (\Exception $e) { }

ZF2 Patterns

37

Autoloading

The Problem Performance issues Many classes are used JIT, and shouldnt be loaded until needed

ZF2 Patterns

39

The Problem Performance issues Many classes are used JIT, and shouldnt be loaded until needed Missing require_once calls lead to errors

ZF2 Patterns

39

ZF2 Approach No more require_once calls!

ZF2 Patterns

40

ZF2 Approach No more require_once calls! Deliver multiple autoloading approaches ZF1-style include_path autoloader Per- namespace/vendor prex autoloading Class-Map autoloading

ZF2 Patterns

40

ZF1-Style Autoloading

require_once Zend/Loader/StandardAutoloader.php; $loader = new Zend\Loader\StandardAutoloader(array( fallback_autoloader => true, )); $loader->register();

ZF2 Patterns

41

ZF2 NS/Prex Autoloading

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

ZF2 Patterns

42

Class-Map Autoloading

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

ZF2 Patterns

43

Class-Map Autoloading

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

ZF2 Patterns

43

Class-Maps? Wont those require work? Yes, they will. But we already have a tool, bin/classmap_generator.php.

ZF2 Patterns

44

Class-Maps? Wont those require work? Yes, they will. But we already have a tool, bin/classmap_generator.php. Usage is trivial:
prompt> cd your/library prompt> php /path/to/classmap_generator.php -w # Class-Map now exists in .classmap.php

ZF2 Patterns

44

Why? Class-Maps show a 25% improvement on the ZF1 autoloader when no acceleration is present

ZF2 Patterns

45

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

ZF2 Patterns

45

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

Pairing namespaces/prexes with specic paths shows >10% gains with no acceleration

ZF2 Patterns

45

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

Pairing namespaces/prexes with specic paths shows >10% gains with no acceleration
and 40% improvements when an opcode cache is in place!

ZF2 Patterns

45

Autoloader Factory With multiple strategies comes the need for a factory

ZF2 Patterns

46

Autoloader Factory With multiple strategies comes the need for a factory Choose several strategies

ZF2 Patterns

46

Autoloader Factory With multiple strategies comes the need for a factory Choose several strategies Class-Map for fastest lookup

ZF2 Patterns

46

Autoloader Factory With multiple strategies comes the need for a factory Choose several strategies Class-Map for fastest lookup Namespace/prex paths for common code

ZF2 Patterns

46

Autoloader Factory With multiple strategies comes the need for a factory Choose several strategies Class-Map for fastest lookup Namespace/prex paths for common code ZF1/PSR0-style fallback autoloader for development

ZF2 Patterns

46

Autoloader Factory Example

require_once Zend/Loader/AutoloaderFactory.php; use Zend\Loader\AutoloaderFactory; AutoloaderFactory::factory(array( Zend\Loader\ClassMapAutoloader => array( __DIR__ . /../library/.classmap.php, __DIR__ . /../application/.classmap.php, ), Zend\Loader\StandardAutoloader => array( namespaces => array( Zend => __DIR__ . /../library/Zend, ), fallback_autoloader => true, ), ));

ZF2 Patterns

47

Start Migrating

You can use the ZF2 autoloaders and class-map generation facilities today; start migrating now!

ZF2 Patterns

48

Plugin Loading

Terminology For our purposes, a plugin is any class that is determined at runtime.

ZF2 Patterns

50

Terminology For our purposes, a plugin is any class that is determined at runtime. Action and view helpers Adapters Filters and validators

ZF2 Patterns

50

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 Prex-path stacks (most common) Setters to indicate classes

ZF2 Patterns

51

The Problem Varying approaches to dynamically discovering plugin classes Paths relative to the calling class Prex-path stacks (most common) Setters to indicate classes Most common approach is terrible

ZF2 Patterns

51

The Problem Varying approaches to dynamically discovering plugin classes Paths relative to the calling class Prex-path stacks (most common) Setters to indicate classes Most common approach is terrible Bad performance Hard to debug No caching of discovered plugins

ZF2 Patterns

51

ZF2 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 Zend\Loader; interface ShortNameLocator { public function isLoaded($name); public function getClassName($name); public function load($name); }

ZF2 Patterns

53

Plugin broker interface

namespace Zend\Loader; interface Broker { public function public function public function public function public function public function public function }

load($plugin, array $options = null); getPlugins(); isLoaded($name); register($name, $plugin); unregister($name); setClassLoader(ShortNameLocator $loader); getClassLoader();

ZF2 Patterns

54

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, dene static conguration

ZF2 Patterns

55

How do I use it? Create a default plugin loader Create a default plugin broker Compose a broker into your class Optionally, dene static conguration Optionally, pass broker and loader conguration

ZF2 Patterns

55

How do I use it? Create a default plugin loader Create a default plugin broker Compose a broker into your class Optionally, dene static conguration Optionally, pass broker and loader conguration Optionally, register plugins with locator and/or broker

ZF2 Patterns

55

Plugin Locator Implementation

namespace Zend\View; use Zend\Loader\PluginClassLoader; class HelperLoader extends PluginClassLoader { /** * @var array Pre-aliased view helpers */ protected $plugins = array( action => Zend\View\Helper\Action, base_url => Zend\View\Helper\BaseUrl, /* ... */ ); }

ZF2 Patterns

56

Plugin broker implementation

class HelperBroker extends PluginBroker protected $defaultClassLoader = Zend\View\HelperLoader; public function load($plugin, array $options = null) { $helper = parent::load($plugin, $options); if (null !== ($view = $this->getView())) { $helper->setView($view); } return $helper; } protected function validatePlugin($plugin) { if (! $plugin instanceof Helper) { throw new InvalidHelperException(); } return true; } }

ZF2 Patterns

57

Composing a broker

use Zend\View\HelperLoader; class Foo { protected $broker; public function broker($spec = null, array $options = array()) { if ($spec instanceof Broker) { $this->broker = $spec; return $spec; } elseif (null === $this->broker) { $this->broker = new PluginBroker(); } if (null === $spec) { return $this->broker; } elseif (!is_string($spec)) { throw new \Exception(); } return $this->broker->load($spec, $options); } }

ZF2 Patterns

58

Locator Precedence (From least specic to most specic) Map dened in concrete plugin loader

ZF2 Patterns

59

Locator Precedence (From least specic to most specic) Map dened in concrete plugin loader Static maps (latest registration having precedence)

ZF2 Patterns

59

Locator Precedence (From least specic to most specic) Map dened in concrete plugin loader Static maps (latest registration having precedence) Mapping passed via instantiation

ZF2 Patterns

59

Locator Precedence (From least specic to most specic) Map dened in concrete plugin loader Static maps (latest registration having precedence) Mapping passed via instantiation Explicit mapping provided programmatically

ZF2 Patterns

59

Dening static maps

use Zend\View\HelperLoader; HelperLoader::addStaticMap(array( url => My\Helper\Url, base_url => Project\Helper\BaseUrl, )); $loader = new HelperLoader(); $class = $loader->load(url); // "My\Helper\Url"

ZF2 Patterns

60

Passing maps via conguration

use Zend\View\HelperLoader; $config = array( url => My\Helper\Url, base_url => Project\Helper\BaseUrl, ); $loader = new HelperLoader($config); $class = $loader->load(url); // "My\Helper\Url"

ZF2 Patterns

61

Passing maps to maps!

use Zend\View\HelperLoader, Zend\Loader\PluginClassLoader; class HelperMap extends PluginClassLoader { protected $plugins = array( url => My\Helper\Url, base_url => Project\Helper\BaseUrl, ); } $helpers = new HelperMap(); $loader = new HelperLoader($helpers); $class = $loader->load(url); // "My\Helper\Url"

ZF2 Patterns

62

Extending loaders

use Zend\View\HelperLoader; class HelperMap extends HelperLoader { public function __construct($options = null) { // Addes to and/or overrides map in HelperLoader $this->registerPlugins(array( url => My\Helper\Url, base_url => Project\Helper\BaseUrl, )); parent::__construct($options); } } $helpers = new HelperMap(); $class = $loader->load(url); // "My\Helper\Url"

ZF2 Patterns

63

Passing maps via broker

use Zend\View\HelperBroker; $broker = new HelperBroker(array( class_loader => array( class => HelperMap, options => array( base_url => App\Helper\BaseUrl, ), ), )); $plugin = $broker->load(base_url); // "App\Helper\BaseUrl"

ZF2 Patterns

64

Creating maps manually

use Zend\View\HelperLoader; $loader = new HelperLoader(); $loader->registerPlugin(url, My\Helper\Url) ->registerPlugins(array( base_url => Project\Helper\BaseUrl, )); $class = $loader->load(url); // "My\Helper\Url"

ZF2 Patterns

65

Managing plugins via a broker By default, it consults the loader for a classname, and instantiates that class with the given arguments

ZF2 Patterns

66

Managing plugins via a broker By default, it consults the loader for a classname, and instantiates that class with the given arguments Optionally, you can also seed the broker, manually registering plugin objects under a given name

ZF2 Patterns

66

Registering a plugin with the broker

use My\Helper\Url; // // // // Assumes: - $request == Request object - $router == Router object - $broker == HelperBroker

$url = new Url($request, $router); $broker->registerPlugin(url, $url); // OR: $broker->registerPlugins(array( url => $url, )); $url = $broker->load(url); // === $url from above

ZF2 Patterns

67

What about lazy-loading? Often you need to congure plugins

ZF2 Patterns

68

What about lazy-loading? Often you need to congure plugins But you dont want an instance hanging around until its actually requested

ZF2 Patterns

68

What about lazy-loading? Often you need to congure plugins But you dont want an instance hanging around until its actually requested Enter the Zend\Loader\LazyLoadingBroker

ZF2 Patterns

68

LazyLoadingBroker Interface

namespace Zend\Loader; interface LazyLoadingBroker extends Broker { public function registerSpec($name, array $spec = null); public function registerSpecs($specs); public function unregisterSpec($name); public function getRegisteredPlugins(); public function hasPlugin($name); }

ZF2 Patterns

69

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

$broker->registerSpec(url, array($request, $router)); $broker->registerSpecs(array( url => array($request, $router), )); if (!$broker->hasPlugin(url)) { // no spec! } $plugins = $broker->getRegisteredPlugins(); // array(url) $url = $broker->load(url); // With $request, $router injected

ZF2 Patterns

71

LazyLoadingBroker Usage Via Conguration

use Zend\View\HelperBroker; $config = array( specs => array( url => array($request, $rourter), ), ); $broker = new HelperBroker($config); $url = $broker->load(url); // With $request, $router injected

ZF2 Patterns

72

New Components

New Components Zend\EventManager Zend\Di

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, ltering, ACL checks, etc., without needing to extend framework code?

ZF2 Patterns

77

The Problem How do we introduce logging/debug points in framework code? How do we allow users to introduce caching without needing to extend framework code? How do we allow users to introduce validation, ltering, ACL checks, etc., without needing to extend framework code? How do we allow users to manipulate the order in which plugins, intercepting lters, events, etc., trigger?

ZF2 Patterns

77

The Problem How do we introduce logging/debug points in framework code? How do we allow users to introduce caching without needing to extend framework code? How do we allow users to introduce validation, ltering, ACL checks, etc., without needing to extend framework code? How do we allow users to manipulate the order in which plugins, intercepting lters, events, etc., trigger? How can we provide tools for userland code to benet from the above?
ZF2 Patterns 77

Solution: Aspect Oriented Programming Code denes various aspects that may be interesting to observe and/or attach to from a consumer

ZF2 Patterns

78

Solution: Aspect Oriented Programming Code denes various aspects that may be interesting to observe and/or attach to from a consumer Basically, all of the solutions well look at can be used to implement AOP in a code base.

ZF2 Patterns

78

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

ZF2 Patterns

81

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

ZF2 Patterns

82

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 lter 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 lter chain as an argument, and is responsible for calling the next in the chain Often, the entire work of a method is simply a lter

ZF2 Patterns

83

Solution: Intercepting Filters Pros


Similar to previous solutions, except that each handler receives the lter chain as an argument, and is responsible for calling the next in the chain Often, the entire work of a method is simply a lter 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 lter chain as an argument, and is responsible for calling the next in the chain Often, the entire work of a method is simply a lter Depending on the design, can allow static/global access

Cons
Sometimes difcult to accomplish complex workows

ZF2 Patterns

83

Solution: Intercepting Filters Pros


Similar to previous solutions, except that each handler receives the lter chain as an argument, and is responsible for calling the next in the chain Often, the entire work of a method is simply a lter Depending on the design, can allow static/global access

Cons
Sometimes difcult to accomplish complex workows 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 lter chain as an argument, and is responsible for calling the next in the chain Often, the entire work of a method is simply a lter Depending on the design, can allow static/global access

Cons
Sometimes difcult to accomplish complex workows Same issues with composition per-class and static usage as seen in event systems Easy to forget to invoke next lter in chain

ZF2 Patterns

83

Solution: Intercepting Filters Pros


Similar to previous solutions, except that each handler receives the lter chain as an argument, and is responsible for calling the next in the chain Often, the entire work of a method is simply a lter Depending on the design, can allow static/global access

Cons
Sometimes difcult to accomplish complex workows Same issues with composition per-class and static usage as seen in event systems Easy to forget to invoke next lter in chain Typically, no ability to prioritize lters

ZF2 Patterns

83

ZF2: EventManager Component Cherry-picks from each of PubSub, SignalSlot, and Intercepting Filters to provide a comprehensive solution

ZF2 Patterns

84

ZF2: EventManager Component Cherry-picks from each of PubSub, SignalSlot, and Intercepting Filters to provide a comprehensive solution Cannot completely solve the composition/static usage issues
We can solve the composition problem in PHP 5.4 via Traits

ZF2 Patterns

84

ZF2: EventManager Component Cherry-picks from each of PubSub, SignalSlot, and Intercepting Filters to provide a comprehensive solution Cannot completely solve the composition/static usage issues
We can solve the composition problem in PHP 5.4 via Traits There are some elegant ways to handle static usage

ZF2 Patterns

84

EventCollection Interface

namespace Zend\EventManager; use Zend\Stdlib\CallbackHandler; interface EventCollection { public function trigger($event, $context, $argv = array()); public function triggerUntil($event, $context, $argv, $callback); public function attach($event, $callback, $priority = 1); public function detach(CallbackHandler $handle); public function getEvents(); public function getHandlers($event); public function clearHandlers($event); }

ZF2 Patterns

85

Triggering Events

use Zend\EventManager\EventManager; $events = new EventManager(); $events->trigger($eventName, $object, $params); /* Where: * - $eventName is the name of the event; usually the current method name * * - $object is the object triggering the event * - $params are the parameters the handler might need to access, usually the method arguments * */

ZF2 Patterns

86

CallbackHandler

$handler = $events->attach(some-event, function($e) use ($log) { $event = $e->getName(); $context = get_class($e->getTarget()); $params = json_encode($e->getParams()); $log->info(sprintf("%s: %s: %s", $event, $context, $params)); });

ZF2 Patterns

87

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: testing results

$results = $events->triggerUntil(some-event, $o, $argv, function($result) { return ($result instanceof SomeType); }); if ($results->stopped()) { return $results->last(); }

ZF2 Patterns

89

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 Zend\EventManager\EventCollection as Events, Zend\EventManager\EventManager; class Foo { protected $events; public function events(Events $events = null) { if (null !== $events) { $this->events = $events; } elseif (null === $this->events) { $this->events = new EventManager(__CLASS__); } return $this->events; } public function doSomething($param1, $param2) { $params = compact(param1, param2); $this->events()->trigger(__FUNCTION__, $this, $params); } } ZF2 Patterns 91

Using a Trait!

use Zend\EventManager\EventCollection as Events, Zend\EventManager\EventManager; trait Eventful { public function events(Events $events = null) { if (null !== $events) { $this->events = $events; } elseif (null === $this->events) { $this->events = new EventManager(__CLASS__); } return $this->events; } } class Foo { use Eventful; protected $events; }

ZF2 Patterns

92

Connecting handlers statically

use Zend\EventManager\StaticEventManager; $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, sufx with a .(pre|post|etc.)

ZF2 Patterns

94

Recommendations Name your events using __FUNCTION__


If triggering multiple events in the same method, sufx 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, sufx 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: dening ways to pass dependencies into an object.

ZF2 Patterns

96

What is Dependency Injection?

Quite simply: dening ways to pass dependencies into an object.


namespace My\Helper; 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 dont.

ZF2 Patterns

97

So, why do people fear it? They dont. They fear Dependency Injection Containers.

ZF2 Patterns

97

Whats 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 My\Helper; 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 mwop\Mvc; 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 dene them explicitly.

ZF2 Patterns

103

The questions How can I be sure I have my dependencies?


You dene them explicitly. You retrieve the object via the container, which ensures the denitions are used.

ZF2 Patterns

103

The questions How can I be sure I have my dependencies?


You dene them explicitly. You retrieve the object via the container, which ensures the denitions are used.

Where do I dene these?

ZF2 Patterns

103

The questions How can I be sure I have my dependencies?


You dene them explicitly. You retrieve the object via the container, which ensures the denitions are used.

Where do I dene these?


Either programmatically, via conguration, or using a tool.

ZF2 Patterns

103

The questions How can I be sure I have my dependencies?


You dene them explicitly. You retrieve the object via the container, which ensures the denitions are used.

Where do I dene these?


Either programmatically, via conguration, 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 dene them explicitly. You retrieve the object via the container, which ensures the denitions are used.

Where do I dene these?


Either programmatically, via conguration, or using a tool.

If I call $object = new Foo(), how do I force using different dependencies?


Calling new doesnt 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 your direct control (e.g. controllers), how do you retain control over your dependencies?

ZF2 Patterns

104

Why use one?

If instantiation of your object is not under your direct control (e.g. controllers), how do you retain control over your dependencies?
Different data access based on application environment

ZF2 Patterns

104

Why use one?

If instantiation of your object is not under your direct control (e.g. controllers), how do you retain control over your dependencies?
Different data access based on application environment Substituting mock/stub implementations during testing

ZF2 Patterns

104

ZF2 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 denitions during development

ZF2 Patterns

105

Service Locator interface

namespace Zend\Di; interface ServiceLocation { public function set($name, $service); public function get($name, array $params = null); }

ZF2 Patterns

106

Dependency Injector interface

namespace Zend\Di; interface DependencyInjection { public function get($name, array $params = null); public function newInstance($name, array $params = null); public function setDefinitions($definitions); public function setDefinition( DependencyDefinition $definition, $serviceName = null); public function setAlias($alias, $serviceName); public function getDefinitions(); public function getAliases(); }

ZF2 Patterns

107

Denitions

namespace Zend\Di; interface DependencyDefinition { public function __construct($className); public function getClass(); public function setConstructorCallback($callback); public function getConstructorCallback(); public function hasConstructorCallback(); public function setParam($name, $value); public function setParams(array $params); public function setParamMap(array $map); public function getParams(); public function setShared($flag = true); public function isShared(); public function addTag($tag); public function addTags(array $tags); public function getTags(); public function hasTag($tag); public function addMethodCall($name, array $args); public function getMethodCalls(); } ZF2 Patterns 108

References

namespace Zend\Di; interface DependencyReference { public function __construct($serviceName); public function getServiceName(); }

ZF2 Patterns

109

Class Denition

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

ZF2 Patterns

110

Setter Injection

use Zend\Di\Definition, Zend\Di\Reference; $service = new Definition(mwop\Service\Resources); $service->addMethod(setResource, array( new Reference(resource) )); $di->setDefinition(resources, $service); $resources = $di->get(resources);

ZF2 Patterns

111

Making it faster Specify constructor parameter maps in denitions

ZF2 Patterns

112

Making it faster Specify constructor parameter maps in denitions Generate Service Locators from a DI container

ZF2 Patterns

112

Parameter maps

$mongoDB->setParam(conn, new Reference(mongo)) ->setParam(name, test) ->setParamMap(array( conn => 0, name => 1, )); // Ensures parameters are in order, without needing // to resort to Reflection API.

ZF2 Patterns

113

Generating a Service Locator from DI

use Zend\Di\ContainerBuilder as DiBuilder; $builder = new DiBuilder($injector); $builder->setContainerClass(AppContext); $container = $builder->getCodeGenerator( __DIR__ . /../application/AppContext.php ); // Returns instance of Zend\CodeGenerator\Php\PhpFile $container->write(); // Write to disk

ZF2 Patterns

114

Example of a generated locator

use Zend\Di\DependencyInjectionContainer; class AppContext extends DependencyInjectionContainer { public function get($name, array $params = array()) { switch ($name) { case request: case Zend\Http\Request: return $this->getZendHttpRequest(); default: return parent::get($name, $params); } } public function getZendHttpRequest() { if (isset($this->services[Zend\Http\Request])) { return $this->services[Zend\Http\Request]; } $object = new \Zend\Http\Request(); $this->services[Zend\Http\Request] = $object; return $object; } } ZF2 Patterns 115

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 conguration les

ZF2 Patterns

117

Making it simpler Use conguration les Can use any format supported by Zend\Config

ZF2 Patterns

117

Conguration (JSON)

{ "production": { "definitions": [ { "class": "Mongo" }, { "class": "MongoDB", "params": { "conn": {"__reference": "mongocxn"}, "name": "mwoptest" }, "param_map": { "conn": 0, "name": 1 } }, { "class": "MongoCollection", "params": { "db": {"__reference": "MongoDB"}, "name": "entries" }, "param_map": { "db": 0, "name": 1 } } ], "aliases": { "mongocxn": "Mongo", "mongo-collection-entries": "MongoCollection" } } } ZF2 Patterns 118

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 Blog\Controller; class Entry implements Dispatchable { public function setResource(Resource $resource) { $this->resource = $resource; } public function dispatch(Request $request, Response $response = null) { /* ... */ $entry = $this->resource->get($id); /* ... */ } }

ZF2 Patterns

120

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

Benets to using DI this way Performance

ZF2 Patterns

122

Benets to using DI this way Performance Code de-coupling

ZF2 Patterns

122

Benets to using DI this way Performance Code de-coupling Simplication 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 denitions

ZF2 Patterns

123

More to come! First-run compilation Tools for scanning classes or namespaces to build denitions Interface injection

ZF2 Patterns

123

More to come! First-run compilation Tools for scanning classes or namespaces to build denitions Interface injection . . . and likely more.

ZF2 Patterns

123

MVC Patterns

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 ne-tune action selection after routing, based on other data in the request environment?

ZF2 Patterns

125

The Problems How do controllers get dependencies? How do we accommodate different controller patterns?
What if we want to ne-tune action selection after routing, based on other data in the request environment? What if we want to pass arguments to action names, or prevalidate arguments?

ZF2 Patterns

125

The Problems How do controllers get dependencies? How do we accommodate different controller patterns?
What if we want to ne-tune action selection after routing, based on other data in the request environment? What if we want to pass arguments to action names, or prevalidate arguments? What if we dont like the Action sufx in action methods?

ZF2 Patterns

125

The Problems How do controllers get dependencies? How do we accommodate different controller patterns?
What if we want to ne-tune action selection after routing, based on other data in the request environment? What if we want to pass arguments to action names, or prevalidate arguments? What if we dont like the Action sufx in action methods? What if . . . ?

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 ne-tune action selection after routing, based on other data in the request environment? What if we want to pass arguments to action names, or prevalidate arguments? What if we dont like the Action sufx in action methods? What if . . . ?

How can we better use server components within the MVC? How can we make the MVC more performant?

ZF2 Patterns

125

The basic structure of a web application is that of a Request/Response lifecycle

ZF2 Patterns

126

The Dispatchable Interface

namespace Zend\Stdlib; interface Dispatchable { public function dispatch( Request $request, Response $response = null ); }

ZF2 Patterns

127

Request and Response Both the Request and Response simply aggregate metadata and content

ZF2 Patterns

128

Request and Response Both the Request and Response simply aggregate metadata and content The Response also has the ability to send itself

ZF2 Patterns

128

Request and Response Both the Request and Response simply aggregate metadata and content The Response also has the ability to send itself HTTP-specic variants will be the core of the MVC
To provide convenience around superglobals, cookies, and common tasks such as determining Accept and Content-Type headers

ZF2 Patterns

128

Anything dispatchable can attach to the MVC

Dispatchable is simply a formalization of the Command pattern

ZF2 Patterns

129

Anything dispatchable can attach to the MVC

Dispatchable is simply a formalization of the Command pattern


Controllers

ZF2 Patterns

129

Anything dispatchable can attach to the MVC

Dispatchable is simply a formalization of the Command pattern


Controllers Servers

ZF2 Patterns

129

Anything dispatchable can attach to the MVC

Dispatchable is simply a formalization of the Command pattern


Controllers Servers Whatever you may dream of! Just implement Dispatchable!

ZF2 Patterns

129

Simple Front Controller Prototype

public function dispatch(Request $request, Response $response = null) { $params = compact(request, response); $this->events()->trigger(__FUNCTION__ . .route.pre, $this, $params); $result = $this->getRouter()->route($request); if (!$result) { $result = array(controller => page, page => 404); } $params[routing] = (object) $result; $this->events()->trigger(__FUNCTION__ . .route.post, $params); $controller = $this->di->get($params[routing]->controller); if (!$controller instanceof Dispatchable) { $controller = new NotFoundController(); } $result = $controller->dispatch($request, $response); $params[__RESULT__] = $result; $this->events()->trigger(__FUNCTION__ . .dispatch.post, $params); return $response; } ZF2 Patterns 130

Getting Involved

Contribute to ZF2! ZF2 wiki: http://bit.ly/zf2wiki zf-contributors mailing list: zf-contributors-subscribe@lists.zend.com IRC: #zftalk.dev on Freenode

ZF2 Patterns

133

ZF2 Git Repository Git guide: http://bit.ly/zf2gitguide GitHub: http://github.com/zendframework/zf2 Ofcial repo: git://git.zendframework.com/zf.git http://git.zendframework.com/ You still need to sign a CLA!

ZF2 Patterns

134

References ZF2 Dependency Injection Proposal http://bit.ly/zf2diproposal http://bit.ly/zf2diprototype ZF2 DI/MVC prototype sandbox http://bit.ly/zf2sandbox (mobile-layout branch is latest)

ZF2 Patterns

135

Thank You! Feedback: http://joind.in/3339 Twitter: http://twitter.com/weierophinney Zend Framework: http://framework.zend.com/

ZF2 Patterns

136

Você também pode gostar