Les expressions régulières (Partie 2/2)
Voici donc la suite (et fin) de notre aventure avec les expressions régulières

Le mot d'ordre de ce chapitre est :
pratiquer. Hormis quelques points que nous allons aborder au début, vous savez le principal sur les regex mais il vous manque le plus important : la pratique !
Dans la seconde moitié de ce chapitre, nous allons donc construire
ensemble des Regex, pour que vous voyiez comment il faut procéder pour arriver enfin à écrire cette $%@#$#% de Regex

Ecrire un bout de Regex comme on l'a fait jusqu'ici, c'est une chose, mais créer une Regex complète vous allez voir que c'est une toute autre paire de manches
Pour commencer, et avant d'aller plus loin, il me semble important d'ajouter un nouveau mot à votre vocabulaire :
métacaractères.
Ce n'est pas une insulte de programmeur, mais un mot qui signifie tout simplement "
caractères spéciaux". Ce sont des caractères pas comme les autres qui servent normalement à faire quelque chose de particulier.
Alerte mon Général ! Les métacaractères s'échappent !
Dans le langage PCRE (des Regex), les métacaractères qu'il faut connaître sont les suivants :
# ! ^ $ ( ) [ ] { } ? + * . \ |
Ces caractères-là, il faut bien les retenir. Pour la plupart d'entre eux, vous les connaissez déjà.
Ainsi, le dollar "
$" est un caractère spécial parce qu'il permet d'indiquer une fin de chaîne.
De même pour l'accent circonflexe, le dièse, les parenthèses, les crochets, les accolades et les symboles "
? + *" : nous les avons tous utilisés dans le chapitre précédent, souvenez-vous.
Pour le point "
." et l'antislash "
\", vous ne les connaissez pas mais vous n'allez pas tarder à les apprendre.
Bon, ce sont des caractères spéciaux et chacun d'eux signifie quelque chose de précis. Et alors ?
Et alors, le problème vous tombe dessus le jour où vous voulez chercher par exemple "Quoi ?" dans une chaîne.
Comment écririez-vous la Regex ? Comme ça ?
#Quoi ?#
Eh non surtout pas ! Le point d'interrogation, vous le savez, sert à dire que la lettre juste avant est facultative (elle peut apparaître 0 ou 1 fois). Ici, l'espace devant le point d'interrogation serait donc facultatif, mais ce n'est pas ce qu'on veut dire !
Alors, comment faire pour faire comprendre qu'on recherche "Quoi ?" alors que le point d'interrogation a déjà une signification ?
Il va falloir
l'échapper. Cela signifie que vous devez mettre en fait un antislash "
\" devant un caractère spécial. Ainsi, la bonne regex serait :
#Quoi \?#
Ici, l'antislash sert à dire que le point d'interrogation juste après n'est pas un symbole spécial, mais bel et bien une lettre comme une autre !
C'est la même chose pour tous les autres métacaractères que je vous ai montré plus haut (# ! ^ $ ( ) [ ] { } ? + * . \) : il faut mettre un antislash devant si vous voulez les utiliser dans votre recherche.
Vous remarquerez que pour utiliser un antislash il faut mettre... un antislash devant ! Comme ceci : \\
Bien tordu tout ça non ?

Pourtant, ce que vous devez retenir est simple : si vous voulez utiliser un caractère spécial dans votre recherche, il faut mettre un antislash devant. Point barre.
Je vous donne quelques exemples d'utilisation, ça devrait bien vous faire rentrer ça dans la tête :
| Chaîne |
Regex |
Résultat |
| Je suis impatient ! |
#impatient \!# |
VRAI |
| PARDON !? |
#PARDON !\?# |
ERROR |
| Je suis (très) fatigué |
#\(très\) fatigué# |
VRAI |
| J'ai sommeil... |
#sommeil\.\.\.# |
VRAI |
| Le smiley :-\ |
#:-\\# |
VRAI |
Dans le second exemple, j'ai bien pensé à échapper le point d'interrogation, mais j'ai oublié le point d'exclamation, ce qui a produit une erreur.
La bonne regex aurait été :
#PARDON \!\?#
Le clas cas des classes
Vous m'excuserez si j'ai dérapé sur ce sous-titre, mais je vous mets au défi quand même d'arriver à dire 10 fois très rapidement "Le cas des classes" sans erreur
De quoi parlait-on déjà ?

