Les expressions rationnelles avec PHP (article du Journal du Net)


Les expressions rationnelles (regular expressions, d'où la traduction erronée "expressions régulières") définissent des motifs (patterns) qui, en les appliquant sur une chaîne de caractères, permettent de tester la présence (ou non) de certains caractères, et donc de valider cette chaîne, la modifier ou encore d'y faire une recherche. Elles sont extrêmement puissantes et permettent, une fois que le développeur en maîtrise les capacités, de grandement réduire la longueur du code: permettant ainsi de gagner du temps, mais aussi d'être assuré de ne rien avoir oublié tant elles sont strictes.

Cet aspect des expressions rationnelles tient au fait que le motif décrit non-seulement le(s) caractère(s) ou la sous-chaîne voulue, mais spécifie aussi dans quel ordre et combien de fois les occurrences de ce(s) caractère(s) peu(ven)t apparaître. Ainsi, si tous les utilisateurs ont un mot de passe répondant au motif "_G347" (un tiret de soulignement, puis une lettre majuscule, puis trois chiffres), l'expression rationnelle correspondant à ce motif sera "^_{1}[A-Z]{1}[0-9]{3}$". C'est ensuite aux fonctions "regex" (abréviation usuelle pour regular expressions) de PHP de traiter ce motif de la manière dont vous le souhaitez...

PHP propose une poignée de fonctions en rapport avec les expressions rationnelles, mais il faut savoir qu'elles se divisent en deux: celles qui sont compatibles Perl (PCRE, Perl Compatible Regular Expression), et celles compatibles POSIX. Le langage Perl a été l'un des premier à faire usage des regex, initialement développées pour l'outils Unix grep. Perl a largement contribué à leur amélioration. Nous ne verrons ici que celles de types POSIX, PHP devant faire appel à une bibliothèque externe pour utiliser les regex PCRE. Ajoutons que les deux syntaxes sont différentes en certains points (les fonctions PCRE commencent et se terminent obligatoirement par un caractère de délimitation), et que les fonctions PCRE sont plus rapides et flexibles que les POSIX.

Syntaxe

La syntaxe de base
Caractère
Description
Exemple
^
Indique le début de la chaîne
"^Le": toute chaîne commençant par "Le": "Le chiens", "Les avions"...
$
Indique la fin de la chaîne
"soir$": toute chaîne se terminant par "soir": "bonsoir", "a ce soir"...
*
Le caractère apparaît zéro, une ou plusieurs fois
"^jim*y$": "jiy", "jimmy", "jimmmmmmmmy"...
+
Le caractère apparaît au moins une fois
"^jim+y$": "jimy", "jimmy", "jimmmmmmmmy"...
?
La caractère apparaît zéro ou une fois
"^lapins?$": "lapin" ou "lapins"
{x}
Le caractère apparaît strictement x fois
"^jim{2}y$": "jimmy".
{x,)
Le caractère apparaît au moins x fois
"^jim{2,}y$": "jimmy", "jimmmmmy"...
{x,y}
Le caractère apparaît entre x et y fois
"^sup{1,3}e{1,9}r$": "super", "supppeeeeeeeer"...
.
N'importe quel caractère
"^P.P$": "PHP", "PGP", "PCP"...
|
Opérateur OU
"^b(a|o|u)tte$": "batte", botte" ou "butte"
[xy]
"x ou y" (identique à x|y)
"^[rmg]ite$": "rite", "mite" ou "gite"
[x-y]
Tous les caractères entres x et y
"^[a-z]{5}$": "teejj", "dkjsh", "jfjdn", "kgodj"...
"^[a-zA-Z]{3}": une chaîne commençant par trois lettres.
",[A-Z0-9]$": une chaîne se terminant par une virgule suivie d'une majuscule ou d'un chiffre.

Ce n'est là qu'une partie de la syntaxe complète, mais qui nous permet déjà de créer des motifs très avancés. Pour utiliser les caractères spéciaux ci-dessus de manière littérale, il faut utiliser le caractère \, sauf à l'intérieur des crochets où les caractères spéciaux n'ont pas d'effet.

Les fonctions POSIX sont ereg(), ereg_replace(), eregi(), eregi_replace(), split(), spliti() et sql_regcase(). Le "i" à la fin de certaines fonctions signifie simplement que ces fonctions ne sont pas sensibles à la casse (la fonction ne fait pas la différence entre "a" et "A", par exemple). Florilège introductif de quelques unes de leurs possibilités.

ereg() et consoeurs
ereg() est la fonction standard: elle permet de tester la présence d'un ou plusieurs caractères dans une chaîne donnée. Ainsi,

ereg("^_{1}[A-Z]{1}[0-9]{3}$", $chaîne)

...renverra "TRUE" si $chaine contient effectivement une chaîne correspondant au motif que nous avons décrit plus haut. Il ne reste qu'à l'implémenter dans une structure de notre choix. Ainsi, créez une fonction is_mail() qui va nous dire si la chaîne donné est un mail valide syntaxiquement:

<?php
function is_mail($chaine)
  {
  return eregi('^[a-z0-9\._-]+@[a-z0-9\.-]+\.[a-z]{2,3}$', $chaine);
  }
?>

[a-z0-9\._-]+ nous indique qu'avant l'arobase (@), nous acceptons un nombre illimité (à cause de l'usage de "+") de caractères alphanumériques (lettres et chiffres), et les caractères ".", "_" et "-". Idem pour la première partie du nom de domaine, l'extension étant pour sa part limitée à 2 à 3 caractères.

ereg_replace() et eregi_replace() poussent la technique un peu plus loin: au lieu de dire si un motif donné est trouvé dans une chaîne, elles remplacent dans la chaîne les occurrences du motif, par un caractère ou une chaîne donné en deuxième argument de la fonction:

<?php
$chaine = "Il      y   a         trop       d'espaces       !";
$var = eregi_replace(" +", " ", $chaine);
echo $var;
?>

split(), spliti() et sql_regcase()
Ce sont là des fonctions beaucoup plus spécifiques: split() et spliti() décomposent une chaîne en plusieurs sous-chaînes stockées dans un tableau. Ainsi, pour mettre chaque mot d'une chaîne dans un tableau:

<?php
$chaine = split(" ", "Je ne sais pas quoi dire");
?>

sql_regcase(), enfin, "prépare une expression régulière pour effectuer une recherche insensible à la casse", c'est à dire qu'elle construit une expression régulière qui acceptera une motif correspondant à la chaîne spécifiée. Par exemple, pour lancer une recherche sur une base sans avoir à se soucier des accents ni dans la chaîne à chercher, ni dans la base stockée:

<?php
$recherche = sql_regcase($chaine);
$recherche = ereg_replace("e", "eéèêë", $chaine);
SELECT mot FROM table WHERE champ REGEXP '$recherche';");
?>