Este documento discute o carregamento de plugins no Zend Framework 2, introduzindo as interfaces de Localizador de Plugins e Agente de Plugins, que fornecem uma abordagem consistente e de alto desempenho para descobrir e carregar classes de plugins.
2. 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.
3. Advertência
Esta apresentação não visa saciar sua sede de
conhecimento, mas deixar você sedento por
ele.
4. O que esperar do Zend Framework 2
Flávio Gomes da
Silva Lisboa
www.fgsl.eti.br
@fgsl
5.
6. Inspirador Inspirado
Desde 2008 capacitando
profissionais em Zend
Framework
@eminetto
@fgsl
ESGOTADO
ESGOTADO
8. 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
9. 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.
10. 1.5.0 – Março de 2008
Primeiro release menor
Zend_Form
Zend_Layout
Sistema de view helper ciente
do layout
Layout content
View content
11. 1.6.0 – Setembro de 2008
Integração com Dojo
Zend_Test: extensão PHPUnit
para controladores
Action helper ContextSwitch
HTML JSON
XML
12. 1.7.0 – Novembro de 2008
Suporte à AMF
Melhorias de performance
13. 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
14. 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
15. 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”
16. 1.11.0 – Novembro de 2010
Suporte mobile via
Zend_Http_UserAgent
API SimpleCloud via
Zend_Cloud
26. 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.”
28. O foco do Zend Framework 2.0 é na melhoria da
consistência e performance.
29. Código Explícito
Não é isto:
class SoraniknatuController
extends Zend_Controller_Action
{
public function useTheRingAction()
{
$this->view->object = 'imagination';
}
}
Onde isso Quando
E os
está ocorre a
layouts?
definido? renderização?
33. Passos de Bebê
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
37. 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
38. 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.
39. Exemplo de Namespace
namespace ZendEventManager;
use ZendStdlibCallbackHandler;
class EventManager implements EventCollection
{
/* ... */
}
40. Usando Apelidos
namespace ZendMvc;
use ZendStdlibDispatchable,
ZendDiServiceLocator as Locator;
class FrontController implements Dispatchable
{
public function __construct(Locator $locator)
{
$this->serviceLocator = $locator;
}
}
42. Importando Classes
use Zend_Controller_Action as Controller;
class PowerController extends Controller
{ ZF1
}
Como ficará:
use ZendControllerAction as Controller;
class PowerController extends Controller
{ ZF2
}
43. 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.
46. 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
47. Exemplo de Interfaces
namespace ZendSession;
use Traversable, ArrayAccess, Serializable, Countable;
interface Storage extends Traversable, ArrayAccess, Serializable,
Countable
{
}
50. 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
51. 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
52. 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
53. Definições de Exceção
namespace ZendEventManager;
interface Exception
{
}
namespace ZendEventManagerException;
use ZendEventManagerException;
class InvalidArgumentException extends InvalidArgumentException
implements Exception
{
}
56. 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.
57. 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
58. Autocarregamento estilo ZF1
require_once 'Zend/Loader/StandardAutoloader.php';
$loader = new ZendLoaderStandardAutoloader(array(
'fallback_autoloader' => true,
));
$loader->register();
EX
EM
PL
O
59. Autocarregamento
Namespace/Prefixo ZF2
require_once 'Zend/Loader/StandardAutoloader.php';
$loader = new ZendLoaderStandardAutoloader();
$loader->registerNamespace('My', __DIR__ . '/../library/My')
->registerPrefix('Fgsl_', __DIR__ . '/../library/Fgsl');
$loader->register();
EX
EM
PL
O
60. Autocarregamento com Mapas de
Classes
return array(
'GreenLanternHal' => __DIR__ . '/Lantern/Hal.php',
);
require_once 'Zend/Loader/ClassMapAutoloader.php';
$loader = new ZendLoaderClassMapAutoloader();
$loader->registerAutoloadMap(__DIR__ .
'/../library/.classmap.php');
$loader->register();
61. 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:
prompt> cd your/library
prompt> php /path/to/classmap_generator.php -w
A execução desse script cria o Mapa de
Classes em .classmap.php
62. 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.
63. 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
67. 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
69. 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
70. 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
71. Interface de Localização de
Plugins
namespace ZendLoader;
interface ShortNameLocator
{
public function isLoaded($name);
public function getClassName($name);
public function load($name);
}
72. Interface de Agente de Plugins
namespace ZendLoader;
interface Broker
{
public function load($plugin, array $options = null);
public function getPlugins();
public function isLoaded($name);
public function register($name, $plugin);
public function unregister($name);
public function setClassLoader(ShortNameLocator $loader);
public function getClassLoader();
}
73. 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
74. Implementação do Localizador de
Plugins
namespace ZendView;
use ZendLoaderPluginClassLoader;
class HelperLoader extends PluginClassLoader
{
/**
* @var array Pre-aliased view helpers
*/
protected $plugins = array(
'action'
=> 'ZendViewHelperAction',
'base_url' => 'ZendViewHelperBaseUrl',
/* ... */
);
}
75. Implementação do Agente de
Plugins
class HelperBroker extends PluginBroker
{
protected $defaultClassLoader = 'ZendViewHelperLoader';
public function load($plugin, array $options = null)
{
$helper = parent::load($plugin, $options);
if (null !== ($view = $this->getView())) {
$helper->setView($view);
}
return $helper;
}
protected function validatePlugin($plugin)
{
if (! $plugin instanceof Helper) {
throw new InvalidHelperException();
}
return true;
}
}
76. Compondo um Agente
use ZendViewHelperLoader;
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);
}
}
77. 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
84. 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
85. Registrando um Plugin com o
Agente
use KilowogHelperUrl;
// 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
86. 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
ZendLoaderLazyLoadingBroker
87. LazyLoadingBroker Interface
namespace ZendLoader;
interface LazyLoadingBroker extends Broker
{
public function registerSpec($name, array $spec = null);
public function registerSpecs($specs);
public function unregisterSpec($name);
public function getRegisteredPlugins();
public function hasPlugin($name);
}
88. 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
89. 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
90. Usando LazyLoadingBroker via
Configuração
use ZendViewHelperBroker;
$config = array(
'specs' => array(
'url' => array($request, $router),
),
);
$broker = new HelperBroker($config);
$url
= $broker->load('url'); // Com $request, $router é
injetado
96. 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?
97. 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?
98. 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
99. 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
100. 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
101. Requisitos
Previsibilidade de argumentos passados para
manipuladores.
Habilidade de anexar a muitos componentes
emissores de eventos de uma vez.
102. 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
103. 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
104. 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...
105. 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.
106. 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
107. 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.
108. 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.
109. 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.
113. Combinação
de Poderes
Linka
Wheeler
Gi Ma-Ti Kwame
114. 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.
115. Interface EventCollection
namespace ZendEventManager;
use ZendStdlibCallbackHandler;
interface EventCollection
{
public function trigger($event, $context, $argv = array());
public function triggerUntil($event, $context, $argv, $callback);
public function attach($event, $callback, $priority = 1);
public function detach(CallbackHandler $handle);
public function getEvents();
public function getHandlers($event);
public function clearHandlers($event);
}
116. Disparando Eventos
use ZendEventManagerEventManager;
$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
*
*/
118. Callback Handler com Prioridade
$handler = $events->attach('algum-evento', function($e) use
($log) {
/* o mesmo que o anterior */
}, 100); // Priorize! (números altos ganham)
120. 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();
}
121. Compondo um EventManager
use ZendEventManagerEventCollection as Events,
ZendEventManagerEventManager;
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);
}
}
122. Usando um Trait!
use ZendEventManagerEventCollection as Events,
ZendEventManagerEventManager;
trait Eventful
{
public function events(Events $events = null)
{
if (null !== $events) {
$this->events = $events;
} elseif (null === $this->events) {
$this->events = new EventManager(__CLASS__);
}
return $this->events;
}
}
class Arisia
{
use Eventful;
protected $events;
}
123. Conectando Manipuladores
Estaticamente
use ZendEventManagerStaticEventManager;
$events = StaticEventManager::getInstance();
$events->connect('Arisia', 'algum-evento', function
($e) {
/* ... */
});
124. 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!
126. O Que é Injeção de Dependência?
Muito simples: definir modos de passar
dependências para dentro de um objeto.
namespace TomarreHelper;
class Url
{
public function __construct(Request $request)
{
$this->request = $request;
}
public function setRouter(Router $router)
{
$this->router = $router;
}
}
127. Então porque as pessoas tem
medo disso?
Porque elas não fazem isso.
Elas temem os Conteineres de Injeção de
Dependência.
128. 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.
131. Objeto com Dependências
namespace TomarreHelper;
class Url
{
public function __construct(Request $request)
{
$this->request = $request;
}
public function setRouter(Router $router)
{
$this->router = $router;
}
}
132. Outro Objeto com Dependências
namespace mwopMvc;
class Router
{
public function addRoute(Route $route)
{
$this->routes->push($route);
}
}
133. 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'));
134. 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.
135. 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!
136. 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.
137. 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.
138. Interface para Localizador de
Serviços
namespace ZendDi;
interface ServiceLocation
{
public function set($name, $service);
public function get($name, array $params = null);
}
139. Interface para Injetor de
Dependências
namespace ZendDi;
interface DependencyInjection
{
public function get($name, array $params = null);
public function newInstance($name, array $params = null);
public function setDefinitions($definitions);
public function setDefinition(
DependencyDefinition $definition, $serviceName = null);
public function setAlias($alias, $serviceName);
public function getDefinitions();
public function getAliases();
}
140. Definições
namespace ZendDi;
interface DependencyDefinition
{
public function __construct($className);
public function getClass();
public function setConstructorCallback($callback);
public function getConstructorCallback();
public function hasConstructorCallback();
public function setParam($name, $value);
public function setParams(array $params);
public function setParamMap(array $map);
public function getParams();
public function setShared($flag = true);
public function isShared();
public function addTag($tag);
public function addTags(array $tags);
public function getTags();
public function hasTag($tag);
public function addMethodCall($name, array $args);
public function getMethodCalls();
}
142. Definição de Classe
use ZendDiDefinition,
ZendDiReference;
$mongo = new Definition('Mongo');
$mongoDB = new Definition('MongoDB');
$mongoDB->setParam('conn', new Reference('mongo'))
->setParam('name', 'test');
$coll = new Definition('MongoCollection');
$coll->setParam('db', new Reference('mongodb'))
->setParam('name', 'resource');
$di->setDefinitions(array(
'mongo'=> $mongo,
'mongodb' => $mongoDB,
'resource' => $coll,
));
$resource = $di->get('resource');
143. Injeção por Modificador
use ZendDiDefinition,
ZendDiReference;
$service = new Definition('mwopServiceResources');
$service->addMethod('setResource', array(
new Reference('resource')
));
$di->setDefinition('resources', $service);
$resources = $di->get('resources');
144. 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.
145. 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
146. Gerando um Localizador de
Serviços a partir de DI
use ZendDiContainerBuilder as DiBuilder;
$builder = new DiBuilder($injector);
$builder->setContainerClass('AppContext');
$container = $builder->getCodeGenerator(
__DIR__ . '/../application/AppContext.php'
); // Retorna uma instância de ZendCodeGeneratorPhpPhpFile
$container->write(); // Grava no disco
147. Exemplo de um localizador
gerado
use ZendDiDependencyInjectionContainer;
class AppContext extends DependencyInjectionContainer
{
public function get($name, array $params = array())
{
switch ($name) {
case 'request':
case 'ZendHttpRequest':
return $this->getZendHttpRequest();
default:
return parent::get($name, $params);
}
}
public function getZendHttpRequest()
{
if (isset($this->services['ZendHttpRequest'])) {
return $this->services['ZendHttpRequest'];
}
$object = new ZendHttpRequest();
$this->services['ZendHttpRequest'] = $object;
return $object;
}
}
148. Usando um localizador gerado
$context = new AppContext();
$request = $context->get('request');
// O mesmo que usar um localizador de serviços ou
um conteiner DI!
149. Fazendo mais simples ainda
Use arquivos de configuração
Você pode usar qualquer formato suportado
por ZendConfig:
INI
JSON
XML
YAML
151. Quais são os casos de uso no
ZF2?
Um grandão.
Tirar os controladores
MVC do conteiner
152. Um Controlador de Ação
namespace BlogController;
class Entry implements Dispatchable
{
public function setResource(Resource $resource)
{
$this->resource = $resource;
}
public function dispatch(Request $request, Response $response =
null)
{
/* ... */
$entry = $this->resource->get($id);
/* ... */
}
}
153. 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);
/* ... */
}
}
154. Benefícios de usar DI deste modo
Performance
Desacoplamento de código
Simplificação do código do controlador
155. 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.
158. 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...
159. 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?
160. A estrutura básica de uma
aplicação web é a de um
ciclo de vida
Requisição/Resposta
161. A interface Dispatchable
namespace ZendStdlib;
interface Dispatchable
{
public function dispatch(
Request $request, Response $response = null
);
}
162. 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.
163. 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!
164. 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;
}
165. 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.
166. 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!