Ah oui, les expressions régulières c'est vrai

Bon, si je vous ennuie un peu là (je vous comprends), il faudra m'excuser mais je n'ai pas le choix. Il est obligatoire que vous sachiez ce genre de choses si vous voulez vraiment utiliser les Regex.
Il reste une dernière petite chose à voir (encore un cas particulier) : c'est à propos des classes de caractères.
Jusqu'ici, vous avez mis des lettres et des chiffres entre les crochets, par exemple :
#[a-z0-9]#
Oui mais, vous vous en doutez, vous avez le droit de mettre d'autres caractères, comme les accents (mais dans ce cas il faut les énumérer un à un). Par exemple : [a-zéèàêâùïüë] etc...
Jusque-là, tout va bien. Mais si vous voulez lister aussi des caractères spéciaux mmh ? Par exemple un point d'interrogation (au hasard :p). Eh bien là, ça ne compte pas ! Pas besoin de l'échapper, à l'intérieur de crochets les métacaractères... ne comptent plus !
Ainsi, cette regex marche très bien :
#[a-z?+*{}]#
Elle signifie qu'on a le droit de mettre une lettre, un point d'interrogation, un signe + etc...
3 cas particuliers cependant :
- "#" (dièse) : il sert toujours à indiquer la fin de la Regex. Vous DEVEZ mettre un antislash devant même dans une classe de caractères pour l'utiliser.
- "]" (crochet fermant) : normalement, le crochet fermant indique la fin de la classe. Si vous voulez vous en servir comme d'un caractère que vous recherchez, il faut là aussi mettre un antislash devant.
- "-" (tiret) : encore un cas un peu particulier. Le tiret, vous le savez, sert à définir un intervalle de classe (comme [a-z]). Et si vous voulez mettre le tiret dans la liste des caractères possibles ? Eh bien il suffit de le mettre soit au début de la classe, soit à la fin. Par exemple : [a-z0-9-] permet de chercher une lettre, un chiffre, ou un tiret.
La bonne nouvelle, c'est que vous êtes maintenant prêts à faire quasiment toutes les Regex que vous voulez

La mauvaise, c'est que je viens de dire "quasiment"
Oh rassurez-vous, ça ne sera pas long et vous ne sentirez aucune douleur (à ce stade, on ne ressent plus la douleur de toute façon )
Je souhaite juste vous montrer ce qu'on appelle
les classes abrégées, et que moi j'appelle
les raccourcis (ce mot me parle un peu plus).
Certains de ces raccourcis ne vous seront pas indispensables, mais comme vous risquez de les rencontrer un jour ou l'autre, je ne voudrais pas que vous soyiez surpris et que vous croyiez que je vous ai caché des choses
Voici ce qu'il faut retenir :
| Raccourci |
Signification |
| \d |
Indique un chiffre. Ca revient exactement à taper [0-9] |
| \D |
Indique ce qui n'est PAS un chiffre. Ca revient à taper [^0-9] |
| \w |
Indique un mot. Cela correspond à taper [a-zA-Z0-9_] |
| \W |
Indique ce qui n'est PAS un mot. Si vous avez suivi, ça revient à taper [^a-zA-Z0-9_] |
| \t |
Indique une tabulation |
| \n |
Indique une nouvelle ligne |
| \r |
Indique un retour chariot |
| \s |
Indique un espace blanc (correspond à \t \n \r) |
| \S |
Indique ce qui n'est PAS un espace blanc (\t \n \r) |
| . |
Le point indique n'importe quel caractère ! Il autorise donc tout ! |
Il s'agit de lettres normales, mais quand on met un antislash devant, on leur
donne une signification spéciale.
C'est l'inverse de ce qu'on faisait tout à l'heure : on mettait un antislash devant les métacaractères pour leur
enlever leur signification spéciale.
Pour le point, il existe une exception : il indique tout sauf les Entrées (\n).
Pour faire en sorte que le point indique tout, même les entrées, vous devrez utiliser l'option "s". Exemple :
#[0-9]-.#s
Allez, cette fois vous en savez assez, on va pouvoir passer à la pratique
Vous allez enfin comprendre pourquoi vous en avez bavé tout le long

