La réécriture d’URL récursive

Vous souhaitez vous affranchir des réécritures statiques ?
La limitation à 9 de la variable $N utilisée pour les références arrières vous cause un problème parce que vous avez trop de paramètres ?
Vous voulez une règle suffisamment générique pour digérer vos variables, quels que soient leurs noms ou l’ordre dans lequel elles sont invoquées ?

Qu’en est-il de la récursivité ?

Avant toutes choses, gardez à l’esprit que le flag [N] peut créer une boucle infinie s’il est mal utilisé. Nous vous suggérons de lire attentivement La réécriture des URL « à la volée » pour en comprendre le fonctionnement avant de vous engager dans la voie de la récursivité.Les bases de la réécriture d’URL avec le module Apache mod_rewrite ont été analysées dans ce précédent article.

Etude de cas

Imaginons que vos URLs aient à l’heure actuelle la forme suivante :

index.php?var1=valeur1&var2=valeur2&...&varN=valeurN

avec un nombre de paramètres variable selon les différentes invocations de votre script index.php

Imaginons toujours que vous souhaitez présenter au monde extérieur des URLs plus « propres » de la forme :

index-var1-valeur1-var2-valeur2-....-varN-valeurN.html

En dehors de leur aspect plus « esthétique », ces URLs présentent l’avantage supplémentaire d’être indexables par les moteurs de recherche (pour mémoire, Google n’indexe pas les URLs comprenant plus de 2 paramètres…)

Si vous ne souhaitez pas écrire une règle spécifique pour chaque cas qui peut se présenter, la récursivité est la seule issue possible.

Résolution du problème

L’analyse nous montre :
- que nous avons N couples de noms/valeurs à traiter, les valeurs pouvant être « vides » pour certaines variables.
- que les noms et les valeurs ne sont pas connus au moment d’écrire la règle.

Nous poserons une seule condition
- nos noms de variables ou valeurs ne pourront pas contenir le tiret (-) que nous choisissons comme séparateur. Si certaines de vos valeurs contiennent un tiret « -« , il suffira d’adapter la règle en choisissant un autre caractère séparateur.

Voici les règles qui seront commentées ci-après :

RewriteEngine on
RewriteRule index(-.+)-([^-]+)-([^-&]*)([^-]*).html index$1&$2=$3$4.html [N]
RewriteRule index-([^-]+)-([^-]*)(.*).html index.php?$1=$2$3 [L]

La première ligne « RewriteEngine on » devrait vous être familière, si ce n’est pas le cas retournez lire La réécriture des URL « à la volée », tout y est expliqué.

Pour décoder ce type d’URL, deux lignes suffisent.
- la première ligne va boucler sur elle même de manière à traiter un couple variable/valeur à la fois. Elle comprend 4 « blocs » que nous réécrivons à l’aide du mécanisme de « référence arrière » d’Apache (appelé « backreference » dans la documentation).
Cette boucle est contrôlée par le flag [N] en fin de règle. Ce flag donnant instruction au serveur Apache de recommencer les règles au début du fichier si la règle est vérifiée.
- La deuxième règle est triviale et ne devrait vous poser aucun problème de compréhension. Elle marquera la fin de la réécriture grâce au flag [L]

Attention aux boucles infinies
Comme le flag [N] causera un retour au début du fichier (à la première règle), il est de première importance d’éviter que cette règle soit précédée par toute autre pouvant interférer dans les réécritures. Gardez à l’esprit que chaque itération fera recommencer tout le processus à la première règle définie dans le fichier .htaccess, en injectant dans le moteur de réécriture l’URL sous la forme réécrite par la dernière itération, et non l’URL originelle.

Une mauvaise règle peut donc entraîner une boucle infinie si une URL mal réécrite est présentée.

Dans notre exemple, chaque nom de variable sera comparé à une chaîne de longueur supérieure ou égale à 1 caractère, ne comprenant pas le caractère « -« , raison pour laquelle nous utilisons la syntaxe ([^-]+).
Les valeurs pouvant consister en une chaîne nulle, nous utiliserons la notation ([^-&]*).L’ajout du caractère « & » dans notre chaîne est utile car c’est celui-ci qui marquera le début de la chaîne réécrite.

