Upload
flavio-lisboa
View
1.112
Download
1
Embed Size (px)
Citation preview
O que esperar do Zend Framework 3
Flávio Gomes da Silva Lisboawww.fgsl.eti.br
Quem sou eu?
● Bacharel em Ciência da Computação com pós-graduação em Aplicações Corporativas usando Orientação a Objetos e Tecnologia Java pela Universidade Tecnológica Federal do Paraná. Programador formado pelo Centro Estadual de Educação Tecnológica Paula Souza.
● Chefe do setor de adequação da solução e mobilidade do projeto Expresso 3 na Coordenação Estratégica de Ações Governamentais do Serviço Federal de Processamento de Dados (Serpro).
● Zend PHP Certified Engineer, Zend Framework Certified Engineer e Zend Framework 2 Certified Architect.
Autor de
20132012201020092008
http://www.novatec.com.br/autores/flaviogomes/
Saindo do forno...
E em breve...
Zend Framework Componentes Poderosos para PHP 3ª edição
Criando Aplicações PHP com Zend e ExtJS
Autor também de
http://www.perse.com.br
Escreve no bloghttp://romocavaleirodoespaco.blogspot.com.br/
Sumário
● Componentes● HTTP, PSR-7 e Middleware● PHP 7 e PHP 5.5
ZF3
● Ênfase em componentes
ZF3
● Ênfase em componentes● Foco em HTTP, via PSR-7 e middleware
ZF3
● Ênfase em componentes● Foco em HTTP, via PSR-7 e middleware● Otimizado para PHP 7, mas suporta PHP 5.5+
ZF3
● Ênfase em componentes● Foco em HTTP, via PSR-7 e middleware● Otimizado para PHP 7, mas suporta PHP 5.5+● Lançamento previsto para...
Componentes
ZF1: o passado
● Componentes eram desenvolvidos dentro do repositório do framework, e
ZF1: o passado
● Componentes eram desenvolvidos dentro do repositório do framework, e
● Eram instaláveis somente com o framework inteiro.
ZF2: o presente
● Componentes são desenvolvidos dentro do repositório do framework, e
● Podem ser instalados individualmente (Composer, GIT).
ZF 2.5: o presente
● Não há mais pacotes de instalação.● O repositório ZF depende dos componentes.
Instale só o que vai usar
{
"require": {
"zendframework/zend-authentication": "^2.5",
"zendframework/zend-cache": "^2.5",
"zendframework/zend-captcha": "^2.5",
"etc": "*"
}
}
Por que?
Fácil manutenção
● Permite mais times com direitos de alterar código de componentes
● Permite um destino determinístico de repositórios
Esqueletos específicos para casos de uso
● Entrega somente serviços?● Precisa de desempenho?● Precisa de tudo?
Haverá um esqueleto para isso!
HTTP, PSR-7 e Middleware
HTTP é o fundamento da Web
● Um cliente envia uma requisição● Um servidor devolve uma resposta
Mensagens HTTP
GET /path HTTP/1.1Host: example.comAccept: application/json
HTTP/1.1 200 OKContent-Type: application/json{"foo":"bar"}
Requisição
Resposta
Frameworks modelam mensagens
Mas cada framework faz isso diferente
$method = $request->getMethod();$method = $request->getRequestMethod();$method = $request->method;
Requisição
$method = $request->getMethod();$accept = $request->getHeader('Accept');$path = $request->getUri()->getPath();$controller = $request->getAttribute('controller');
Resposta
$response->getBody()->write('Hello world!');$response = $response ->withStatus(200, 'OK') ->withHeader('Content-Type', 'text/plain');
Middleware
Entre a requisição e a resposta
function (Request $request, Response $response){ // do some work return $response; // same, or a new instance.}
Uma arquitetura de terceirização
Isso parece...
Tipos de Middleware
Pilhas ou Condutores
$pipeline->pipe($middleware1); // always evaluate$pipeline->pipe('/path', $middleware2); // only if path matches$pipeline->pipe('/different/path', $middleware3);$response = $pipeline->run();
Estilo Cebolaclass Outer{ public $inner; public function __invoke($request, $response) { // do some work $response = ($this->inner)($request, $response); // do some work return $response; }}$response = $outer($request, $response);
Estilo Lambda
function (Request $request){ // do some work return $response;}$response = $middleware($request);
Consumindo Middleware
ZF permitirá despachar o seguinte middleware:
… e controladores ZF serão middleware.
/** * @return Response */public function (Request $request, Response $response);
Encapsulador de Middleware para ZF
$middleware = new MvcMiddlewareWrapper( require 'config/application/config.php');class MvcMiddlewareWrapper{ public function __invoke($request, $response) { $app = Application::init($this->config); return $app->dispatch($request, $response); }}
Middleware como alternativa para tempo de execução
● Desempenho● Experiência do desenvolvedor● Reusabilidade entre frameworks
Exemplo$app = new Middleware();$app->pipe('/', $homepage); // Static!$app->pipe('/customer', $zf2Middleware); // ZF2!$app->pipe('/products', $zf1Middleware); // ZF1!$app->pipe('/api', $apigility); // Apigility!$app->pipe('/user', $userMiddleware); // 3rd party!$server->listen($app);
PSR-7 e Middleware provêem...
● Caminhos para otimização de desempenho● Interfaces para web mais simples● Maior interoperabilidade e potencial reuso
PHP 7 e PHP 5.5
Atualização para o PHP 5.5
● Conseguimos usar traits!● Conseguimos usar a sintaxe curta de array!● Conseguimos usar callable type hint!● Conseguimos usar finally!● Podemos usar a constante mágica ::class!● Conseguimos usar generators!● Conseguimos um PHP mais rápido, mais seguro!
PHP 7
Impressionante melhoria de desempenho!
Novo gerenciamento de estruturas de dados no motor do PHP
Novas funcionalidades como declarações de tipo para argumento e retorno
PHP 7 : Benchmark
PHP 5.6 PHP 7
Uso de memória 428 MB 33 MB
Tempo de execução 0.49 sec 0.06 sec
$a = array();for ($i = 0; $i < 1000000; $i++) { $a[$i] = array("hello");}echo memory_get_usage(true);
Bench.php
Wordpress
Frameworks
ZF3: Otimizado para PHP 7
Atualizar PHP provê melhor segurança, melhora o desempenho e permite melhorar o framework
Certo, mas e onde está esse ZF3?
Não existe Zend Framework 3
Mas não tema!
Zend Expressive
Expressive permite que você escreva aplicações middleware PSR-7 para a web. É um simples micro-framework construído no topo do Stratigility, fornecendo:
● Roteamento dinâmico;● Injeção de dependência via interoperabilidade de
containers;● Templating;● Manipulação de erros.
Hello World(para index.php no web root)
// In index.php
use Zend\Expressive\AppFactory;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\RequestInterface;
require 'vendor/autoload.php';
$app = AppFactory::create();
$app->route('/', function (RequestInterface $request,
ResponseInterface $response, $next) {
$response->getBody()->write('Hello, world!');
return $response;
});
$app->run();
php -S 0.0.0.0:8080 -t
Instalação com Composer via composer.json
{
"require": {
"zendframework/zend-expressive" : "*",
"zendframework/zend-servicemanager" : "*",
"zendframework/zend-expressive-fastroute" : "*"
}
}
Se a rota não casar...
Cannot GET http://[URL DO PROJETO]]
Instalação com Composer via terminal
composer require zendframework/zend-expressive aura/router zendframework/zend-servicemanager
Anatomia
Comparação com ZF2
Próximas funcionalidades
Um esqueleto de aplicação;
Criptografia de sessão;
Suporte a cache de HTTP;
Autenticação de usuário (via OAuth2 e/ou outros mecanismos de autenticação).
Criando uma galeria de fotos com Zend\Expressive
Criando um novo projeto
composer create-project -s rc zendframework/zend-expressive-skeleton <project-directory>
Removendo o código desnecessário
rm public/favicon.ico
rm public/zf-logo.png
rm src/Action/*
rm test/Action/*
rm templates/app/*
rm templates/layout/*
Configuração do Container
<?php
return [
'dependencies' => [
'factories' => [
Zend\Expressive\Application::class => Zend\Expressive\Container\ApplicationFactory::class,
],
],
];
config/autoload/dependencies.global.php
Configuração das Rotas
<?php
return [
'dependencies' => [
'invokables' => [
Zend\Expressive\Router\RouterInterface::class => Zend\Expressive\Router\FastRouteRouter::class,
],
'factories' => [
App\Action\IndexAction::class => App\Action\IndexFactory::class,
]
],
'routes' => [
[
'name' => 'index',
'path' => '/',
'middleware' => App\Action\IndexAction::class,
'allowed_methods' => ['GET'],
],
],
];
config/autoload/routes.global.php
Controlador
● <?php●
● namespace App\Action;●
● use Psr\Http\Message\ServerRequestInterface;● use Psr\Http\Message\ResponseInterface;● use Zend\Expressive\Template\TemplateRendererInterface;● use Zend\Stratigility\MiddlewareInterface;●
● class IndexAction implements MiddlewareInterface● {● private $templateRenderer;●
● public function __construct(TemplateRendererInterface $templateRenderer)● {● $this->templateRenderer = $templateRenderer;● }●
● public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)● {● $html = $this->templateRenderer->render('app::index');● $response->getBody()->write($html);● return $response->withHeader('Content-Type', 'text/html');● }● }●
src/Action/IndexAction.php
Fábrica
<?php
namespace App\Action;
use Interop\Container\ContainerInterface;
use Zend\Expressive\Template\TemplateRendererInterface;
class IndexFactory
{
public function __invoke(ContainerInterface $container)
{
$templateRenderer = $container->get(TemplateRendererInterface::class);
return new IndexAction($templateRenderer);
}
}
src/Action/IndexFactory.php
Templating
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title><?=$this->e($title);?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>
templates/layout/default.phtml
Caching
composer require doctrine/cache ^1.5
mkdir data/doctrine-cache
Caching
<?php
return [
'dependencies' => [
'factories' => [
// ...
Doctrine\Common\Cache\Cache::class => App\DoctrineCacheFactory::class,
],
],
'application' => [
'cache_path' => 'data/doctrine-cache/',
],
];
config/autoload/dependencies.global.php
Caching
<?php
return [
'dependencies' => [
'factories' => [
App\Middleware\CacheMiddleware::class => App\Middleware\CacheFactory::class,
]
],
'middleware_pipeline' => [
'pre_routing' => [
[ 'middleware' => App\Middleware\CacheMiddleware::class ],
],
'post_routing' => [
],
],
];
config/autoload/middleware-pipeline.global.php
Caching
<?php
namespace App;
use Doctrine\Common\Cache\FilesystemCache;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
class DoctrineCacheFactory
{
public function __invoke(ContainerInterface $container)
{
$config = $container->get('config');
if (!isset($config['application']['cache_path'])) {
throw new ServiceNotCreatedException('cache_path must be set in application configuration');
}
return new FilesystemCache($config['application']['cache_path']);
}
}
src/DoctrineCacheFactory.php
Caching
<?php
namespace App\Middleware;
use Doctrine\Common\Cache\Cache;
use Interop\Container\ContainerInterface;
class CacheFactory
{
public function __invoke(ContainerInterface $container)
{
$cache = $container->get(Cache::class);
return new CacheMiddleware($cache);
}
}
src/Middleware/CacheFactory.php
Caching
<?php
namespace App\Middleware;
use Doctrine\Common\Cache\Cache;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Stratigility\MiddlewareInterface;
class CacheMiddleware implements MiddlewareInterface
{
private $cache;
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
src/Middleware/CacheMiddleware.php
Caching
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
{
$cachedResponse = $this->getCachedResponse($request, $response);
if (null !== $cachedResponse) {
return $cachedResponse;
}
$response = $next($request, $response);
$this->cacheResponse($request, $response);
return $response;
}
src/Middleware/CacheMiddleware.php (cont.)
Caching
private function getCacheKey(ServerRequestInterface $request)
{
return 'http-cache:'.$request->getUri()->getPath();
}
src/Middleware/CacheMiddleware.php (cont.)
Caching
private function getCachedResponse(ServerRequestInterface $request, ResponseInterface $response)
{
if ('GET' !== $request->getMethod()) {
return null;
}
$item = $this->cache->fetch($this->getCacheKey($request));
if (false === $item) {
return null;
}
$response->getBody()->write($item['body']);
foreach ($item['headers'] as $name => $value) {
$response = $response->withHeader($name, $value);
}
return $response;
}
src/Middleware/CacheMiddleware.php (cont.)
Caching
private function cacheResponse(ServerRequestInterface $request, ResponseInterface $response)
{
if ('GET' !== $request->getMethod() || !$response->hasHeader('Cache-Control')) {
return;
}
$cacheControl = $response->getHeader('Cache-Control');
$abortTokens = array('private', 'no-cache', 'no-store');
if (count(array_intersect($abortTokens, $cacheControl)) > 0) {
return;
}
src/Middleware/CacheMiddleware.php (cont.)
Caching
foreach ($cacheControl as $value) {
$parts = explode('=', $value);
if (count($parts) == 2 && 'max-age' === $parts[0]) {
$this->cache->save($this->getCacheKey($request), [
'body' => (string) $response->getBody(),
'headers' => $response->getHeaders(),
], intval($parts[1]));
return;
}
}
}
}
src/Middleware/CacheMiddleware.php (cont.)
Caching
rm -rf data/doctrine-cache/*
Limpeza de cache
Instalando um provedor de fotos
composer require andrewcarteruk/astronomy-picture-of-the-day ^0.1
NASA Astronomy Picture of the Day - API Wrapper
Instalando um provedor de fotos
<?php
return [
'dependencies' => [
'factories' => [
// ...
AndrewCarterUK\APOD\APIInterface::class => App\APIFactory::class,
],
],
'application' => [
// ...
'results_per_page' => 24,
'apod_api' => [
'store_path' => 'public/apod',
'base_url' => '/apod',
],
],
];
config/autoload/dependencies.global.php
Instalando um provedor de fotos
<?php
return [
'application' => [
'apod_api' => [
'api_key' => 'DEMO_KEY',
// DEMO_KEY might be good for a couple of requests
// Get your own here: https://api.nasa.gov/index.html#live_example
],
],
];
config/autoload/dependencies.local.php
Instalando um provedor de fotos
<?php
return [
'dependencies' => [
// ...
'factories' => [
// ...
App\Action\PictureListAction::class => App\Action\PictureListFactory::class,
],
],
'routes' => [
// ...
[
'name' => 'picture-list',
'path' => '/picture-list[/{page:\d+}]',
'middleware' => App\Action\PictureListAction::class,
'allowed_methods' => ['GET'],
],
],
];
config/autoload/routes.global.php
Instalando um provedor de fotos
mkdir public/apod
Diretório de thumbnails
Instalando um provedor de fotos
<?php
namespace App;
use AndrewCarterUK\APOD\API;
use GuzzleHttp\Client;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
class APIFactory
{
public function __invoke(ContainerInterface $container)
{
$config = $container->get('config');
if (!isset($config['application']['apod_api'])) {
throw new ServiceNotCreatedException('apod_api must be set in application configuration');
}
return new API(new Client, $config['application']['apod_api']);
}
}
src/APIFactory.php
Instalando um provedor de fotos
<?php
namespace App\Action;
use AndrewCarterUK\APOD\APIInterface;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
class PictureListFactory
{
public function __invoke(ContainerInterface $container)
{
$apodApi = $container->get(APIInterface::class);
$config = $container->get('config');
if (!isset($config['application']['results_per_page'])) {
throw new ServiceNotCreatedException('results_per_page must be set in application configuration');
}
return new PictureListAction($apodApi, $config['application']['results_per_page']);
}
}
src/Action/PictureListFactory.php
Instalando um provedor de fotos
<?php
namespace App\Action;
use AndrewCarterUK\APOD\APIInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Stratigility\MiddlewareInterface;
class PictureListAction implements MiddlewareInterface
{
private $apodApi;
private $resultsPerPage;
public function __construct(APIInterface $apodApi, $resultsPerPage)
{
$this->apodApi = $apodApi;
$this->resultsPerPage = $resultsPerPage;
}
src/Action/PictureListAction.php
Instalando um provedor de fotos
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $out = null)
{
$page = intval($request->getAttribute('page')) ?: 0;
$pictures = $this->apodApi->getPage($page, $this->resultsPerPage);
$response->getBody()->write(json_encode($pictures));
return $response
// ->withHeader('Cache-Control', ['public', 'max-age=3600'])
->withHeader('Content-Type', 'application/json');
}
}
src/Action/PictureListAction.php (cont.)
Instalando um provedor de fotos
<?php
chdir(__DIR__.'/..');
include 'vendor/autoload.php';
$container = include 'config/container.php';
// Create a SIGINT handler that sets a shutdown flag
$shutdown = false;
declare(ticks = 1);
pcntl_signal(SIGINT, function () use (&$shutdown) {
$shutdown = true;
});
bin/update.php
Instalando um provedor de fotos
$newPictureHandler = function (array $picture) use (&$shutdown) {
echo 'Added: ' . $picture['title'] . PHP_EOL;
// If the shutdown flag has been set, die
if ($shutdown) {
die;
}
};
$errorHandler = function (Exception $exception) use (&$shutdown) {
echo (string) $exception . PHP_EOL;
// If the shutdown flag has been set, die
if ($shutdown) {
die;
}
};
$container->get(AndrewCarterUK\APOD\APIInterface::class)->updateStore(20, $newPictureHandler, $errorHandler);
bin/update.php (cont.)
API Key NASA
● https://api.nasa.gov/index.html#live_example
php bin/update.php
Onde estará o ZF3?
Referência
● Zimuel, E. Pushing Boundaries: Zend Framework 3. Disponível em <http://zimuel.it/slides/phpday2015>
● Ikhsan, A. M. e O'Phinney, M. W. How to Build a NASA Photo Gallery with Zend Expressive. Disponível em <http://www.sitepoint.com/build-nasa-photo-gallery-zend-expressive/>