Capture et remplacement



Je vous avais dit au début de ces deux chapitres consacrés aux regex qu'elles servaient à faire une recherche puissante (ça, on vient de le voir, à travers les exemples du téléphone et du mail), mais aussi à faire une recherche et un remplacement.


Cela va nous permettre par exemple de faire la chose suivante :

  1. chercher s'il y a des adresses e-mail dans un message laissé par un visiteur ;
  2. modifier automatiquement son message pour mettre un lien

<a href="mailto:blabla@truc.com"> devant chaque adresse, ce qui rendra les adresses e-mail cliquables !

Avec cette technique, on peut faire pareil pour rendre les liens http:// automatiquement cliquables eux aussi. On peut également, 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 d'elles pour entourer une partie de votre regex et dire qu'elle devait se répéter quatre 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.

À partir de maintenant, nous allons travailler avec la fonction preg_replace.
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 utilisez des parenthèses, cela crée 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ère(s) (le point permet de dire « n'importe lesquels »), suivi(s) d'un [/b] ».

J'ai été obligé de mettre des antislashs « \ » devant les crochets pour que PHP ne les confonde pas avec des classes de caractères (comme [a-z]).

Normalement, si vous réfléchissez deux secondes, vous devez vous dire qu'ici les parenthèses ne sont pas obligatoires. Et c'est vrai que pour faire juste une recherche, les parenthèses sont effectivement inutiles. Mais pour faire un remplacement, cela 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 ensuite se servir 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] :

<?php

$texte = preg_replace('#\[b\](.+)\[/b\]#i', '<strong>$1</strong>', $texte);

?>


Voici comment s'utilise la fonction preg_replace.

  1. 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]).

  1. 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 même si c'est pas fait pour).

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 en fait entouré des balises <strong> !

  1. 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.

Si je schématise le fonctionnement, ça donne la figure suivante.



Fonctionnement de la fonction preg_replace



Il y a quelques règles à respecter que vous allez devoir apprendre.

  • Si vous avez plusieurs parenthèses, pour savoir le numéro de l'une d'elles il suffit de les compter dans l'ordre de gauche à droite.

Par exemple : #(anti)co(nsti)(tu(tion)nelle)ment# Il y a quatre parenthèses dans cette regex (donc$1$2$3 et $4). La parenthèse numéro 3 ($3) contient « tutionnelle », et la parenthèse $4contient « tion ».

N'oubliez pas que c'est l'ordre dans lequel les parenthèses sont ouvertes qui est important.

  • Vous pouvez utiliser jusqu'à 99 parenthèses capturantes dans une regex (ça vous laisse de la marge). Ça va donc jusqu'à $99.
  • Une variable $0 est toujours créée ; elle contient toute la regex. Sur le même exemple que tout à l'heure :

#(anti)co(nsti)(tu(tion)nelle)ment# … $0 contient « anticonstitutionnellement ».

  • Si, par hasard, vous ne voulez pas qu'une parenthèse soit capturante (pour vous faciliter les comptes, ou parce que vous avez beaucoup beaucoup de parenthèses), il faut qu'elle commence par un point d'interrogation suivi d'un deux points « : ». Par exemple :

#(anti)co(?:nsti)(tu(tion)nelle)ment# La seconde parenthèse n'est pas capturante. Il ne nous reste que trois variables (quatre si on compte $0) :

  1. $0 : anticonstitutionnellement
  2. $1 : anti
  3. $2 : tutionnelle
  4. $3 : tion

Voilà : si vous avez compris ça, vous avez tout compris, bravo ! ;-)


Créez votre bbCode


On peut maintenant 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 balises 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, ceux-ci suffiront déjà :

  • [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 trois 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 et 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 activer 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 en italique avec le bbCode :

<?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, 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 deux parenthèses capturantes :

  1. la première pour récupérer le nom de la couleur qui a été choisie (en anglais, comme ça on n'aura pas besoin de le changer pour le code HTML) ;
  2. la seconde pour récupérer le texte entre [color=truc] et [/color] (pareil que pour gras et italique).

Voici le résultat :

<?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 la solution :

<?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 : je veux surtout 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 fonctionne 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.

Résumons maintenant notre parser bbCode au complet :

<?php

if (isset($_POST['texte']))

{

    $texte = stripslashes($_POST['texte']); // On enlève les slashs 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 !

    echo $texte . '<br /><hr />';

}

?>

 

<p>

    Bienvenue dans le parser du Site du Zéro !<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éro[/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, je ne peux plus rien pour vous.

Avant de terminer, comme j'ai peur que vous vous ennuyiez, je vous donne ci-dessous 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.


Bon courage ! Vous en aurez besoin !


En résumé

   

  • Certains caractères sont spéciaux au sein d'une expression régulière : on parle de métacaractères. Si on souhaite les rechercher dans une chaîne, il faut les échapper en plaçant un symbole antislash devant. Par exemple : \[.
  • Il existe des classes abrégées, c'est-à-dire des classes toutes prêtes, comme par exemple \d qui revient à écrire [0-9].
  • La fonction preg_replace permet d'effectuer des remplacements dans une chaîne de texte.
  • Dans le cas d'un remplacement, les parenthèses au sein d'une expression régulière permettent de capturer une portion de texte pour la réutiliser dans une autre chaîne.

Créé avec HelpNDoc Personal Edition: Créer de la documentation iPhone facilement