O que esperar do Zend Framework 2

Post on 26-May-2015

5.008 views 3 download

description

Novidades do Zend Framework 2. Palestra proferida na PHP Conference Brasil 2011.

Transcript of O que esperar do Zend Framework 2

Existe uma coisa que um programador PHP não pode ter...

MEDO!

Advertência

Alguns exemplos de código são exemplos de como implementar construções com Zend Framework 2 e não do Zend Framework 2.

Alguns exemplos fazem uso de construções disponíveis apenas no PHP 5.4.

Advertência

Esta apresentação não visa saciar sua sede de conhecimento, mas deixar você sedento por ele.

O que esperar do Zend Framework 2

@fgsl

Flávio Gomes da Silva Lisboa

www.fgsl.eti.br

ESGOTADO

@eminetto

Inspirador Inspirado

@fgsl

Desde 2008 capacitando profissionais em Zend Framework

ESGOTADO

Uma Breve História do ZF

Flávio Gomes da Silva Lisboa

A Gênese

Outubro de 2005: o projeto é anunciado Março de 2006: primeiro release público,

0.1.0 Final de 2006: Reescrita do MVC

1.0.0, julho de 2007

Primeiro release estável Sistema básico de MVC,

com plugins, action helpers, renderização automatizada, etc.

Muitos consumidores de APIs de web services

Classes servidoras para XML-RPC e REST.

Primeiro release menor Zend_Form Zend_Layout Sistema de view helper ciente

do layout

1.5.0 – Março de 2008

Layout content

View content

1.6.0 – Setembro de 2008

Integração com Dojo Zend_Test: extensão PHPUnit

para controladores Action helper ContextSwitch

HTML

XML

JSON

1.7.0 – Novembro de 2008

Suporte à AMF Melhorias de performance

1.8.0 – Abril de 2009

Zend_Tool Zend_Application

AMPLAMENTE USADO!

Matthew O'Phinney, ZF Leader e autor do conteúdo no qual esta apresentação se baseia

1.9.0 – Agosto de 2009

Zend_Feed_Reader Suporte/compatibilidade

com PHP 5.3 Adições levadas pela

comunidade Início da caça mensal a

bugs

1.10.0 – Janeiro de 2009

Integração de ControllerTestCase com Zend_Application

Adição de Zend_Feed_Writer Mudanças na documentação:

adoção do PhD para renderizar o manual do usuário, introdução do sistema de comentário e a seção “Learning Zend Framework”

1.11.0 – Novembro de 2010

Suporte mobile via Zend_Http_UserAgent

API SimpleCloud via Zend_Cloud

Arquitetura

Arquitetura

Arquitetura

Flexibilidade

Liberdade de Escolha

E daqui vamos para onde?

Revolução

Inevitável

Junte-se ou morra!

Evolução

“A mutação é a chave para a nossa evolução. Ela nos permitiu evoluir de um organismo unicelular à espécie dominante do planeta. O processo é lento, normalmente leva milhares e milhares de anos. Mas em algumas centenas de milênios a evolução dá um salto.”

Evolução

O foco do Zend Framework 2.0 é na melhoria da consistência e performance.

Código Explícito

Não é isto:

class SoraniknatuControllerextends Zend_Controller_Action{public function useTheRingAction(){$this->view->object = 'imagination';}}

Onde isso está

definido?

Quando ocorre a

renderização?

E os layouts?

Mágica ou

Bruxaria?

Código Explícito

Código explícito é fácil de entender. Código explícito é fácil de analisar. Código explícito é fácil de manter.

Melhorias incrementais

Conversão de código dos prefixos de fornecedor (por exemplo “Zend_Phaser”) para namespaces do PHP 5.3

Refatoração das exceções Somente autoload Melhoria e padronização do sistema de plugins

Passos de Bebê

Reescrever somente onde faz sentido

Mudanças na Infraestrutura

Namespaces

O problema

Nomes de classes muito grandes Dificuldade de refatorar Dificuldade de reter semântica com nomes mais

curtos

Zend_Form_Decorator_Marker_File_Interface

A solução

Cada arquivo de classe declara uma namespace

Um namespace por arquivo Qualquer classe usada que não estiver no

