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!