Cette fois, nous allons toucher du concret à travers des exemples qui vous seront sûrement utiles. Nous allons construire de grosses Regex
ensemble, pour que vous compreniez la méthode. Ensuite, vous serez tout à fait capable d'inventer vos Regex et de vous en servir pour vos scripts PHP !
Un numéro de téléphone
Pour cette première vraie Regex, nous allons essayer de voir si une variable (rentrée par un visiteur via un formulaire par exemple) correspond bien à un numéro de téléphone.
Je vais me baser sur les numéros de téléphone français, alors il faudra m'excuser si vous n'êtes pas français et que vous ne connaissez pas. L'avantage, c'est que vous pourrez ensuite vous exercer à écrire cette Regex pour les numéros de téléphone de votre pays
Pour rappel (et pour ceux qui ne savent pas donc), un numéro de téléphone français comporte 10 chiffres. Par exemple : "01 53 78 99 99". Il faut respecter les règles suivantes :
- Le premier chiffre est TOUJOURS un 0
- Le second chiffre va de 1 à 6 (1 pour la région parisienne... 6 pour les téléphones portables), mais il y a aussi le 8 (ce sont des numéros spéciaux)
- Ensuite viennent les 8 chiffres restants (ils peuvent aller de 0 à 9 sans problème)
Pour commencer, et pour faire simple, on va supposer que l'utilisateur rentre le numéro de téléphone sans mettre d'espace ni quoi que ce soit (mais on complique juste après, et vous verrez que c'est là le véritable intérêt des Regex).
Ainsi, le numéro de téléphone doit ressembler à ça : "0153789999". Comment écrire une Regex qui corresponde à un numéro de téléphone comme celui-ci ?
Voici comment je procède, dans l'ordre, pour construire cette Regex :
- Primo, on veut qu'il y ait UNIQUEMENT le numéro de téléphone. On va donc commencer par mettre les symboles ^ et $ pour indiquer un début et une fin de chaîne :
#^$#
- Continuons. On sait que le premier caractère est forcément un 0. On tape donc :
#^0$#
- Le 0 est suivi par un nombre allant de 1 à 6, sans oublier le 8 pour les numéros spéciaux. Il faut donc utiliser la classe [1-68], qui signifie "Un nombre de 1 à 6 OU le 8"
#^0[1-68]$#
- Ensuite, viennent les 8 chiffres restants, pouvant aller de 0 à 9. Il nous suffit donc d'écrire [0-9]{8} pour indiquer que l'on veut 8 chiffres. Au final, ça nous donne cette Regex :
#^0[1-68][0-9]{8}$#
Et c'est tout !
Bon, je vois que vous êtes en forme, alors ne nous arrêtons pas en si bon chemin et améliorons cette Regex

Maintenant, on va supposer que la personne peut taper un espace tous les 2 chiffres (comme c'est courant de le faire en France), mais aussi un point ou un tiret. Notre Regex devra donc accepter les numéros de téléphone suivants :
- 0153789999
- 01 53 78 99 99
- 01-53-78-99-99
- 01.53.78.99.99
- 0153 78 99 99
- 0153.78 99-99
- etc...
Et c'est là qu'est toute la puissance des Regex !!!
Les possibilités sont très nombreuses, et pourtant vous avez juste besoin d'écrire la Regex qui correspond
On reprend la création de notre Regex donc :
- Primo, le 0 puis le chiffre de 1 à 6 sans oublier le 8. Ca, ça ne change pas :
#^0[1-68]$#
- Après ces 2 premiers chiffres, il peut y avoir soit un espace, soit un tiret, soit un point, soit rien du tout (si les chiffres sont attachés). On va donc utiliser la classe [-. ] (tiret, point, espace).
N'oubliez pas : il faut mettre le tiret soit au début de la classe, soit à la fin. Sinon, c'est plantage assuré
Mais comment faire pour dire que le point (ou le tiret, ou l'espace) n'est pas obligatoire ? Avec le point d'interrogation bien sûr !
Ca nous donne :
#^0[1-68][-. ]?$#
- Après le premier tiret (ou point, ou espace, ou rien), on a les 2 chiffres suivants. On doit donc rajouter [0-9] à notre Regex.
#^0[1-68][-. ]?[0-9]{2}$#
- Et maintenant réfléchissez. Il y a moyen de terminer rapidement : on a juste besoin de dire que "[-. ]?[0-9]{2}" doit être répété 4 fois, et notre Regex est terminée ! On va se servir des parenthèses pour entourer tout ça, et mettre un {4} juste après pour indiquer que tout ça doit se répéter 4 fois. Ce qui nous fait finalement :
#^0[1-68]([-. ]?[0-9]{2}){4}$#
Vous pouvez l'encadrer en gros en poster dans votre chambre : c'est votre première VRAIE Regex !!!
#^0[1-68]([-. ]?[0-9]{2}){4}$#
Voici un petit script que j'ai fait rapidement, pour que vous puissiez tester toute la puissance des Regex :
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | <p>
<?php
if (isset($_POST['telephone']))
{
$_POST['telephone'] = htmlspecialchars($_POST['telephone']); // On rend inoffensives les balises HTML que le visiteur a pu rentrer
if (preg_match("#^0[0-68]([-. ]?[0-9]{2}){4}$#", $_POST['telephone']))
{
echo 'Le ' . $_POST['telephone'] . ' est un numéro <strong>valide</strong> !';
}
else
{
echo 'Le ' . $_POST['telephone'] . ' n\'est pas valide, recommencez !';
}
}
?>
</p>
<form method="post">
<p>
<label for="telephone">Votre téléphone ?</label> <input id="telephone" name="telephone" /><br />
<input type="submit" value="Vérifier le numéro" />
</p>
</form>
|
Vous pouvez essayer tous les numéros de téléphone que vous voulez, avec des espaces au milieu, ou pas si ça vous chante : la Regex est infaillible
Une adresse e-mail
Ca serait dommage de s'arrêter sur une si bonne lancée

Je vais donc vous présenter un deuxième exemple, qui vous sera certainement utile :
tester si l'adresse e-mail est valide.
Alors, avant de commencer quoi que ce soit, et pour qu'on soit bien d'accord, je vais rappeler comment est construite une adresse e-mail :
- On a tout d'abord le pseudonyme (au minimum une lettre, mais c'est plutôt rare). Il peut y avoir des lettres minuscules (pas de majuscules), des chiffres, des points, des tirets et des underscores "_".
- Il y a ensuite un arobase : @
- Ensuite il y a le nom du fournisseur d'accès. Pour ce nom, c'est pareil que pour le pseudonyme : que des minuscles, des chiffres, des tirets, des points et des underscores. La seule différence, vous ne pouviez pas forcément deviner, c'est qu'il y a au moins 2 caractères (par exemple, "a.com" n'existe pas, mais "aa.com" oui).
- Enfin, il y a l'extension (comme ".fr"). Cette extension comporte un point, suivi de 2 à 4 lettres (minuscules). En effet, il y a ".es", ".de", mais aussi ".com", ".net", ."org", ".info" etc...
L'adresse e-mail peut donc ressembler à :
j.dupont_2@wanadoo.fr
Construisons la Regex :
- Primo, comme tout à l'heure, on ne veut QUE l'adresse e-mail, donc on va demander à ce que ça soit un début et une fin de chaîne :
#^$#
- Ensuite, on a des lettres, chiffres, tirets, points, underscores, au moins une fois. On utilise donc la classe [a-z0-9._-] à la suite de laquelle on rajoute le signe + pour demander à ce qu'il y en ait au moins un :
#^[a-z0-9._-]+$#
- Vient ensuite l'arobace (là c'est pas compliqué, on a juste à taper le caractère) :
#^[a-z0-9._-]+@$#
- Puis, encore une suite de lettres, chiffres, points, tirets au moins 2 fois. On tape donc {2,} pour dire "2 fois ou plus" :
#^[a-z0-9._-]+@[a-z0-9._-]{2,}$#
- Ensuite vient le point (de ".fr" par exemple). Comme je vous l'ai dit plus haut, c'est un caractère spécial qui sert à indiquer "n'importe quel caractère" (même des accents). Or, ici, on veut enlever sa signification au point pour dire que l'on veut le symbole point dans notre Regex. On va donc mettre un antislash devant :
#^[a-z0-9._-]+@[a-z0-9._-]{2,}\.$#
- Enfin, pour terminer, il nous faut 2 à 4 lettres. Ce sont forcément des lettres minuscules, et cette fois pas de chiffres ou de tiret etc... On écrit donc :
#^[a-z0-9._-]+@[a-z0-9._-]{2,}\.[a-z]{2,4}$#
Et voilà encore une nouvelle Regex de bouclée !
#^[a-z0-9._-]+@[a-z0-9._-]{2,}\.[a-z]{2,4}$#
Ca a de la gueule vous trouvez pas ?
Allez, je suis en forme et de bonne humeur, je vous donne le script PHP pour tester cette Regex :
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | <p>
<?php
if (isset($_POST['mail']))
{
$_POST['mail'] = htmlspecialchars($_POST['mail']); // On rend inoffensives les balises HTML que le visiteur a pu rentrer
if (preg_match("#^[a-z0-9._-]+@[a-z0-9._-]{2,}\.[a-z]{2,4}$#", $_POST['mail']))
{
echo 'L\'adresse ' . $_POST['mail'] . ' est <strong>valide</strong> !';
}
else
{
echo 'L\'adresse ' . $_POST['mail'] . ' n\'est pas valide, recommencez !';
}
}
?>
</p>
<form method="post">
<p>
<label for="mail">Votre mail ?</label> <input id="mail" name="mail" /><br />
<input type="submit" value="Vérifier le mail" />
</p>
</form>
|
Testez donc des adresses comme :
- the_cypher@hotmail.com
- business_consultants@free4work.info
- mega-killer.le-retour@super-site.fr.st
- etc etc...
Alors, ça vous plaît ?

Je reconnais que ça paraît être un truc de malade quand on lit une Regex la première fois. J'imagine la tête que vous avez dû faire lorsque je vous en ai montré une dans l'introduction du chapitre précédent
Mais bon, vous voyez le progrès ?! On vient ensemble d'écrire un de ces fameux trucs imbuvables, et je ne pense pas que beaucoup d'entre vous pensaient y arriver en lisant le chapitre précédent !
Pourtant nous y voilà, nous avons réussi à écrire 2 Regex complètes. Je ne vais pas vous faire travailler sur une troisième, vous avez je pense compris le principe et vous savez vous débrouiller comme des grands
Je veux juste vous montrer une dernière petite chose avant de passer à la dernière notion importante que nous aborderons (Capture et remplacement).
Des Regex... avec MySQL !!!
Comme quoi, vous allez vraiment être heureux d'en avoir un peu bavé pour arriver jusqu'ici

Eh oui, grrrande nouvelle : MySQL comprend les Regex !
Et ça, bah c'est tout bénèf pour vous : vous venez d'apprendre à écrire des Regex, vous n'avez presque rien de plus à savoir pour vous en servir avec MySQL.
Il faut savoir cependant que MySQL ne comprend que les Regex en langage POSIX, et pas PCRE comme on a appris.
Salaud ! Tu nous as fait apprendre PCRE parce que c'est plus rapide, et on peut même pas s'en servir avec MySQL ???
Meuh non, calmez-vous voyons
Je vous ai appris PCRE parce que c'était beaucoup plus rapide ET que c'était pratiquement pareil que POSIX.
Alors, vous avez juste besoin de retenir ceci pour faire une Regex POSIX :
- Il n'y a pas de délimiteur ni d'options. Votre Regex n'est donc pas entourée de dièses
- Il n'y a pas de classes abrégées comme on l'a vu plus haut, donc pas de \d etc... En revanche, vous pouvez toujours utiliser le point pour dire : "n'importe quel caractère".
Le mieux, bien entendu, c'est toujours un bon exemple. Supposons que vous avez stocké les IP de vos visiteurs dans une table "visiteurs" et que vous voulez les noms des visiteurs dont l'ip commence par "84.254" (vous obtiendrez normalement uniquement des personnes qui sont chez Free) :
SELECT nom FROM visiteurs WHERE ip REGEXP '^84\.254(\.[0-9]{1,3}){2}$'
Cela signifie :
Sélectionne tous les noms de la table visiteurs où l'ip commence par "84.254" et se termine par 2 autres nombres de 1 à 3 chiffres (ex : 84.254.6.177).
Toute la puissance des Regex dans une requête MySQL pour faire une recherche très précise... Ca ne se refuse pas

Je ne m'étends pas plus dessus, je sais que vous saurez vous débrouiller si jamais cela vous est utile.
Passons maintenant à la dernière notion importante avec les Regex : "Capture et remplacement" !
Je vous avais dit au début de ces 2 chapitres consacrés aux Regex qu'elles servaient à faire une recherche puissante (ça on vient de le voir, à travers l'exemple du téléphone et du mail), mais aussi à faire une recherche / remplacement.
Cela va nous permettre par exemple de faire la chose suivante :
- Chercher s'il y a des adresses e-mail dans un message laissé par un visiteur.
- Modifier automatiquement son message pour mettre un lien <a href="mailto:blabla@truc.com"> devant chaque adresse, ce qui rendra les e-mails cliquables !
Avec cette technique, on peut faire pareil pour rendre les liens http:// automatiquement cliquables eux aussi. On peut aussi, vous allez voir, créer notre propre langage simplifié pour le visiteur, comme le fameux bbCode utilisé sur la plupart des forums ([b][/b] pour mettre en gras, ça vous dit quelque chose ? ;))
Les parenthèses capturantes
Tout ce que nous allons voir maintenant tourne autour des parenthèses.
Vous vous êtes déjà servi des parenthèses pour entourer une partie de votre Regex et dire qu'elle devait se répéter 4 fois par exemple (comme on l'a fait pour le numéro de téléphone).
Eh bien ça, c'est la première utilité des parenthèses, mais
elles peuvent aussi servir à autre chose !
Nous allons travailler avec la fonction
preg_replace à partir de maintenant.
C'est avec cette fonction que nous allons pouvoir réaliser ce qu'on appelle une "capture" de chaîne.
Ce qu'il faut savoir, c'est qu'à chaque fois que vous mettez des parenthèses, ça va créer une "variable" contenant ce qu'elles entourent.
Je m'explique avec une Regex :
#\[b\](.+)\[/b\]#
Vous ne devriez pas avoir trop de mal à la déchiffrer : elle signifie "
Chercher dans la chaîne un [b], suivi d'un ou plusieurs caractères (le point permet de dire "n'importe lesquels"), suivis d'un [/b]".
J'ai été obligé de mettre des antislash "\" devant les crochets pour ne pas que PHP les confonde avec des classes de caractères (comme [a-z])
Normalement, si vous réfléchissez 2 secondes, vous devez vous dire que les parenthèses ne sont pas obligatoires ici. Et c'est vrai, que pour faire juste une recherche, les parenthèses sont inutiles.
Mais pour faire un remplacement, ça va être très pratique !
En effet, retenez bien ceci : à chaque fois qu'il y a une parenthèse, cela crée une variable appelée $1 (pour la première parenthèse), $2 pour la seconde etc...
On va se servir ensuite de ces variables pour
modifier la chaîne (faire un remplacement).
Sur la Regex que je vous ai montrée plus haut, il y a une seule parenthèse vous êtes d'accord ? Donc, il y aura juste une variable $1, qui contiendra ce qui se trouve entre le [b] et le [/b]. Et grâce à ça, on sait ce qu'on va mettre en gras
Bon, la théorie de tout ça est délicate à expliquer, alors je vais vous montrer de suite comment on fait pour mettre en gras tous les mots compris entre des [b][/b] :
Code : PHP1
2
3 | <?php
$texte = preg_replace('#\[b\](.+)\[/b\]#i', '<strong>$1</strong>', $texte);
?>
|
Voici comment s'utilise la fonction
preg_replace :
- On lui donne en premier paramètre la Regex. Rien de particulier, comme vous pouvez le constater, à part qu'il faut bien garder en tête que chaque parenthèse va créer une variable ($1, $2 etc...)
Ici, j'ai rajouté l'option "i" pour que le code fonctionne aussi avec des majuscules ([B][/B])
- Ensuite, et c'est là qu'est la nouveauté, on indique le texte de remplacement : "<strong>$1</strong>" (je vous rappelle que <strong> permet de mettre en gras en HTML).
Entre les balises HTML, j'ai mis $1. Cela signifie que ce qui se trouve dans la parenthèse capturante (entre [b] et [/b]) sera mis entre les balises <strong> à la place !
- Enfin, dernier paramètre, c'est le texte dans lequel on fait notre recherche / remplacement (ça vous connaissez déjà).
La fonction
preg_replace renvoie le résultat après avoir fait les remplacements (ce qui explique pourquoi on fait "$texte = preg_replace();").
Si je schématise le fonctionnement, ça donne ça :
Il y a quelques règles à respecter que vous allez devoir apprendre :
Voilà si vous avez compris ça, vous avez tout compris, bravo !
Créez votre bbCode
Maintenant, on peut passer à la pratique et apprendre à se servir des parenthèses capturantes.
Nous allons réaliser ce qu'on appelle un
parser (prononcez "parseur";).
Le parser va servir à transformer le texte rédigé par un visiteur (pour un message sur un forum, ou sur votre livre d'or, ou même sur votre mini-chat !), en un texte inoffensif (sans balise HTML grâce à
htmlspecialchars) mais qui accepte aussi du bbCode !
On ne va pas faire tous les bbCode qui existent (trop long), mais pour s'entraîner déjà ceux-ci suffiront :
- [b][/b] : pour mettre du texte en gras.
- [i][/i] : pour mettre du texte en italique.
- [color=red][/color] : pour colorer le texte (il faudra laisser le choix entre plusieurs couleurs).
Et nous ferons en sorte de remplacer aussi automatiquement les URL (http://) par des liens cliquables
Commençons par [b] et [i] (c'est la même chose).
Vous avez déjà vu le code pour [b], et c'est en effet
presque le bon. Il y a un problème toutefois : il manque des options. Pour que ça marche, on va avoir besoin d'utiliser 3 options :
- i : pour accepter les majuscules comme les minuscules ([B] et [b])
- s : pour que le "point" fonctionne aussi pour les retours à la ligne (pour que le texte puisse être en gras sur plusieurs lignes)
- U : le U majuscule est une option que vous ne connaissez pas, qui signifie "Ungreedy" ("pas gourmand"). Je vous passe les explications un peu complexes sur son fonctionnement, mais sachez que, grosso modo, ça ne marcherait pas correctement s'il y avait plusieurs [b] dans votre texte. Exemple :
"Ce texte est [b]important[/b], il faut me [b]comprendre[/b] !"
... sans l'option Ungreedy, la Regex aurait voulu mettre en gras tout ce qu'il y a entre le premier [b] et le dernier [/b] (c'est-à-dire "important[/b], il faut me [b]comprendre";).
En utilisant l'option "U", la Regex s'arrêtera au premier [/b], et c'est ce qu'on veut 
Voici donc le code correct pour mettre en gras et italique avec le bbCode :
Code : PHP1
2
3
4 | <?php
$texte = preg_replace('#\[b\](.+)\[/b\]#isU', '<strong>$1</strong>', $texte);
$texte = preg_replace('#\[i\](.+)\[/i\]#isU', '<em>$1</em>', $texte);
?>
|
Comme vous pouvez le voir, c'est quasiment pareil pour [b] et [i] (à part que la balise HTML qu'on utilise est <em>).
Donc là, si vous avez suivi jusqu'ici, ça ne doit pas trop vous surprendre.
Passons maintenant à un cas un peu plus complexe : celui de la balise [color=truc]. On va laisser le choix entre plusieurs couleurs avec le symbole "
|" (OU), et on va utiliser 2 parenthèses capturantes :
- La première pour récupérer la couleur qui a été choisie (en anglais, comme ça on n'aura pas besoin de le changer pour le code HTML).
- La seconde pour récupérer le texte entre [color=truc] et [/color] (pareil que pour gras et italique quoi)
Voici le résultat :
Code : PHP1
2
3 | <?php
$texte = preg_replace('#\[color=(red|green|blue|yellow|purple|olive)\](.+)\[/color\]#isU', '<span style="color:$1">$2</span>', $texte);
?>
|
Ainsi, si on tape [color=blue]texte[/color], ça écrira texte en bleu. Vous pouvez essayer avec les autres couleurs aussi !
Allez dernière étape, et après je vous laisse essayer.
Je veux que les liens "http://" soient automatiquement transformés en liens cliquables. Essayez d'écrire la regex, vous en êtes tout à fait capables !
Voici le résultat :
Code : PHP1
2
3 | <?php
$texte = preg_replace('#http://[a-z0-9._/-]+#i', '<a href="$0">$0</a>', $texte);
?>
|
Dans le texte de remplacement, j'ai utilisé $0 qui, si vous vous souvenez bien, prend tout le texte reconnu par la Regex (donc ici toute l'url).
Il n'y a pas les options "s" et "U" car on ne fait jamais de retour à la ligne au milieu d'une URL et, le mode "Ungreedy" ne sert pas ici (essayez avec U, vous verrez que le lien s'arrête à la première lettre !)
Vous remarquerez que j'ai fait simple pour cette Regex. C'est vrai, j'aurais pu la faire plus complexe et plus précise, mais je n'ai pas envie de vous embrouiller avec ça, et surtout je veux que vous l'amélioriez vous-mêmes.
En effet, la Regex marche très bien pour http://www.siteduzero.com/images/super_image2.jpg, mais elle ne marche pas s'il y a des variables en paramètres dans l'url, comme par exemple :
http://www.siteduzero.com/index.php?page=3&skin=blue
Je vous laisse le soin d'améliorer la Regex, ça vous fera un peu de travail
Vous savez quoi ? Vous avez peut-être mal à la tête, mais moi mal à la tête ET mal aux doigts !

Mais je vais fournir un dernier effort allez, appelez ça "Le cadeau bonus de Mateo"
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 | <?php
if (isset($_POST['texte']))
{
$texte = stripslashes($_POST['texte']); // On enlève les slash qui se seraient ajoutés automatiquement
$texte = htmlspecialchars($texte); // On rend inoffensives les balises HTML que le visiteur a pu rentrer
$texte = nl2br($texte); // On crée des <br /> pour conserver les retours à la ligne
// On fait passer notre texte à la moulinette des Regex
$texte = preg_replace('#\[b\](.+)\[/b\]#isU', '<strong>$1</strong>', $texte);
$texte = preg_replace('#\[i\](.+)\[/i\]#isU', '<em>$1</em>', $texte);
$texte = preg_replace('#\[color=(red|green|blue|yellow|purple|olive)\](.+)\[/color\]#isU', '<span style="color:$1">$2</span>', $texte);
$texte = preg_replace('#http://[a-z0-9._/-]+#i', '<a href="$0">$0</a>', $texte);
// Et on affiche le résultat. Admirez ! :D
echo $texte . '<br /><hr />';
}
?>
<p>
Bienvenue dans le parser du Site du Zér0 !<br />
Nous avons écrit ce parser ensemble, j'espère que vous saurez apprécier de voir que tout ce que vous avez appris va vous être très utile !
</p>
<p>Amusez-vous à utiliser du bbCode. Tapez par exemple :</p>
<blockquote style="font-size:0.8em">
<p>
Je suis un gros [b]Zér0[/b], et pourtant j'ai [i]tout appris[/i] sur http://www.siteduzero.com<br />
Je vous [b][color=green]recommande[/color][/b] d'aller sur ce site, vous pourrez apprendre à faire ça [i][color=purple]vous aussi[/color][/i] !
</p>
</blockquote>
<form method="post">
<p>
<label for="texte">Votre message ?</label><br />
<textarea id="texte" name="texte" cols="50" rows="8"></textarea><br />
<input type="submit" value="Montre-moi toute la puissance des Regex" />
</p>
</form>
|
Pfiou !
Eh bah si avec ça vous me pondez pas un super site de la mort qui tue, je peux plus rien pour vous
Avant de terminer, comme j'ai peur que vous vous ennuyiez, je vous donne quelques idées de Regex que vous pourriez rajouter au parser :
- Je vous l'ai déjà dit plus haut, mais il serait très appréciable que les URL cliquables fonctionnent aussi pour des URL avec des variables comme :
http://www.siteduzero.com/index.php?page=3&skin=blue
- Vous devriez aussi parser les adresses e-mail, en faisant un lien "mailto:" dessus !
- Il serait bien de compléter le bbCode avec [u], [img] etc...
Mais puisqu'on y est, pourquoi refaire du bbCode ? Après tout, si vous êtes allergiques aux crochets, que pour vous [b] ne veut rien dire, vous n'avez qu'à inventer le code : {gras} {/gras} 
- Et, si faire des Regex vous plaît, je peux vous proposer un dernier défi qui devrait vous occuper un petit moment : écrire une fonction qui colore automatiquement le code HTML !
Vous donnez à la fonction le code HTML, elle en fait un htmlspecialchars, puis elle rajoute des <span style="color:..."> pour colorer par exemple en bleu les noms des balises, en vert les attributs, en rouge ce qui est entre guillemets etc etc... 
Bon courage !
Après des chapitres comme ceux-ci, je pense que ce petit cadeau vous fera le plus grand bien.
Je n'ai pas grand chose à ajouter, si ce n'est que je suis exténué mais heureux, parce que c'était vraiment ce que j'avais de plus difficile et tordu à vous enseigner
Informations sur le tutoriel