namespace atual (ou em um subnamespace) é importada e tipicamente apelidada

A resolução global é desencorajada, exceto no caso de classes referenciadas em strings.

Exemplo de Namespace

namespace Zend\EventManager;

use Zend\Stdlib\CallbackHandler;

class EventManager implements EventCollection{/* ... */}

Usando Apelidos

namespace Zend\Mvc;

use Zend\Stdlib\Dispatchable,Zend\Di\ServiceLocator as Locator;

class FrontController implements Dispatchable{public function __construct(Locator $locator){$this->serviceLocator = $locator;}}

Recomendação para Migração

Importe classes com o comando use em vez de fazer chamadas com require_once em seu código!

Importando Classes

use Zend_Controller_Action as Controller;

class PowerController extends Controller{

}

use Zend\Controller\Action as Controller;

class PowerController extends Controller{

}

ZF1

ZF2

Como ficará:

Nomeação

Todo código no projeto está no namespace “Zend”

Cada componente define um namespace único Classes dentro de um componente residem

naquele namespace ou em um subnamespace Tipicamente, uma classe nomeada de acordo

com o componente é a classe gateway.

Exemplos de Nomeação

namespace Zend\EventManager;

class EventManager implements EventCollection{

}

Interfaces

Interfaces

Interfaces são nomeadas de acordo com nomes e adjetivos, e descrevem o que elas provêem

Na maioria dos casos, implementações concretas interfaces residem em um subnamespace nomeado de acordo com a interface

Um paradigma Orientado a Contrato mais forte

Exemplo de Interfaces

namespace Zend\Session;

use Traversable, ArrayAccess, Serializable, Countable;

interface Storage extends Traversable, ArrayAccess, Serializable, Countable{

}

Implementação Concreta

namespace Zend\Session\Storage;

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

class ArrayStorage extends ArrayObject implements Storage{/* ... */}

Exceções

O problema

Todas as exceções derivavam de uma classe comum

Incapacidade de estender os tipos de exceção semânticas oferecidas na SPL

Estratégias de captura limitadas Forte dependência para cada e todos os

componenentes

Abordagem ZF2

Zend_Exception foi eliminado Cada componente define uma interface

Exception marcadora Exceções concretas residem em um

subnamespace Exception, e estendem exceções SPL

O que a solução provê

Captura tipos de exceções específicas Captura tipos de exceções SPL Captura exceções no nível do componente Captura baseada no tipo de exceção global

Definições de Exceçãonamespace Zend\EventManager;

interface Exception{

}

namespace Zend\EventManager\Exception;

use Zend\EventManager\Exception;

class InvalidArgumentException extends \InvalidArgumentException implements Exception{

}

Capturando Exceções

namespace Zend\EventManager\Exception;

use Zend\EventManager\Exception;

try {$events->trigger('dom.quixote', $object);} catch (InvalidArgumentException $e) {} catch (Exception $e) {} catch (\InvalidArgumentException $e) {} catch (\Exception $e) {}

Autocarregamento

O problema

Problemas de performance Muitas classes são usadas apenas no momento

adequado e não devem ser carregadas até que seja necessário.

A falta de chamadas require_once leva a erros.

Abordagem ZF2

Chega de chamadas require_once! Múltiplas abordagens de autocarregamento

Autocarregador via include_path estilo ZF1 Autocarregamento pelo namespace / prefixo do

fornecedor Autocarregamento por Mapa de Classes

Autocarregamento estilo ZF1

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

EXEMPLO

Autocarregamento Namespace/Prefixo ZF2

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

EXEMPLO

Autocarregamento com Mapas de Classes

return array('Green\Lantern\Hal' => __DIR__ . '/Lantern/Hal.php',);

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

Mapas de Classes? Mas não dá trabalho pra fazer?

Sim, dá trabalho. Mas nós temos uma ferramenta, bin/classmap_generator.php

E o uso é trivial:

A execução desse script cria o Mapa de Classes em .classmap.php

prompt> cd your/libraryprompt> php /path/to/classmap_generator.php -w

Por que?

Mapas de Classes mostram 25% de melhoria no carregador do ZF1 quando não é usada aceleração.

