Le PHP Framework Interoperability Group est un groupe de développeurs qui ont publié des préconisations concernant le codage PHP. Ces préconisations n’ont rien d’officiel ou d’obligatoire mais peu à peu elles investissent le champ du développement PHP. On y gagne en cohérence et ont peut ainsi partager facilement du code. Laravel respecte ces préconisations.

Il existe actuellement 6 préconisations :

Les messages HTTP sont à la base du développement web et cette préconisation PSR-7 est donc importante pour la standardisation et l’interopérabilité. Elle est assez récente puiqu’elle date du 15 mai 2015 et elle ne s’est pas encore généralisée. Laravel utilise symfony pour la gestion des messages HTTP et celui-ci ne s’est pas encore converti à la préconisation, mais il propose un outil de conversion. Il doit donc être possible d’utiliser des packages à la sauce PSR-7 avec Laravel, mais je n’ai pas essayé…

Le but de la préconisation PSR-7 est d’offrir une abstraction des messages côté client et côté serveur pour rendre les projets dans une certaine mesure compatibles. On va donc y trouver des interfaces et des exemples pratiques. D’ailleurs il n’est pas demandé à tous les packages existants de modifier leur code pour adopter la préconisation mais plutôt de prévoir une passerelle pour pouvoir communiquer avec les autres packages. En ce sens symfony est tout à fait dans la ligne de ce qui est demandé.

PSR-7 Http Message

Un dépôt Github contient les interfaces relatifs à la préconisation. On en trouve 7 :

img12

MessageInterface

C’est l’interface de base qui présente des accesseurs pour les éléments fondamentaux des requêtes :

  • la version du protocole HTTP (1.0, 1.1)
  • les entêtes (headers)
  • le corps (body)

RequestInterface

Cette interface étend la précédente et concerne les messages sortants côté client.

ResponseInterface

Cette interface étend aussi MessageInterface et concerne les messages sortants côté serveur en tant que réponses.

ServerRequestInterface

Cette interface étend RequestInterface et concerne les messages entrants côté serveur.

StreamInterface

Cette interface propose de wrapper un flux (stream) PHP. Il est à noter que la méthode getBody de l’interface MessageInterface doit retourner un flux, donc une classe qui instancie StreamInterface, ce qui permet de ne pas être limité au niveau de la nature et de la dimension du corps du message.

UploadedFileInterface

Cette interface concerne l’envoi de fichier. Il est à remarquer qu’on doit avoir un Valu Object. Si vous ne savez pas précisément de quoi il s’agit je vous conseille ce petit diaporama.

UriInterface

Pour terminer cette interface concerne les URI qui doivent aussi avoir une réprésentation en Value Object.

Une implémentation

L’abstraction c’est bien mais l’application c’est encore mieux ! Pour voir tout ça en oeuvre je vous propose d’aller voir le projet zend-diactoros.

Ce projet respecte complètement le PSR-7 et évidemment il s’installe avec Composer :

composer require zendframework/zend-diactoros

Dans ses dépendances on trouve naturellement psr/http-message :

"require": {
  "php": "^5.4 || ^7.0",
  "psr/http-message": "~1.0"
},

Le package permet de gérer des requêtes côté client ou côté serveur, on va s’intéresser à ces dernières. On a donc des requêtes entrantes et sortantes. A partir d’une requête entrante on doit pouvoir récupérer toutes les informations, préparer une réponse et l’envoyer.

Les requêtes entrantes

On trouve dans le package une classe ServerRequestFactory qui a pour objet de gérer les requêtes entrantes. En particulier la méthode fromGlobals crée un objet ServerRequest à partir des données des supers globales :

$request = Zend\Diactoros\ServerRequestFactory::fromGlobals(
    $_SERVER,
    $_GET,
    $_POST,
    $_COOKIE,
    $_FILES
);

Tous les paramètres sont optionnels :

