Créez votre propre framework… avec les composants Symfony2 (partie 11) | KeiruaProd
Je suis développeur web freelance et propose des formations à Symfony2 ! Contactez-moi pour en discuter.

Cet article est la traduction d’un article original de Fabien Potencier, à l’origine de Symfony2, disponible ici.

Si vous deviez utiliser notre framework dès maintenant, vous voudriez probablement permettre de personnaliser les pages d’erreur. Actuellement, nous gérons les erreurs 404 et les erreurs 500, mais les réponses sont codées en dur dans le framework lui-même. Les rendre personnalisables n’est pas très difficile : on déclenche un nouvel évènement, et quelqu’un l’écoute. Pour faire ça bien, il faut que l’écouteur appelle ensuite un contrôleur. Et si le contrôleur d’erreur lève une exception ? Boucle infinie. Il doit y avoir un moyen plus facile, hein ?

Pensez à la classe HttpKernel ! Au lieu de résoudre les mêmes problèmes encore et encore, et réinventer la roue à chaque fois, la classe HttpKernel est une implémentation générique, extensible et flexible de HttpKernelInterface.

Cette classe est très similaire à la classe de framework que nous avons écrit jusqu’à présent : elle déclenche des évènements à des endroits stratégiques durant la gestion des requêtes, elle utilise un résolveur de contrôleur pour choisir le contrôleur à qui envoyer la requête, et, bonus supplémentaire, elle gère les cas limites et fournit de bons retours lorsqu’un problème survient.

Voici le nouveau code du framework :


Et le nouveau contrôleur de façade:


addSubscriber(new HttpKernel\EventListener\RouterListener($matcher));

$framework = new Simplex\Framework($dispatcher, $resolver);

$response = $framework->handle($request);
$response->send();

RouterListener est une implémentation de la même logique que nous avions dans notre framework : elle associe les requêtes entrantes et remplit les attributs de requête avec les paramètres de route.

Notre code est maintenant bien plus concis et étonnamment plus robuste et puissant que jamais. Par exemple, utilisez la classe toute prête ExceptionListener pour rendre la gestion d'erreurs configurable :


$errorHandler = function (HttpKernel\Exception\FlattenException $exception) {
$msg = 'Something went wrong! ('.$exception->getMessage().')';

return new Response($msg, $exception->getStatusCode());
});
$dispatcher->addSubscriber(new HttpKernel\EventListener\ExceptionListener($errorHandler);

ExceptionListener fournit une instance de FlattenException au lieu de lancer une instance d'Exception pour faciliter la manipulation et l'affichage des exceptions. Elle peut prendre n'importe quel contrôleur (valide) comment gestionnaire d'exceptions, vous pouvez donc créer une classe ErrorController au lieu d'utiliser une closure :


$listener = new HttpKernel\EventListener\ExceptionListener('Calendar\\Controller\\ErrorController::exceptionAction');
$dispatcher->addSubscriber($listener);

Le contrôleur d'erreurs, par exemple :


getMessage().')';

return new Response($msg, $exception->getStatusCode());
}
}

Voilà ! Gestion d'erreurs propre et personnalisable sans efforts. Et bien sûr, si votre contrôleur lève une exception, HttpKernel la gèrera correctement.

Dans la partie 2, nous avons parlé de la méthode Response::prepare(), qui permet de nous assurer qu'une réponse correspond à des spécifications HTTP. C'est sans doute une bonne idée de toujours y faire appel avant d'envoyer la réponse au client. C'est ce que fait le ResponseListener :


$dispatcher->addSubscriber(new HttpKernel\EventListener\ResponseListener('UTF-8'));

Celui là était facile également ! Essayons autre chose : vous voulez avoir le support du streaming sans efforts ? Enregistrez un StreamedResponseListener :


$dispatcher->addSubscriber(new HttpKernel\EventListener\StreamedResponseListener());

Et dans votre contrôleur, renvoyez une instance de StreamedResponse au lieu de Response.

Lisez le chapitre sur le fonctionnement interne de Symfony2 dans sa documentation pour en savoir plus sur les évènements déclenchés par HttpKernel et en quoi il vous permettent de modifier le déroulement d'une requête.

Créez maintenant un listener qui vous permette de renvoyer une chaine au lieu d'un objet Response :


class LeapYearController
{
public function indexAction(Request $request, $year)
{
$leapyear = new LeapYear();
if ($leapyear->isLeapYear($year)) {
return 'Oui, c\'est une annee bissextile ! ';
}

return 'Non, ce n\'est pas une annee bissextile.';
}
}

Pour implémenter cette fonctionnalité, nous allons écouter l'évènement kernel.view, déclenché juste après que le contrôleur ait été appelé. Son but est de convertir la valeur de retour du contrôleur en une instance appropriée de Response, mais seulement si c'est nécessaire :


getControllerResult();

if (is_string($response)) {
$event->setResponse(new Response($response));
}
}

public static function getSubscribedEvents()
{
return array('kernel.view' => 'onView');
}
}

Le code est simple car l'évènement kernel.view est seulement déclenché lorsque la valeur de retour du contrôleur n'est pas une Response et car spécifier la réponse depuis l'évènement stoppe la propagation de l'évènement (notre écouteur ne peut interférer avec les autres écouteurs de vue).

N'oubliez pas de l'enregistrer dans le contrôleur de façade :


$dispatcher->addSubscriber(new Simplex\StringResponseListener());

Si vous oubliez d'enregistrer l'inscription, HttpKernel va lancer une erreur avec le message suivant : "The controller must return a response (Non, ce n'est pas une annee bissextile. given).".

A ce stade, notre framework entier est aussi compact que possible et il est principalement composé d'un assemblage de librairies existantes. Pour l'étendre, il suffit d'enregister des écouteurs d'évènements ou des inscriptions.

Heureusement, vous avez maintenant une meilleure compréhension de pourquoi l'utilisation de HttpKernelInterface est si puissante. Son implémentation par défaut, HttpKernel, vous donne accès sans efforts à des fonctionnalités vraiment sympa. Et comme HttpKernel est en fait le code derrière les framework Symfony2 et Silex, vous avez le meilleur des 2 mondes : un framework personnalisé taillé sur mesure, mais basé sur une architecture bas niveau très solide et bien maintenue qui a déjà prouvée son fonctionnement sur de nombreux sites web; c'est également un code qui a eu un audit de sécurité et a prouvé bien fonctionner à des échelles très différentes.

Commentaires clos.