Un router léger mais évolutif

Il y a quelques mois j’étais tombé sur ce router ultra léger, et je m’en suis servi pour quelques tests. Aujourd’hui je retombe dessus, et l’idée me vient de partager avec vous ce bout de code, mais surtout comment le faire évoluer.

Petit rappel

Pour mémoire, un Router est un code qui va prendre la dernière partie de l’URL (appelée URI) et la décomposer, afin de déterminer quel code exécuter, contrôleur à appeler etc.

Router light, trop light

Partons avec le code d’origine. Pour les plus attentifs, vous remarquerez que j’ai modifié 2 ou 3 trucs (notamment les délimiteurs dans le preg_match) mais rien d’important :

Code de l’application :

Et donc à l’URL http://exemple.com/blog/php/27, vous devriez voir apparaître : php:27

Jusqu’ici rien de compliqué.Si vous vous demandez d’où je sors les \w+ , je vous conseille de faire un tour du coté des Regex.

Mais il y a déjà un problème : Si mon site se trouve dans un sous-dossier, le router ne fonctionne plus.

La classe URI

Si notre Router ne fonctionne plus, c’est en fait lié à la REQUEST_URI, qui contient tous les arguments d’une URI, y compris les sous dossiers et noms de fichiers.

Dans mon cas, mon fichier se trouve à l’adresse http://localhost/tests/router.php. Si j’essaye d’accéder à l’adresse http://localhost/tests/router.php/blog/ok/555, la variable $_SERVER['REQUEST_URI']  me donne /tests/router.php/blog/ok/555. Or, la seule partie qui m’intéresse est blog/ok/555.
Nous allons donc créer une classe URI, chargée de récupérer la REQUEST_URI et de la préparer pour notre classe Router.

Un peu de bonnes pratiques

Pour commencer, nous allons créer une interface afin de définir le contrat que devra remplir notre classe :

Oui, c’est tout. Cette classe a pour seul but de nettoyer l’ URI de ses dossiers et nom de fichier.

Code de la classe

Implémentons maintenant notre classe :

Testons cette classe immédiatement, toujours sur la même URL :

Notre classe rempli bien le rôle que nous lui avions fixé.

Nous allons maintenant modifier notre Router afin qu’il accepte et utilise notre classe URI.

Classe Router, seconde partie

Afin d’implémenter la classe URI dans notre Router, nous allons y faire quelques modifications.

L’interface

Comme tout à l’heure, nous allons définir l’interface du router :

Rien de bien compliqué encore une fois, si ce n’est la fonction __construct, qui oblige à fournir une instance d’une classe implémentant l’interface URIInterface. Si vous n’êtes pas à l’aise avec ce procédé, jetez un œil au type-hinting 😉

Nouveau router

Sans plus tarder, voilà le code :

Pour plus de clarté, je vous ai surligné les lignes qui ont été modifiées. Vous devriez vous en sortir avec les commentaires.

Nouveau test

Passons immédiatement au test de notre router. Pour info, je teste avec l’URL http://localhost/tests/router.php/blog/ok/555

C’est pas génial tout ça ?

A vous la suite

Voici quelques éléments qui vous permettront d’améliorer votre router, si vous en avez besoin.

Un .htaccess

Ajouter un fichier à la racine de votre site vous permettra par exemple de rediriger toutes les requêtes vers un fichier unique, celui qui lancera le router, au hasard 😉 :

Avec ce code, nous allons rediriger toutes les requêtes qui ne pointent pas vers index.php, robots.txt ou le dossier assets (vous pourrez y mettre les images, css, ..) vers le fichier index.php situé à la racine du site.

GET et POST

En ajoutant 2 fonctions à votre Router ( ex : get() et post() ) et en utilisant $_SERVER['REQUEST_METHOD']  vous serez en mesure de modifier simplement le routage de votre application selon la méthode de la requête (GET et POST).

Ainsi, pour 2 URL identiques, vous pourrez appeler 2 fonctions ou controllers totalement différents.

404

Et si aucun masque (pattern) ne correspond à l’URL entrée ?
En plaçant un code après le  foreach  de la méthode run() , vous serez en mesure d’appeler une fonction si aucun masque ne correspond.

Pour conclure