public static function fromGlobals(
    array $server = null,
    array $query = null,
    array $body = null,
    array $cookies = null,
    array $files = null
) {

Evidemment la classe ServerRequest implémente ServerRequestInterface :

class ServerRequest implements ServerRequestInterface
{
    use MessageTrait, RequestTrait;

Il est utilisé deux traits.

On se retrouve avec toutes les méthodes disponibles, par exemple pour connaître le verbe :

$request->getMethod()

Ou les paramètres du serveur :

$request->getServerParams()

Les réponses

Pour les réponses on a une classe Response qui implémente ResponseInterface :

class Response implements ResponseInterface
{
    use MessageTrait;

On voit également qu’un trait est utilisé qu’on a déjà vu pour la requête entrante.

Donc pour créer une réponse on instancie cette classe :

$response = new Zend\Diactoros\Response();

Pour renseigner le body c’est alors tout simple :

$response->getBody()->write("Mon joli contenu");

Le package propose des classes spécifique selon le type de réponse, par exemple pour du HTML on a la classe HtmlResponse qui étend Response :

$response = new HtmlResponse($htmlContent);

Par défaut le Content-Type est fixé à text/html.

Pour les redirections on a une classe RedirectResponse :

$response = new RedirectResponse('/contact');

Pour expédier la réponse on a la classe SapiEmitter :

$emitter = new Zend\Diactoros\Response\SapiEmitter;
$emitter->emit($response);

Le package permet encore d’autres choses mais qui ne relèvent pas directement du PSR-7, comme la création d’un serveur à la mode node ou de la sérialisation.

Il ne reste plus qu’à voir quel succès va connaître cette préconisation encore jeune…

Un exemple pour terminer

La théorie c’est bien mais les exemples c’est beaucoup mieux ! Voyons ce qu’on peut faire avec simplicité avec zend-diactoros. On vient de voir que ce package assure la gestion du HTTP. Pour avoir quelque chose de fonctionnel il nous faut aussi un système de routage. Comme j’aime bien nikic/FastRoute je vais le choisir.

Avec Composer on va installer ces deux packages :

"require": {
    "nikic/fast-route": "^1.0",
    "zendframework/zend-diactoros": "^1.3"
},

Il ne reste plus alors qu’à les utiliser !

On crée un dossier public avec un htaccess :

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

Comme ça on dirige tout sur le fichier index.php dont voici le code :

<?php

require __DIR__.'/../vendor/autoload.php';

$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
    $r->addRoute('GET', '/users', 'users');
    $r->addRoute('GET', '/user/{id:\d+}', 'user');
});

function users() {
    return "<p>Voici tous les utilisateurs : Toto et Riri</p>";
}

function user($vars) {
    return "<p>C'est l'utilisateur avec l'id " . $vars['id'] . "</p>";
}

$request = Zend\Diactoros\ServerRequestFactory::fromGlobals($_SERVER, $_GET);

$routeInfo = $dispatcher->dispatch(
    $request->getMethod(), 
    $request->getUri()->getPath()
);

$emitter = new Zend\Diactoros\Response\SapiEmitter;

switch ($routeInfo[0]) {
    case FastRoute\Dispatcher::NOT_FOUND:
        $response = new Zend\Diactoros\Response\TextResponse(
            "Je ne connais pas cette page !", 
            404
        );
        break;
    case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
        $response = new Zend\Diactoros\Response\TextResponse(
            "Cette méthode n'est pas autorisée !", 
            405, 
            ['Allow' => implode(',', $routeInfo[1])]
        );
        break;
    case FastRoute\Dispatcher::FOUND:
        $handler = $routeInfo[1];
        $vars = $routeInfo[2];
        $response = new Zend\Diactoros\Response\HtmlResponse($handler($vars));
        break;
}

$emitter->emit($response);

C’est simple, léger et efficace ! Une base pour un framework hyper-léger…

 

PSR-7

Vous pourrez aussi aimer

Laisser un commentaire