E 60-85% quando um cache de opcode está em uso.

O emparelhamento namespaces/prefixos com caminhos especificados mostra um ganho de 10% sem aceleração.

E 40% de melhoria quando uma cache de opcode é usado.

Fábrica de Autocarregadores

Com múltiplas estratégias vem a necessidade por uma fábrica.

Escolha diversas estratégias: Mapa de Classes para pesquisa mais rápida Caminhos namespace/prefixo para código comum Autocarregador de reserva estilo ZF1/PSR-0 para

desenvolvimento

PSR: PHP Specification Request

Exemplo de Fábrica de Autocarregadores

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

Quando posso migrar?

Você pode usar os autocarregadores e as facilidades de geração dos mapas de classe do ZF2... hoje! Pode começar a migração!

Carregamento de Plugins

Terminologia

Para nossos propósitos, um “plugin” é qualquer classe que é determinada em tempo de execução.

Auxiliares de Controle e Visão Adaptadores Filtros e Validadores

Plugins

O Problema

Variar abordagens para descobrir classes plugin

Caminhos relativos para as classes chamadas Pilhas prexifo-caminho (mais comum) Modificadores para indicar classes

A abordagem mais comum é terrível Má performance Difícil de depurar Sem caching de plugins descobertos

Abordagem ZF2: o Agente de Plugins

Interface de Localização de Plugins Permite variar a implementação de pesquisa de

plugins

Interface de Agente de Plugins Compõe um Localizador de Plugins

Interface de Localização de Plugins

namespace Zend\Loader;interface ShortNameLocator{public function isLoaded($name);public function getClassName($name);public function load($name);}

Interface de Agente de Plugins

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

Como usar?

Crie um carregador de plugins padrão Crie um agente de plugins padrão Componha um agente dentro de sua classe Opcionalmente, defina configuração estática Opcionalmente, passe a configuração de

agente e carregador Opcionalmente, registre plugins com o

localizador ou o agente

Implementação do Localizador de Plugins

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

Implementação do Agente de Plugins

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

Compondo um Agenteuse Zend\View\HelperLoader;class Sinestro{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);}}

Precedência dos Localizadores

(Do menos para o mais específico) Mapa definido no carregador de plugins

concreto Mapas estáticos ( o registro mais recente tem

precedência) Mapeamento passado via instanciação Mapeamento explícito provido

programaticamente

Definindo Mapas Estáticos

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

Passando Mapas via Configuração

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

Passando Mapas para Mapas!

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