Nous avons vu qu’il était facile de créer un Router simple, évolutif et qui fonctionne dans la plupart des cas. Il pourra être réutilisé dans de petits projets, ce qui nous évitera de mettre en place un micro-framework et de n’utiliser que la fonction de routage.

Bien entendu, je vous conseille de mettre en place quelques tests afin de le fiabiliser avant de passer tout ça en prod 😉

Si vous avez des remarques ou des idées d’améliorations simples, je suis tout ouï !

4 Commentaires

Ajouter un commentaire

  1. Il faudrait aussi que tu ajoutes la possibilité de passer une fonction (comme présentement) ou un chemin vers un fichier, comme php ou html. De cette manière, tu pourrais vérifier s’il s’agit d’une fonction à appeler ou d’un fichier à inclure, en fessant attention de vérifier s’il existe depuis le dossier racine. Ex. http://test.dev/tests/blog/ok/555 pourrait pointer vers $racine/templates/blog/index.php. De cette manière, on peut créer un site avec des adresses en slash pointant vers des fichiers en question. Aussi, ça serait bien de définir s’il s’agit d’un GET ou POST. Une même adresse mais avec des méthodes différentes serviraient, soit à afficher, soit à sauvegarder des données. Aussi, on pourrait se faire une classe App qui détiendrait le route + d’autres options afin d’avoir une architecture optimale. Moi, je me suis fait une architecture simple pour mes sites ou landing page. J’ai une classe App avec le routing, configuration du dossier de base, configuration d’un objet mobile depuis librairie Mobile_Detect, configuration du BaseFolder, configuration de la langue depuis le browser ou si passé dans l’url, configuration des inputs combinant $_GET et $_POST, de $_FILES, de $_COOKIE. Ça me permet aussi de créer un dossier public qui possède les dossiers js, css, images, etc de base + le fichier index.php qui gère le routing. De cette manière, rien n’est accessible sans passer par le dossier public. Je peux aussi configurer le header qui serait par défaut. Je me crée un dossier gabarits ou je met le header.php et footer.php. Bref, je vais arrêter là mais je me suis fait un mini-framework de site afin de faciliter le développement de site et landing page. P.S. Déjà pour le long texte 🙂

    • En effet on pourrait implémenter tout cela dans le router (bien que je ne sois pas très adepte de la possibilité de faire des include via un router, principalement à cause de la sécurité) mais là n’est clairement pas le but.
      Si tu relis le titre de l’article, il s’agit ici de créer un router léger destiné justement à ne pas avoir à utiliser un gros framework pour de petits projets. Mais j’ai tout de même, en fin d’article, donné quelques pistes pour étendre ce router, comme pouvoir distinguer les routes GET et POST.

      Enfin, concernant le reste de ton commentaire, comme dit plus haut le but est uniquement le router, pas créer un framework complet.
      Cela reste très intéressant, peut-être d’ailleurs que je ferai un gros article sur la création d’un micro-framework HMVC.

      Merci de ton commentaire 😉

      • Ça fait plaisir pour le commentaire.

        En fait, pour la sécurité, ce que tu fais, c’est de configurer les possibles url d’avance avec quel fichier il est associé. Ex. $app->addRoot(« GET », « /blog », « /templates/blog/index.php »); De cette manière, tu t’assures que personne peut essayer d’inclure n’importe lequel fichier. À moins que je ne vois pas une possible faille… Tu peux toujours me l’indiquer. En fait, effectivement, le titre parle d’un router léger. C’est juste que je me suis dit: quand on a besoin d’un router sans gros framework, on a souvent besoin d’un minimum d’autres choses. Donc, je voulais te soulever le fait d’avoir un micro framework très sympa. Effectivement, faire un article sur un micro framework serait sympa.

        • Non en effet si l’on procède comme tu l’indiques j’imagine que la fameuse faille de l’inclusion est évitée.

          Bien sûr, lorsqu’on a besoin d’un router, on a besoin d’autres choses. Mais si l’on commence comme ça, on va vite se retrouver avec un moteur de templates et un ORM ^^

          Je note tes remarques, j’en tiendrai compte lorsque j’écrirai un article sur la création d’un framework.

          Encore merci 😉

Laisser un commentaire

Votre adresse mail ne sera pas publiée.

*

© 2017 Max-Koder — Propulsé par WordPress

Theme par Anders NorenHaut ↑