01-08-2017 12:01:00

Dans une précédente vidéo nous avons vu comment créer et utiliser les middlewares en PHP. Aujourd'hui je vous propose de découvrir un framework intéressant qui se base sur ce système pour fonctionner : Zend Expressive.

Un micro framework sans opinions

Zend expressive rentre dans la catégorie des micro-frameworks en offrant simplement une base de travail plutôt qu'un framework complet à la Symfony ou Laravel. Il offre par défaut :

  • Un conteneur d'injection de dépendance (basé sur l'interface du PSR)
  • Un router (qui doit implémenter une interface propre à zend Zend\Expressive\Router\RouterInterface)
  • Un dispatcher pour enchainer (pipe) des middlewares.

Mais la particularité de Zend Expressive par rapport à d'autres micro frameworks, c'est qu'il n'a pas d'opinion sur l'implémentation à utiliser pour ces différents blocs. Il est par exemple possible d'utiliser n'importe quel container d'injection de dépendance tant qu'il implémente l'interface du PSR. De la même façon pour la partie Router, Zend Expressive vous laisse le choix sur le type de router à utiliser et vous propose quelques implémentations (comme par exemple FastRoute ou AuraRouter).

Enfin, si vous souhaitez utiliser un moteur de template, Zend Expressive dispose d'une interface TemplateRendererInterface et de quelques implémentations, dont notamment Twig.

Démarrer un projet Zend Expressive

Pour démarrer un nouveau projet, 2 solutions s'offrent à vous. Vous pouvez utiliser la commande composer pour créer un nouveau squelette de projet

composer create-project zendframework/zend-expressive-skeleton expressive

Cette commande vous posera une série de question afin d'adapter les blocs à utiliser (structure, router, container...) mais dispose d'une structure de configuration qui n'est pas forcément évidente.

Vous pouvez aussi créer votre propre structure en vous basant sur les classes offertes par zend expressive.

<?php
require '../vendor/autoload.php';

// On charge un container (il doit implémenter l'interface du PSR)
$builder = new \DI\ContainerBuilder();
$builder->addDefinitions('../config.php');
$container = $builder->build();

// On lance l'application en lui passant un Router, et un container
$app = new \Zend\Expressive\Application(
  $container->get(\Zend\Expressive\Router\RouterInterface::class), 
  $container
);

// On crée nos routes
// $app->get(url, middleware, nom de la route)
$app->get('/blog', \App\BlogAction::class, 'blog');
$app->get('/blog/{slug}-{id}', \App\BlogAction::class, 'blog.show');

// On ajoute des middlewares
$app->pipe(\Middlewares\Whoops::class);
$app->pipe('/api/V1', \App\API::class);
$app->pipeRoutingMiddleware(); // Détecte la route correspondant à l'url
$app->pipeDispatchMiddleware(); // Dispatch l'action correspondant à la route

// On fait fonctionner notre application
$app->run();

Tout middleware

L'autre particularité de Zend Expressive est que l'ensemble de son fonctionnement est basé sur des middlewares. Même les actions spécifiées dans le router seront des middlewares.

<?php
namespace App;

use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Expressive\Template\TemplateRendererInterface;

class BlogAction implements MiddlewareInterface {

    private $renderer;

    public function __construct(TemplateRendererInterface $renderer)
    {
        $this->renderer = $renderer;
    }

    public function process(ServerRequestInterface $request, DelegateInterface $delegate)
    {
        if ($request->getAttribute('id')) {

            return $this->show($request->getAttribute('id'), $request);
        } else {
            return $this->index($request);
        }
    }

    private function index(ServerRequestInterface $request)
    {
        return new HtmlResponse('Salut je suis sur le blog');
    }

    private function show(int $id, ServerRequestInterface $request): ResponseInterface
    {
        return new HtmlResponse($this->renderer->render('@app/show'));
    }

}

C'est d'ailleurs ce système qui est utilisé en interne par Zend pour la détection des routes :

  • pipeRoutingMiddleware(), va interpréter l'URL et rajouter un attribut RouteResult sur l'objet Request.
  • Le pipeDispatchMiddleware(), récupère l'attribut RouteResult et utilise la propriété action afin d'obtenir le middleware a éxécuter.