Estendendo Carregadoresuse Zend\View\HelperLoader;class HelperMap extends HelperLoader{public function __construct($options = null){// Adiciona e/ou sobrescreve o mapa do HelperLoader$this->registerPlugins(array('url'=> 'Kilowog\Helper\Url','base_url' => 'Project\Helper\BaseUrl',));parent::__construct($options);}}$helpers = new HelperMap();$class= $loader->load('url'); // "Kilowog\Helper\Url"

Passando Mapas via Agente

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"

Criando Mapas Manualmente

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

Gerenciando Plugins via Agente

Por padrão, o carregador é consultado para um nome de classe, e instancia a classe com os argumentos dados

Opcionalmente, você pode alimentar o agente, registrando objetos plugins manualmente sob um dado nome

Registrando um Plugin com o Agente

use Kilowog\Helper\Url;// Assume:// - $request == objeto Request// - $router == objeto Router// - $broker == HelperBroker

$url = new Url($request, $router);$broker->registerPlugin('url', $url); // OU:$broker->registerPlugins(array('url' => $url,));$url = $broker->load('url'); // === $url acima

E sobre o carregamento tardio?

Frequentemente você precisa configurar plugins

Mas você quer uma instância só quando ela for realmente requisitada

É aí que entra Zend\Loader\LazyLoadingBroker

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

Usando LazyLoadingBroker

Registra “especificações” com o agente Quando o plugin é requisitado, as opções

fornecidas serão usadas a menos que novas opções sejam passadas

De todas as outras maneiras, ele comporta-se como outros agentes, incluindo a permissão do registro explícito de plugins

Usando LazyLoadingBroker

$broker->registerSpec('url', array($request, $router));$broker->registerSpecs(array('url' => array($request, $router),));if (!$broker->hasPlugin('url')) {// sem especificação!}$plugins = $broker->getRegisteredPlugins(); // array('url')$url = $broker->load('url'); // Com $request, $router é injetado

Usando LazyLoadingBroker via Configuração

use Zend\View\HelperBroker;$config = array('specs' => array('url' => array($request, $router),),);$broker = new HelperBroker($config);$url= $broker->load('url'); // Com $request, $router é injetado

E ainda tem mais!

Novos Componentes

E Componentes Poderosos!

Novos Componentes

Zend\EventManager Zend\Di

EventManager

O Problema

Como nós introduzimos pontos de log/debug no código do framework?

Como nós permitimos que os usuários introduzam caching sem necessidade de estender o código do framework?

Como nós permitimos que os usuários introduzam validação, filtragem, verificações de controle de acesso, etc., sem necessariamente estender o código do framework?

O Problema

Como permitirmos que os usuários manipulem a ordem na qual plugins, filtros de interceptação, eventos, etc., são disparados.

Como nós podemos prover ferramentas para o código do usuário trabalhe em prol das questões anteriores?

Solução: Programação Orientada a Aspectos

O código define vários “aspectos” que podem ser interessantes observar e/ou anexar a partir de um consumidor.

Basicamente, todas as soluções que examinaremos podem ser usadas para implementar POA em um código base.

www.fgsl.eti.br

Palestras

Requisitos

Projeto que seja razoavelmente fácil de entender.

Permitir anexar manipuladores de forma estática ou por instância, preferencialmente de ambas as formas.

Preferencialmente enquanto reter o estado não-global ou permitir sobrescrita.

Permitir interrupção da execução Permitir a priorização de manipuladores

Requisitos

Projeto que seja razoavelmente fácil de entender.

Permitir anexar manipuladores de forma estática ou por instância, preferencialmente de ambas as formas.

Preferencialmente enquanto reter o estado não-global ou permitir sobrescrita.

Permitir interrupção da execução Permitir a priorização de manipuladores

Requisitos

Previsibilidade de argumentos passados para manipuladores.

Habilidade de anexar a muitos componentes emissores de eventos de uma vez.

Solução: Observador de Sujeitos

Prós Simples de entender Interfaces SPL são bem conhecidas (mas

limitadas)

Contras Tipicamente, não pode interromper a execução de

observadores remanescentes Requer um sistema para cada componente e/ou

classe Tipicamente, sem habilidade para priorizar

manipuladores

Solução: Publicador/Sobrescritor de Eventos

Prós Sobrescrita de notificações arbitrárias Tipicamente por componente + uso global; em

muitas linguagens, um único agregador global Paradigma bem-conhecido na programação de

interfaces com o usuário (pense em Javascript) Tende a ser um Turing completo

Solução: Publicador/Sobrescritor de Eventos (PubSub)

Contras Frequentemente, precisa testar o evento fornecido

para garantir que você pode manipulá-lo. Uso global implica em agregação estática e/ou

dependências estáticas. … mas o uso por componente implica em um

boilerplate para compor em cada classe se ele for usado.

Tipicamente, sem habilidade para priorizar manipuladores.

Falaremos mais sobre isso mais tarde...

Pausa para esclarecimento

boilerplate é o termo usado para descrever seções de código que foram incluídas em muitos lugares com pouca ou nenhuma alteração.

Solução: SignalSlots

Prós Conceito bem conhecido nos círculos de Ciência da

Computação O código emite sinais, que são interceptados por

slots (vulgos manipuladores) Tipicamente, compõe um gerenciador de sinais em

uma classe, mas pode ser integrado com um gerenciador global também

Geralmente tem algumas habilidades para priorizar manipuladores

Solução: SignalSlots

Contras Esse palavreado não é bem conhecido entre

programadores PHP. Argumentos irão variar entre sinais. Os mesmos problemas com composição por classe

e uso estático como vemos em sistemas de eventos.

Filtros de Interceptação

Prós Similar às soluções anteriores, exceto que cada

manipulador recebe a cadeia de filtros como um argumento, e é responsável por chamar o próximo na cadeia.

Frequentemente, o “trabalho” inteiro de um método é simplesmente um executar um filtro.

Dependendo do projeto, pode permitir acesso global/estático.

Filtros de Interceptação

Contras Algumas vezes é difícil acompanhar fluxos de

trabalho complexos. Os mesmos problemas com composição por classe

e uso estático como vemos em sistemas de evento. É fácil esquecer de invocar o próximo filtro na

cadeia. Tipicamente, sem habilidade de priorizar filtros.

Mas qual a

solução afinal?

Nenhuma!

Todas!

Linka

Ma-Ti Kwame

Wheeler

Gi

Co m

bina

ção

de P

oder

es

ZF2: EventManager Component

A cereja do bolo de cada solução, PubSub, SignalSlot, e Filtros de Interceptação, para prover uma solução compreensiva.

Não pode resolver completamente os problemas de composição/uso estático.

Nós podemos resolver o problema da composição no PHP 5.4 com Traits.

Há formas elegantes de manipular o uso estático.

Interface EventCollection

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

Disparando Eventos

use Zend\EventManager\EventManager;$events = new EventManager();$events->trigger($eventName, $object, $params);/* Onde: * - $eventName é o nome do evento; geralmente o nome do evento atual** - $object é o objeto que está disparando o evento* - $params são os parâmetros que o manipulador pode precisar para ter acesso,geralmente os argumentos do método**/

CallbackHandler

$handler = $events->attach('algum-evento', 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));});

