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


  Flávio Gomes da
  Silva Lisboa

 www.fgsl.eti.br




                    @fgsl
Inspirador   Inspirado
                                    Desde 2008 capacitando
                                    profissionais em Zend
                                    Framework

           @eminetto
                         @fgsl
ESGOTADO




    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.
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
1.6.0 – Setembro de 2008

   Integração com Dojo
   Zend_Test: extensão PHPUnit
    para controladores
   Action helper ContextSwitch

         HTML          JSON




         XML
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 SoraniknatuController
                extends Zend_Controller_Action
                {
                public function useTheRingAction()
                {
                $this->view->object = 'imagination';
                }
                }




    Onde isso             Quando
                                              E os
      está                ocorre a
                                            layouts?
    definido?          renderização?
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
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
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 ZendEventManager;

use ZendStdlibCallbackHandler;

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

namespace ZendMvc;

use ZendStdlibDispatchable,
ZendDiServiceLocator 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
{                                           ZF1
}

             Como ficará:
use ZendControllerAction as Controller;

class PowerController extends Controller
{                                           ZF2
}
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 ZendEventManager;

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

use Traversable, ArrayAccess, Serializable, Countable;

interface Storage extends Traversable, ArrayAccess, Serializable,
Countable
{

}
Implementação Concreta


namespace ZendSessionStorage;

use ArrayObject,
ZendSessionStorage,
ZendSessionException;

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ção
                                   namespace ZendEventManager;

                                   interface Exception
                                   {

                                   }

namespace ZendEventManagerException;

use ZendEventManagerException;

class InvalidArgumentException extends InvalidArgumentException
implements Exception
{

}
Capturando Exceções

namespace ZendEventManagerException;

use ZendEventManagerException;

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 ZendLoaderStandardAutoloader(array(
'fallback_autoloader' => true,
));
$loader->register();


                  EX
                     EM
                          PL
                            O
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
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();
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
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 ZendLoaderAutoloaderFactory;
AutoloaderFactory::factory(array(
'ZendLoaderClassMapAutoloader' => array(
__DIR__ . '/../library/.classmap.php',
__DIR__ . '/../application/.classmap.php',
),
'ZendLoaderStandardAutoloader' => 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 ZendLoader;
   interface ShortNameLocator
   {
   public function isLoaded($name);
   public function getClassName($name);
   public function load($name);
   }
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();
}
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 ZendView;
    use ZendLoaderPluginClassLoader;
    class HelperLoader extends PluginClassLoader
    {
    /**
      * @var array Pre-aliased view helpers
      */
    protected $plugins = array(
    'action'
    => 'ZendViewHelperAction',
    'base_url' => 'ZendViewHelperBaseUrl',
    /* ... */
    );
    }
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;
}
}
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);
}
}
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 ZendViewHelperLoader;
HelperLoader::addStaticMap(array(
'url'
=> 'KilowogHelperUrl',
'base_url' => 'ProjectHelperBaseUrl',
));
$loader = new HelperLoader();
$class = $loader->load('url'); // "KilowogHelperUrl"
Passando Mapas via
           Configuração


use ZendViewHelperLoader;
$config = array(
'url'
=> 'KilowogHelperUrl',
'base_url' => 'ProjectHelperBaseUrl',
);
$loader = new HelperLoader($config);
$class = $loader->load('url'); // "KilowogHelperUrl"
Passando Mapas para Mapas!

 use ZendViewHelperLoader,
 ZendLoaderPluginClassLoader;
 class HelperMap extends PluginClassLoader
 {
 protected $plugins = array(
 'url'
 => 'KilowogHelperUrl',
 'base_url' => 'ProjectHelperBaseUrl',
 );
 }
 $helpers = new HelperMap();
 $loader = new HelperLoader($helpers);
 $class
 = $loader->load('url'); // "KilowogHelperUrl"
Estendendo Carregadores
use ZendViewHelperLoader;
class HelperMap extends HelperLoader
{
public function __construct($options = null)
{
// Adiciona e/ou sobrescreve o mapa do
HelperLoader
$this->registerPlugins(array(
'url'
=> 'KilowogHelperUrl',
'base_url' => 'ProjectHelperBaseUrl',
));
parent::__construct($options);
}
}
$helpers = new HelperMap();
$class
= $loader->load('url'); // "KilowogHelperUrl"
Passando Mapas via Agente

use ZendViewHelperBroker;
$broker = new HelperBroker(array(
'class_loader' => array(
'class'
=> 'HelperMap',
'options' => array(
'base_url' => 'AppHelperBaseUrl',
),
),
));
$plugin = $broker->load('base_url'); // "AppHelperBaseUrl"
Criando Mapas Manualmente


use ZendViewHelperLoader;
$loader = new HelperLoader();
$loader->registerPlugin('url', 'KilowogHelperUrl')
->registerPlugins(array(
'base_url' => 'ProjectHelperBaseUrl',
));
$class = $loader->load('url'); // "KilowogHelperUrl"
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 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
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
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);
}
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 ZendViewHelperBroker;
$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

   ZendEventManager
   ZendDi
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!
Combinação




                               de Poderes
                                            Linka
