PSR-7
Mardi 19 juillet 2016 16:31
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 :
- PSR-1 : les règles de base du codage, par exemple le nom des constantes doit être en majuscules avec un soulignement comme séparateur.
- PSR-2 : un ensemble de règles de codage qui vient compléter et étendre le PSR-1, par exemple on n'utilise pas la tabulation mais on remplit avec 4 espaces.
- PSR-3 : des règles pour les log, qui définit 8 méthodes : debug, info, notice, warning, error, critical, alert, emergency.
- PSR-4 : des règles pour le chargement automatique des classes, c'est la base de Composer.
- PSR-6 : des règles pour la mise en cache.
- PSR-7 : des règles pour les messages HTTP, c'est l'objet de cet article...
PSR-7 Http Message
Un dépôt Github contient les interfaces relatifs à la préconisation. On en trouve 7 :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-diactorosDans 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...
Par bestmomo
Aucun commentaire