Callback Handler com Prioridade

$handler = $events->attach('algum-evento', function($e) use ($log) {/* o mesmo que o anterior */}, 100); // Priorize! (números altos ganham)

Interrompendo a Execução: Testando Resultados

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

Interrompendo a Execução: via Manipuladores

$events->attach('algum-evento', function($e) {$result = new Result;$e->stopPropagation(true);return $result;});$results = $events->trigger('algum-evento', $object, $params);if ($results->stopped()) {return $results->last();}

Compondo um EventManageruse Zend\EventManager\EventCollection as Events,Zend\EventManager\EventManager;class Arisia{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);}}

Usando um 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 Arisia{use Eventful;protected $events;}

Conectando Manipuladores Estaticamente

use Zend\EventManager\StaticEventManager;$events = StaticEventManager::getInstance();$events->connect('Arisia', 'algum-evento', function ($e) {/* ... */});

Recomendações

Nomeie seus eventos usando __FUNCTION__ Se disparar múltiplos eventos no mesmo método,

sufixe com um “.(pre|pos|etc.)”

Forneça para o construtor do EventManager tanto o nome da classe quanto um ou mais nomes de “serviços”, para fazer anexações estáticas mais semânticas.

Isso permite que um único callback ouça muitos componentes!

Injeção de Dependência (DI)

O Que é Injeção de Dependência?

Muito simples: definir modos de passar dependências para dentro de um objeto.

namespace Tomarre\Helper;class Url{public function __construct(Request $request){$this->request = $request;}public function setRouter(Router $router){$this->router = $router;}}

Então porque as pessoas tem medo disso?

Porque elas não fazem isso. Elas temem os Conteineres de Injeção de

Dependência.

O Que é um Conteiner de Injeção de Dependência

Colocando de forma simples:

Um grafo de objetos para mapear relações de dependência entre

objetos.

Grafos

Novamente, por que as pessoas tem medo disso?

Porque parece mágica!

Objeto com Dependências

namespace Tomarre\Helper;class Url{public function __construct(Request $request){$this->request = $request;}public function setRouter(Router $router){$this->router = $router;}}

Outro Objeto com Dependências

namespace mwop\Mvc;class Router{public function addRoute(Route $route){$this->routes->push($route);}}

Agarrando um Objeto e Usando-o

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

As Questões

Como eu posso estar certo se eu tenho minhas dependências?

Você as define explicitamente. Você recupera o objeto via conteiner, o que garante

que as definições são usadas.

Onde eu defino essas coisas? Programaticamente, via configuração, ou usando

uma ferramenta.

As Questões

Se eu chamar $object = new Salaak(), como eu forço o uso de diferentes dependências?

Chamar new não usa o conteiner. Na verdade, nada força você a usá-lo!