En quelques mots, nous nous attacherons à réécrire l’expression en commençant par la fin, et en progressant jusqu’à ce qu’il ne nous reste plus qu’un couple variable/valeur.

Réduit à un langage plus « humain », le premier argument de la règle 1 s’énoncerait :

On cherche une chaîne qui En clair Expression régulière Var.
débute par le mot « index » index
suivi par une chaîne d’au moins un caractère, débutant par un tiret, représentant la partie de l’expression restant à traiter (-.+) $1
suivi par un tiret
suivi par un premier groupe de caractères différents du tiret et non nul, contenant le nom de la variable ([^-]+) $2
suivi par un tiret
suivi par un deuxième groupe de caractères différents du tiret, pouvant être nul,contenant la valeur de la variable et ne pouvant pas contenir le caractère « & » ([^-&]*) $3
suivi par un groupe facultatif de caractères, à l’exclusion du tiret, contenant la partie de l’expression déjà traitée ([^-]*) $4
finit par le littéral « .html » .html

Nous nous appuierons sur deux particularités des expressions régulières :
- les expressions sont évaluées de gauche à droite
- lorsque plusieurs chaînes correspondent à la règle, la chaîne la plus longue est sélectionnée.

Pour mieux visualiser le processus complet et les différentes itérations, procédons par étapes successives :
(les noms vN et valN ont été choisis arbitrairement pour simplifier l’écriture de l’exemple, mais n’ont aucune incidence sur la réécriture de l’URL)

Itération 1 – Règle 1



Pour cette première itération, l’URL présentée est l’URL originelle, montrée ci-dessus.
Selon les principes énoncés plus haut, la variable (référence arrière) $1 se verra attribuer la plus grande longueur possible. Les variables $2 et $3 prendront donc respectivement les chaînes v5 et val5.
La variable $4, supposée contenir la partie de l’expression déjà traitée, sera vide.
Les chaînes de début « index » et de fin « .html » seront réécrites sans modification.
L’URL réécrite, sera donc représentée au moteur pour l’itération suivante.

Itération 2 – Règle 1


La différence principale par rapport à l’itération précédente est l’apparition de la chaîne déjà réécrite (en bleu) et son affectation à $4. Comme elle commence par le caractère « & » et que celui-ci a été explicitement exclus de la chaîne $3, il n’y a donc pas d’imprécision possible quant au début de $4.
L’expression est réécrite, et représentée pour la prochaine itération.

Itération 3 – Règle 1


La variable $4 se voit affecter une chaîne plus importante, alors que $1 est réduit d’autant.
Continuons le processus…

Itération 4 – Règle 1


Il nous reste toujours suffisamment de caractères pour vérifier la règle, car nous pouvons attribuer à $1 une chaîne non vide. Rappelons-nous que nous avons écrit la règle en imposant une chaîne d’au moins 1 caractère en début d’expression.
Nous voici donc pratiquement au bout de nos efforts…

Itération 5 – Règle 2


Cette fois, nous n’avons plus suffisamment de chaîne en début d’expression pour valider notre première règle.
Notre expression sera donc présentée à la règle suivante qui sera effectivement vérifiée. Cette règle comprend un flag [L], ce qui marque donc la fin de la réécriture.

Expression finale



C’est exactement ce que nous souhaitions, la deuxième règle nous a débarrassé de l’expression « .html » en fin de chaîne, et a modifié notre chaîne « index » en « index.php ? ».

Important !!
Comme pour toutes les règles de réécriture d’URLs, une analyse précise s’impose. Plus encore qu’avec des règles de réécriture simples, la récursivité mal appliquée peut imposer à votre serveur Apache une charge qu’il aura du mal à supporter.Le piège de la boucle infinie n’est jamais très loin, et un seul caractère mal placé peut vous y faire tomber.

La sagesse impose donc de tester ces règles dans un sous répertoire de votre site, ou mieux encore, sur un serveur local.

Un dernier exemple

Quelle règle utiliser si on veut s’affranchir du mot index- en début de chaîne, par exemple pour réécrire :

varA-11-varB-12-varC-13.html en program.php?varA=11&varB=12&varC=13 ?

C’est simple :

RewriteRule (.+)-([^-]+)-([^-&]*)([^-]*).html $1&$2=$3$4.html [N]
RewriteRule ([^-]+)-([^-]*)(.*).html program.php?$1=$2$3 [L]