Beruflich Dokumente
Kultur Dokumente
MEDO!
Advertência
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
View content
1.6.0 – Setembro de 2008
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!
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
Não é isto:
class SoraniknatuController
extends Zend_Controller_Action
{
public function useTheRingAction()
{
$this->view->object = 'imagination';
}
}
Zend_Form_Decorator_Marker_File_Interface
A solução
namespace Zend\EventManager;
use Zend\Stdlib\CallbackHandler;
namespace Zend\Mvc;
use Zend\Stdlib\Dispatchable,
Zend\Di\ServiceLocator as Locator;
Como ficará:
use Zend\Controller\Action as Controller;
namespace Zend\EventManager;
}
Interfaces
Interfaces
namespace Zend\Session;
}
Implementação Concreta
namespace Zend\Session\Storage;
use ArrayObject,
Zend\Session\Storage,
Zend\Session\Exception;
interface Exception
{
namespace Zend\EventManager\Exception;
use Zend\EventManager\Exception;
}
Capturando Exceções
namespace Zend\EventManager\Exception;
use Zend\EventManager\Exception;
try {
$events->trigger('dom.quixote', $object);
} catch (InvalidArgumentException $e) {
} catch (Exception $e) {
} catch (\InvalidArgumentException $e) {
} catch (\Exception $e) {
}
Autocarregamento
O problema
Problemas de performance
Muitas classes são usadas apenas no momento
adequado e não devem ser carregadas até que
seja necessário.
A falta de chamadas require_once leva a erros.
Abordagem ZF2
require_once 'Zend/Loader/StandardAutoloader.php';
$loader = new Zend\Loader\StandardAutoloader(array(
'fallback_autoloader' => true,
));
$loader->register();
EX
EM
PL
O
Autocarregamento
Namespace/Prefixo ZF2
require_once 'Zend/Loader/StandardAutoloader.php';
$loader = new Zend\Loader\StandardAutoloader();
$loader->registerNamespace('My', __DIR__ . '/../library/My')
->registerPrefix('Fgsl_', __DIR__ . '/../library/Fgsl');
$loader->register();
EX
EM
PL
O
Autocarregamento com Mapas de
Classes
return array(
'Green\Lantern\Hal' => __DIR__ . '/Lantern/Hal.php',
);
require_once 'Zend/Loader/ClassMapAutoloader.php';
$loader = new Zend\Loader\ClassMapAutoloader();
$loader->registerAutoloadMap(__DIR__ .
'/../library/.classmap.php');
$loader->register();
Mapas de Classes? Mas não dá
trabalho pra fazer?
Sim, dá trabalho. Mas nós temos uma
ferramenta, bin/classmap_generator.php
E o uso é trivial:
prompt>
cd your/library
prompt> php /path/to/classmap_generator.php -w
namespace Zend\Loader;
interface ShortNameLocator
{
public function isLoaded($name);
public function getClassName($name);
public function load($name);
}
Interface de Agente de Plugins
namespace Zend\Loader;
interface Broker
{
public function load($plugin, array $options = null);
public function getPlugins();
public function isLoaded($name);
public function register($name, $plugin);
public function unregister($name);
public function setClassLoader(ShortNameLocator $loader);
public function getClassLoader();
}
Como usar?
use Zend\View\HelperLoader;
HelperLoader::addStaticMap(array(
'url'
=> 'Kilowog\Helper\Url',
'base_url' => 'Project\Helper\BaseUrl',
));
$loader = new HelperLoader();
$class = $loader->load('url'); // "Kilowog\Helper\Url"
Passando Mapas via
Configuração
use Zend\View\HelperLoader;
$config = array(
'url'
=> 'Kilowog\Helper\Url',
'base_url' => 'Project\Helper\BaseUrl',
);
$loader = new HelperLoader($config);
$class = $loader->load('url'); // "Kilowog\Helper\Url"
Passando Mapas para Mapas!
use Zend\View\HelperLoader,
Zend\Loader\PluginClassLoader;
class HelperMap extends PluginClassLoader
{
protected $plugins = array(
'url'
=> 'Kilowog\Helper\Url',
'base_url' => 'Project\Helper\BaseUrl',
);
}
$helpers = new HelperMap();
$loader = new HelperLoader($helpers);
$class
= $loader->load('url'); // "Kilowog\Helper\Url"
Estendendo Carregadores
use Zend\View\HelperLoader;
class HelperMap extends HelperLoader
{
public function __construct($options = null)
{
// Adiciona e/ou sobrescreve o mapa do
HelperLoader
$this->registerPlugins(array(
'url'
=> 'Kilowog\Helper\Url',
'base_url' => 'Project\Helper\BaseUrl',
));
parent::__construct($options);
}
}
$helpers = new HelperMap();
$class
= $loader->load('url'); // "Kilowog\Helper\Url"
Passando Mapas via Agente
use Zend\View\HelperBroker;
$broker = new HelperBroker(array(
'class_loader' => array(
'class'
=> 'HelperMap',
'options' => array(
'base_url' => 'App\Helper\BaseUrl',
),
),
));
$plugin = $broker->load('base_url'); // "App\Helper\BaseUrl"
Criando Mapas Manualmente
use Zend\View\HelperLoader;
$loader = new HelperLoader();
$loader->registerPlugin('url', 'Kilowog\Helper\Url')
->registerPlugins(array(
'base_url' => 'Project\Helper\BaseUrl',
));
$class = $loader->load('url'); // "Kilowog\Helper\Url"
Gerenciando Plugins via Agente
use Kilowog\Helper\Url;
// Assume:
// - $request == objeto Request
// - $router == objeto Router
// - $broker == HelperBroker
namespace Zend\Loader;
interface LazyLoadingBroker extends Broker
{
public function registerSpec($name, array $spec = null);
public function registerSpecs($specs);
public function unregisterSpec($name);
public function getRegisteredPlugins();
public function hasPlugin($name);
}
Usando LazyLoadingBroker
use Zend\View\HelperBroker;
$config = array(
'specs' => array(
'url' => array($request, $router),
),
);
$broker = new HelperBroker($config);
$url
= $broker->load('url'); // Com $request, $router é
injetado
E ainda tem mais!
Novos Componentes
E Componentes Poderosos!
Novos Componentes
Zend\EventManager
Zend\Di
EventManager
O Problema
www.fgsl.eti.br
Palestras
Requisitos
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
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
namespace Zend\EventManager;
use Zend\Stdlib\CallbackHandler;
interface EventCollection
{
public function trigger($event, $context, $argv = array());
public function triggerUntil($event, $context, $argv, $callback);
public function attach($event, $callback, $priority = 1);
public function detach(CallbackHandler $handle);
public function getEvents();
public function getHandlers($event);
public function clearHandlers($event);
}
Disparando Eventos
use Zend\EventManager\EventManager;
$events = new EventManager();
$events->trigger($eventName, $object, $params);
/* Onde:
* - $eventName é o nome do evento; geralmente o nome do evento
atual
*
* - $object é o objeto que está disparando o evento
* - $params são os parâmetros que o manipulador pode precisar
para ter acesso,
geralmente os argumentos do método
*
*/
CallbackHandler
$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 Zend\EventManager\EventCollection as Events,
Zend\EventManager\EventManager;
class Arisia
{
protected $events;
public function events(Events $events = null)
{
if (null !== $events) {
$this->events = $events;
} elseif (null === $this->events) {
$this->events = new EventManager(__CLASS__);
}
return $this->events;
}
public function doSomething($param1, $param2)
{
$params = compact('param1', 'param2');
$this->events()->trigger(__FUNCTION__, $this, $params);
}
}
Usando um Trait!
use Zend\EventManager\EventCollection as Events,
Zend\EventManager\EventManager;
trait Eventful
{
public function events(Events $events = null)
{
if (null !== $events) {
$this->events = $events;
} elseif (null === $this->events) {
$this->events = new EventManager(__CLASS__);
}
return $this->events;
}
}
class Arisia
{
use Eventful;
protected $events;
}
Conectando Manipuladores
Estaticamente
use Zend\EventManager\StaticEventManager;
$events = StaticEventManager::getInstance();
$events->connect('Arisia', 'algum-evento', function
($e) {
/* ... */
});
Recomendações
namespace Tomarre\Helper;
class Url
{
public function __construct(Request $request)
{
$this->request = $request;
}
public function setRouter(Router $router)
{
$this->router = $router;
}
}
Então porque as pessoas tem
medo disso?
Porque elas não fazem isso.
Elas temem os Conteineres de Injeção de
Dependência.
O Que é um Conteiner de Injeção
de Dependência
Colocando de forma
simples:
Um grafo de objetos para
mapear relações de
dependência entre
objetos.
Grafos
Novamente, por que as pessoas
tem medo disso?
namespace Tomarre\Helper;
class Url
{
public function __construct(Request $request)
{
$this->request = $request;
}
public function setRouter(Router $router)
{
$this->router = $router;
}
}
Outro Objeto com Dependências
namespace mwop\Mvc;
class Router
{
public function addRoute(Route $route)
{
$this->routes->push($route);
}
}
Agarrando um Objeto e Usando-o
$urlHelper = $di->get('url-helper');
echo $url->generate('/css/site.css');
echo $url->generate(array('id' => $id), array('name' =>
'blog'));
As Questões
namespace Zend\Di;
interface ServiceLocation
{
public function set($name, $service);
public function get($name, array $params = null);
}
Interface para Injetor de
Dependências
namespace Zend\Di;
interface DependencyInjection
{
public function get($name, array $params = null);
public function newInstance($name, array $params = null);
public function setDefinitions($definitions);
public function setDefinition(
DependencyDefinition $definition, $serviceName = null);
public function setAlias($alias, $serviceName);
public function getDefinitions();
public function getAliases();
}
Definições
namespace Zend\Di;
interface DependencyDefinition
{
public function __construct($className);
public function getClass();
public function setConstructorCallback($callback);
public function getConstructorCallback();
public function hasConstructorCallback();
public function setParam($name, $value);
public function setParams(array $params);
public function setParamMap(array $map);
public function getParams();
public function setShared($flag = true);
public function isShared();
public function addTag($tag);
public function addTags(array $tags);
public function getTags();
public function hasTag($tag);
public function addMethodCall($name, array $args);
public function getMethodCalls();
}
Referências
namespace Zend\Di;
interface DependencyReference
{
public function __construct($serviceName);
public function getServiceName();
}
Definição de Classe
use Zend\Di\Definition,
Zend\Di\Reference;
$mongo = new Definition('Mongo');
$mongoDB = new Definition('MongoDB');
$mongoDB->setParam('conn', new Reference('mongo'))
->setParam('name', 'test');
$coll = new Definition('MongoCollection');
$coll->setParam('db', new Reference('mongodb'))
->setParam('name', 'resource');
$di->setDefinitions(array(
'mongo'=> $mongo,
'mongodb' => $mongoDB,
'resource' => $coll,
));
$resource = $di->get('resource');
Injeção por Modificador
use Zend\Di\Definition,
Zend\Di\Reference;
$service = new Definition('mwop\Service\Resources');
$service->addMethod('setResource', array(
new Reference('resource')
));
$di->setDefinition('resources', $service);
$resources = $di->get('resources');
Fazendo mais Rápido
Tirar os controladores
MVC do conteiner
Um Controlador de Ação
namespace Blog\Controller;
class Entry implements Dispatchable
{
public function setResource(Resource $resource)
{
$this->resource = $resource;
}
public function dispatch(Request $request, Response $response =
null)
{
/* ... */
$entry = $this->resource->get($id);
/* ... */
}
}
O Controlador Frontal
class FrontController implements Dispatchable
{
public function __construct(DependencyInjection $di)
{
$this->di = $di;
}
public function dispatch(Request $request, Response $response =
null)
{
/* ... */
$controller = $this->di->get($controllerName);
$result = $controller->dispatch($request, $response);
/* ... */
}
}
Benefícios de usar DI deste modo
Performance
Desacoplamento de código
Simplificação do código do controlador
E vem mais por aí
namespace Zend\Stdlib;
interface Dispatchable
{
public function dispatch(
Request $request, Response $response = null
);
}
Requisição e Resposta
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
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!