Por que usar um?

Se a instanciação de seus objetos não está debaixo de seu controle direto (por exemplo, controladores), como você retém controle sobre suas dependências?

Acesso a dados diferente baseado no ambiente da aplicação.

Substituição de implementações mock/stub durante o teste.

Abordagem ZF2

Padronizar em uma interface de localizador de serviços.

Prover uma solução DI performática, e integrá-la dentro de um localizador de serviços.

Prover ferramentas para auxiliar na criação de definições de DI durante o desenvolvimento.

Interface para Localizador de Serviços

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

Interface para Injetor de Dependências

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

Definiçõesnamespace 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();}

Referências

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

Definição de Classe

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

Injeção por Modificador

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

Fazendo mais Rápido

Especificar mapas de parâmetros de construtor em definições.

Gerar localizadores de serviço a partir de um conteiner DI.

Mapas de Parâmetros

$mongoDB->setParam('conn', new Reference('mongo'))->setParam('name', 'test')->setParamMap(array('conn' => 0,'name' => 1,));// Garante que os parâmetros estão em ordem, sem precisar// recorrer à API de reflexão

Gerando um Localizador de Serviços a partir de DI

use Zend\Di\ContainerBuilder as DiBuilder;$builder = new DiBuilder($injector);$builder->setContainerClass('AppContext');$container = $builder->getCodeGenerator(__DIR__ . '/../application/AppContext.php'); // Retorna uma instância de Zend\CodeGenerator\Php\PhpFile$container->write(); // Grava no disco

Exemplo de um localizador gerado

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

Usando um localizador gerado

$context = new AppContext();$request = $context->get('request');// O mesmo que usar um localizador de serviços ou um conteiner DI!

Fazendo mais simples ainda

Use arquivos de configuração Você pode usar qualquer formato suportado

por Zend\Config: INI JSON XML YAML

Exemplo de configuração com 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"}}}

Quais são os casos de uso no ZF2?

Um grandão.

Tirar os controladores MVC do conteiner

Um Controlador de Ação

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

O Controlador Frontal

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

Benefícios de usar DI deste modo

Performance Desacoplamento de código Simplificação do código do controlador

E vem mais por aí

Compilação na primeira execução. Ferramentas para vasculhar classes ou

namespaces para construir definições. Injeção de interface. … e mais coisas legais.

Padrões MVC

Padrões MVC

Os Problemas

Como os controladores obtém dependências? Como nós acomodamos diferentes padrões de

controladores? E se se nós quisermos uma seleção de ações mais

apurada, baseada em outros dados no ambiente de requisição?

E se quisermos passar argumentos para nomes de ações, ou argumentos pré-validados?

E se nós não gostarmos do sufixo “Action” nos métodos acionadores?

E se...

Os Problemas

Como nós podemos melhorar o uso de componentes do servidor dentro do MVC?

Como nós podemos fazer o MVC mais performático?

A estrutura básica de uma aplicação web é a de um

ciclo de vida Requisição/Resposta

A interface Dispatchable

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

Requisição e Resposta

Tanto a Requisição quanto a Resposta simplesmente agregam metadados e conteúdo.

A Resposta também tem a habilidade de enviar a si mesma.

Variantes específicas de HTTP serão o núcleo do MVC.

Para prover conveniência em torno de variáveis superglobais, cookies e tarefas comuns tais como determinar os cabeçalhos Accept e Content-Type.

Qualquer Dispatchable pode anexar ao MVC

Dispatchable é simplesmente uma formalização do padrão de projeto Command. Controladores Servidores Qualquer coisa que você sonhar! Apenas

implemente Dispatchable!

Um protótipo simples de um Controlador Frontal

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

Contexto Temporal

Quando esta apresentação foi finalizada, o último release do Zend Framework 1 era o 1.11.11 e o Zend Framework 2 estava na versão 2.0.0beta1.

Mais informações

http://framework.zend.com https://github.com/zendframework/zf2 www.fgsl.eti.br

Aguarde... treinamentos de arquitetura, migração, e desenvolvimento.

Pra quem quer sair na frente, Mão na Massa MVC Zend Framework 2!

Coming soon 2012!