Wheeler




 Gi                    Ma-Ti                    Kwame
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 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);
}
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
*
*/
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 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);
}
}
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;
}
Conectando Manipuladores
       Estaticamente


use ZendEventManagerStaticEventManager;
$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 TomarreHelper;
       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 TomarreHelper;
class Url
{
public function __construct(Request $request)
{
$this->request = $request;
}
public function setRouter(Router $router)
{
$this->router = $router;
}
}
Outro Objeto com Dependências


     namespace mwopMvc;
     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 ZendDi;
interface ServiceLocation
{
public function set($name, $service);
public function get($name, array $params = null);
}
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();
}
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();
}
Referências



namespace ZendDi;
interface DependencyReference
{
public function __construct($serviceName);
public function getServiceName();
}
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');
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');
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 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
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;
}
}
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 ZendConfig:
       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 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);
/* ... */
}
}
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 ZendStdlib;
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!

O que esperar do Zend Framework 2

  • 1.
    Existe uma coisaque um programador PHP não pode ter... MEDO!
  • 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 esperardo Zend Framework 2 Flávio Gomes da Silva Lisboa www.fgsl.eti.br @fgsl
  • 6.
    Inspirador Inspirado Desde 2008 capacitando profissionais em Zend Framework @eminetto @fgsl ESGOTADO ESGOTADO
  • 7.
    Uma Breve Históriado ZF Flávio Gomes da Silva Lisboa
  • 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 de2007  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çode 2008  Primeiro release menor  Zend_Form  Zend_Layout  Sistema de view helper ciente do layout Layout content View content
  • 11.
    1.6.0 – Setembrode 2008  Integração com Dojo  Zend_Test: extensão PHPUnit para controladores  Action helper ContextSwitch HTML JSON XML
  • 12.
    1.7.0 – Novembrode 2008  Suporte à AMF  Melhorias de performance
  • 13.
    1.8.0 – Abrilde 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 – Agostode 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 – Janeirode 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 – Novembrode 2010  Suporte mobile via Zend_Http_UserAgent  API SimpleCloud via Zend_Cloud
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
    E daqui vamospara onde?
  • 23.
  • 24.
  • 25.
  • 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.”
  • 27.
  • 28.
    O foco doZend 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?
  • 30.
    Mágica ou Bruxaria?
  • 31.
    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.
  • 32.
  • 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
  • 34.
  • 35.
  • 36.
  • 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 namespaceZendEventManager; use ZendStdlibCallbackHandler; class EventManager implements EventCollection { /* ... */ }
  • 40.
    Usando Apelidos namespace ZendMvc; useZendStdlibDispatchable, ZendDiServiceLocator as Locator; class FrontController implements Dispatchable { public function __construct(Locator $locator) { $this->serviceLocator = $locator; } }
  • 41.
    Recomendação para Migração Importeclasses com o comando use em vez de fazer chamadas com require_once em seu código!
  • 42.
    Importando Classes use Zend_Controller_Actionas 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.
  • 44.
    Exemplos de Nomeação namespaceZendEventManager; class EventManager implements EventCollection { }
  • 45.
  • 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 namespaceZendSession; use Traversable, ArrayAccess, Serializable, Countable; interface Storage extends Traversable, ArrayAccess, Serializable, Countable { }
  • 48.
    Implementação Concreta namespace ZendSessionStorage; useArrayObject, ZendSessionStorage, ZendSessionException; class ArrayStorage extends ArrayObject implements Storage { /* ... */ }
  • 49.
  • 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 asoluçã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 { }
  • 54.
    Capturando Exceções namespace ZendEventManagerException; useZendEventManagerException; try { $events->trigger('dom.quixote', $object); } catch (InvalidArgumentException $e) { } catch (Exception $e) { } catch (InvalidArgumentException $e) { } catch (Exception $e) { }
  • 55.
  • 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 Mapasde 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
  • 64.
    Exemplo de Fábricade Autocarregadores require_once 'Zend/Loader/AutoloaderFactory.php'; use ZendLoaderAutoloaderFactory; AutoloaderFactory::factory(array( 'ZendLoaderClassMapAutoloader' => array( __DIR__ . '/../library/.classmap.php', __DIR__ . '/../application/.classmap.php', ), 'ZendLoaderStandardAutoloader' => array( 'namespaces' => array( 'Zend' => __DIR__ . '/../library/Zend', ), 'fallback_autoloader' => true, ), ));
  • 65.
    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!
  • 66.
  • 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
  • 68.
  • 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: oAgente 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çãode Plugins namespace ZendLoader; interface ShortNameLocator { public function isLoaded($name); public function getClassName($name); public function load($name); }
  • 72.
    Interface de Agentede 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 Localizadorde 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 Agentede 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 useZendViewHelperLoader; 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 (Domenos 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
  • 78.
    Definindo Mapas Estáticos useZendViewHelperLoader; HelperLoader::addStaticMap(array( 'url' => 'KilowogHelperUrl', 'base_url' => 'ProjectHelperBaseUrl', )); $loader = new HelperLoader(); $class = $loader->load('url'); // "KilowogHelperUrl"
  • 79.
    Passando Mapas via Configuração use ZendViewHelperLoader; $config = array( 'url' => 'KilowogHelperUrl', 'base_url' => 'ProjectHelperBaseUrl', ); $loader = new HelperLoader($config); $class = $loader->load('url'); // "KilowogHelperUrl"
  • 80.
    Passando Mapas paraMapas! use ZendViewHelperLoader, ZendLoaderPluginClassLoader; class HelperMap extends PluginClassLoader { protected $plugins = array( 'url' => 'KilowogHelperUrl', 'base_url' => 'ProjectHelperBaseUrl', ); } $helpers = new HelperMap(); $loader = new HelperLoader($helpers); $class = $loader->load('url'); // "KilowogHelperUrl"
  • 81.
    Estendendo Carregadores use ZendViewHelperLoader; classHelperMap extends HelperLoader { public function __construct($options = null) { // Adiciona e/ou sobrescreve o mapa do HelperLoader $this->registerPlugins(array( 'url' => 'KilowogHelperUrl', 'base_url' => 'ProjectHelperBaseUrl', )); parent::__construct($options); } } $helpers = new HelperMap(); $class = $loader->load('url'); // "KilowogHelperUrl"
  • 82.
    Passando Mapas viaAgente use ZendViewHelperBroker; $broker = new HelperBroker(array( 'class_loader' => array( 'class' => 'HelperMap', 'options' => array( 'base_url' => 'AppHelperBaseUrl', ), ), )); $plugin = $broker->load('base_url'); // "AppHelperBaseUrl"
  • 83.
    Criando Mapas Manualmente useZendViewHelperLoader; $loader = new HelperLoader(); $loader->registerPlugin('url', 'KilowogHelperUrl') ->registerPlugins(array( 'base_url' => 'ProjectHelperBaseUrl', )); $class = $loader->load('url'); // "KilowogHelperUrl"
  • 84.
    Gerenciando Plugins viaAgente  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 Plugincom 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 ocarregamento 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; interfaceLazyLoadingBroker 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
  • 91.
  • 92.
  • 93.
  • 94.
    Novos Componentes  ZendEventManager  ZendDi
  • 95.
  • 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 deSujeitos  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.
  • 110.
  • 111.
  • 112.
  • 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; useZendStdlibCallbackHandler; 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 * */
  • 117.
    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)); });
  • 118.
    Callback Handler comPrioridade $handler = $events->attach('algum-evento', function($e) use ($log) { /* o mesmo que o anterior */ }, 100); // Priorize! (números altos ganham)
  • 119.
    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(); }
  • 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 useZendEventManagerEventCollection 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! useZendEventManagerEventCollection 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!
  • 125.
  • 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 aspessoas 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.
  • 129.
  • 130.
    Novamente, por queas pessoas tem medo disso? Porque parece mágica!
  • 131.
    Objeto com Dependências namespaceTomarreHelper; class Url { public function __construct(Request $request) { $this->request = $request; } public function setRouter(Router $router) { $this->router = $router; } }
  • 132.
    Outro Objeto comDependências namespace mwopMvc; class Router { public function addRoute(Route $route) { $this->routes->push($route); } }
  • 133.
    Agarrando um Objetoe 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 usarum?  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 Localizadorde Serviços namespace ZendDi; interface ServiceLocation { public function set($name, $service); public function get($name, array $params = null); }
  • 139.
    Interface para Injetorde 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 { publicfunction __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(); }
  • 141.
    Referências namespace ZendDi; interface DependencyReference { publicfunction __construct($serviceName); public function getServiceName(); }
  • 142.
    Definição de Classe useZendDiDefinition, 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 useZendDiDefinition, 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 Localizadorde 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 umlocalizador 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 localizadorgerado $context = new AppContext(); $request = $context->get('request'); // O mesmo que usar um localizador de serviços ou um conteiner DI!
  • 149.
    Fazendo mais simplesainda  Use arquivos de configuração  Você pode usar qualquer formato suportado por ZendConfig:  INI  JSON  XML  YAML
  • 150.
    Exemplo de configuraçãocom 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" } } }
  • 151.
    Quais são oscasos de uso no ZF2?  Um grandão. Tirar os controladores MVC do conteiner
  • 152.
    Um Controlador deAçã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 classFrontController 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 usarDI deste modo  Performance  Desacoplamento de código  Simplificação do código do controlador
  • 155.
    E vem maispor 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.
  • 156.
  • 157.
  • 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ásicade uma aplicação web é a de um ciclo de vida Requisição/Resposta
  • 161.
    A interface Dispatchable namespaceZendStdlib; 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 simplesde 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!