Alain Barraud Mémento informatique  
 
Accueil Logithèque - SE Photo - vidéo Internet - protocoles Site Web PC - Réseau Archives
Page ouverte le 08/03/2010
retour sommaire PHP
Logo PHP
Partie 4 : Les expressions régulières (partie 2/2)

Ici, on va passer à la pratique. Ecrire un bout de Regex comme jusqu'à maintenant, c'est une chose, mais créer une Regex complète, c'est une toute autre paire de manches.
Sommaire

Une histoire de métacaractères

Nouveau mot : métacaractères. C'est 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, il faut bien les retenir. On a déjà vu la plupart d'entre eux.
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 déjà utilisés.
Pour le point "." et l'antislash "\", on ne les connait pas encore.
question Bon, ce sont des caractères spéciaux et chacun d'eux signifie quelque chose de précis. Et alors ?
Justement, si on veut chercher par exemple "Quoi ?" dans une chaîne. Est-ce qu'on écrit la Regex : #Quoi ?# comme ça ?
Surtout pas ! Le point d'interrogation 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 !
Pour dire que le point d'interrogation n'est pas un caractère spécial, il faut l'échapper, ce qui veut dire qu'on doit mettre un antislash "\" devant.
La bonne Regex sera : #Quoi \?#.
alt Ce sera la même chose pour tous les autres métacaractères (# ! ^ $ ( ) [ ] { } ? + * . \) : il faut mettre un antislash devant si on veut les utiliser dans sa recherche en tant que tels et non en tant que caractère spéciaux.
Remarquons que pour utiliser un antislash il faut mettre… un antislash devant ! Comme ceci : \\

Quelques exemples pour comprendre :
ChaîneRegexRé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, la bonne regex aurait été : #PARDON \!\?#
Le cas des classes
Dernière chose à propos des classes de caractères.
Jusqu'ici, on a mis des lettres et des chiffres entre les crochets, comme : #[a-z0-9]#.
Mais, on a le droit de mettre d'autres caractères comme les accents, mais dans ce cas, il faut les énumérer un à un. Exemple : #[a-zéèàêâùïüë]# etc….
Si on veut lister des caractères spéciaux ? Par exemple un point d'interrogation, là, ça ne compte pas ! Pas besoin de l'échapper, à l'intérieur de crochets, les métacaractères n'existent plus.
Ainsi, cette regex marche très bien : #[a-z?+*{}]#. On a le droit entre les crochets 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. On DOIT 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 on veut s'en servir comme d'un caractère qu'on recherche, il faut là aussi mettre un antislash devant.
  • "-" (tiret) : encore un cas un peu particulier. Le tiret, on sait, sert à définir un intervalle de classe (comme [a-z]). Et si on veut 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.

Les classes abrégées

Les classes abrégées, ou les raccourcis (c'est plus parlant).
Raccourcis à retenir :
RaccourciSignification
\dIndique un chiffre. Ca revient exactement à taper [0-9]
\DIndique ce qui n'est PAS un chiffre. Ca revient à taper [ˆ0-9]
\wIndique un mot. Cela correspond à taper [a-zA-Z0-9_]
\WIndique ce qui n'est PAS un mot. Ca revient à taper [^a-zA-Z0-9_]
\tIndique une tabulation
\nIndique une nouvelle ligne
\rIndique un retour chariot
\sIndique un espace blanc (correspond à \t \n \r)
\SIndique 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.
alt 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, on devra utiliser l'option "s". Exemple : #[0-9]-.#s

Construire une Regex complète

On va construire des Regex pour bien comprendre la méthode. Ensuite, on devrait pouvoir en inventer pour s'en servir dans nos scripts PHP.
Un numéro de téléphone
On va 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.
On va se baser sur les numéro de téléphone français qui 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).
    Oubliés les numéros adsl en 09
  • 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 on verra 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 procéder, dans l'ordre, pour construire cette Regex :
  1. 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 : #ˆ$#
  2. Continuons. On sait que le premier caractère est forcément un 0. On tape donc : #ˆ0$#
  3. 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[0-68]$#
  4. Ensuite, viennent les 8 chiffres restants, pouvant aller de 0 à 9. Il suffit donc d'écrire [0-9]{8} pour indiquer que l'on veut 8 chiffres. Au final, ça nous donne cette Regex :
    #ˆ0[0-68][0-9]{8}$#
Il faut améliorer cette Regex. On suppose maintenant que la personne peut taper un espace tous les 2 chiffres, mais aussi un point ou un tiret. Notre Regex devra donc accepter les numéros de ce type :
  • 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 on a juste besoin d'écrire la Regex qui correspond.
On reprend la création de notre Regex donc :
  1. Primo, le 0 puis le chiffre de 1 à 6 sans oublier le 8. Ca, ça ne change pas : #ˆ0[0-68]$#
  2. 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).
    erreur Ne pas oublier : 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 donne : #ˆ0[0-68][-. ]?$#
  3. 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[0-68][-. ]?[0-9]{2}$#
  4. Et maintenant si on réfléchit, 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[0-68]([-. ]?[0-9]{2}){4}$#

Petit script permettant de tester toute la puissance des Regex :
Code PHP
<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[1-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>
On peut essayer avec tous les numéros que l'on veut, la Regex est infaillible.
alt On aurait pu aussi utiliser le raccourci \d pour indiquer un chiffre dans notre Regex :
#ˆ0[1-68]([-. ]?\d{2}){4}$#
On a considéré que mettre [0-9] est quand même plus clair
Une adresse e-mail
Deuxième exemple : tester si l'adresse e-mail est valide.
A quoi ressemble une adresse e-mail :
  1. 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 "_".
  2. Il y a ensuite un arobase : @
  3. 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, on ne pouvait 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).
  4. 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.
Construction de la regex :
  1. 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 : #ˆ$#
  2. 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._-]+$#
  3. Vient ensuite l'arobase (il suffit de taper le caractère) : #ˆ[a-z0-9._-]+@$#
  4. 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,}$#
  5. Ensuite vient le point (de ".fr" par exemple). Comme on l'a 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,}\.$#
  6. Et 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}$#
script PHP pour tester cette Regex :
Code PHP
<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>
Des Regex… avec MySQL !!!
Bonne nouvelle : MySQL comprend les Regex !
On vient d'apprendre à écrire des Regex, on n'a presque rien de plus à savoir pour s'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.
question On a appris PCRE parce que c'est plus rapide, et on ne peut même pas s'en servir avec MySQL ???
Mais non, On a appris PCRE parce que c'est plus rapide ET que c'est pratiquement pareil que POSIX.
Pour faire une Regex POSIX, on a juste besoin de retenir ceci :
  • Il n'y a pas de délimiteur ni d'options. Notre 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, on peut toujours utiliser le point pour dire : "n'importe quel caractère".

Prenons un exemple. Supposons qu'on ait stocké les IP de nos visiteurs dans une table "visiteurs" et qu'on veuille les noms des visiteurs dont l'ip commence par "84.254" (on obtiendra normalement uniquement des personnes qui sont chez Free) :
SELECT nom FROM visiteurs WHERE ip REGEXP 'ˆ84\.254(\.[0-9]{1,3}){2}$'
Ce qui 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 !
Si jamais cela nous est utile, normalement on devrait pouvoir se débrouiller.

Passons à une dernière notion importante : "Capture et remplacement".

Capture et remplacement

Les Regex, on vient de le voir, peuvent servir à faire une recherche puissante (avec les exemples du téléphone et du mail), mais aussi à faire une recherche / remplacement.
Cela peut servir par exemple à :
  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 e-mails cliquables !
Avec cette technique, on peut faire pareil pour rendre les liens http:// automatiquement cliquables eux aussi. On peut aussi, on va voir, créer son propre langage simplifié pour le visiteur, comme le fameux bbCode utilisé sur la plupart des forums ([b][/b] pour mettre en gras, ça dit quelque chose ? ;)).
Les parenthèses capturantes
Tout ce qu'on va voir maintenant tourne autour des parenthèses.
On s'est déjà servi des parenthèses pour entourer une partie de notre Regex et dire qu'elle devait se répéter 4 fois par exemple (comme avec le numéro de téléphone).
Ca, c'est la première utilité des parenthèses, mais elles peuvent aussi servir à autre chose !

On va travailler avec la fonction preg_replace à partir de maintenant.
C'est avec cette fonction qu'on va pouvoir réaliser ce qu'on appelle une "capture" de chaîne.
Ce qu'il faut savoir, c'est qu'à chaque fois qu'on met des parenthèses, ça va créer une "variable" contenant ce qu'elles entourent.
Explication avec une Regex : #\[b\](.+)\[/b\]#
Cela 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].
alt On met des antislash "\" devant les crochets pour ne pas que PHP les confonde avec des classes de caractères (comme [a-z])
Normalement, si on réfléchit 2 secondes, on se dit 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, bien retenir 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 se servira ensuite de ces variables pour modifier la chaîne (faire un remplacement).

Sur la Regex de dessus, il y a une seule parenthèse. 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.
Voici comment on fait pour mettre en gras tous les mots compris entre des [b][/b] :
Code PHP
<?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, à part qu'il faut bien garder en tête que chaque parenthèse va créer une variable ($1, $2 etc…)
    Ici, on a rajouté l'option "i" pour que le code fonctionne aussi avec des majuscules ([B][/B]).
  2. Ensuite, et c'est là qu'est la nouveauté, on indique le texte de remplacement : "<strong>$1</strong>" (rappel que <strong> permet de mettre en gras en HTML).
    Entre les balises HTML, on a 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 !
  3. Enfin, dernier paramètre, c'est le texte dans lequel on fait notre recherche / remplacement (ça on connaît 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 on shématise, ça donne :     preg_replace

Quelques règles à respecter qu'on va devoir apprendre :
  • Si on a plusieurs parenthèses, pour savoir le numéro d'une parenthèse il suffit de les compter dans l'ordre de gauche à droite. Par exemple :
    #(anti)co(nsti)(tu(tion)nelle)ment#
    Il y a 4 parenthèses dans cette regex (donc $1, $2, $3 et $4). La parenthèse numéro 3 ($3) contient "tutionnelle", et la parenthèse $4 contient "tion".
    alt Ne pas oublier que c'est l'ordre dans lequel les parenthèses sont ouvertes qui est important.
  • On peut utiliser jusqu'à 99 parenthèses capturantes dans une Regex (ça laisse de la marge ). Ca va donc jusqu'à $99.
  • Une variable $0 est toujours créée, elle contient toute la Regex. Si on reprend l' exemple précédent :
    #(anti)co(nsti)(tu(tion)nelle)ment#, $0 contient "anticonstitutionnellement".
  • Si, par hasard, on ne veut pas qu'une parenthèse soit capturante (pour faciliter les comptes, ou parce qu'on a 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 3 variables (4 si on compte $0) : $0 (anticonstitutionnellement), $1 (anti), $2 (tutionnelle), $3 (tion).
Créer son bbCode
Maintenant, on peut passer à la pratique et apprendre à se servir des parenthèses capturantes.

On va réaliser ce qu'on appelle un parser (prononcer "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 son 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).
On a 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 qu'on ne connait pas, qui signifie "Ungreedy" ("pas gourmand"). Passons les explications un peu complexes sur son fonctionnement, mais il faut savoir que, grosso modo, ça ne marcherait pas correctement s'il y avait plusieurs [b] dans notre 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 PHP
<?php
$texte = preg_replace('#\[b\](.+)\[/b\]#isU', '<strong>$1</strong>', $texte);
$texte = preg_replace('#\[i\](.+)\[/i\]#isU', '<em>$1</em>', $texte);
?>
Comme on peut voir, c'est quasiment pareil pour [b] et [i] (à part que la balise HTML qu'on utilise est <em>).

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 :
  1. 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).
  2. 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 PHP
<?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. On peut essayer avec les autres couleurs aussi !

Bon maintenant, je veux que les liens "http://" soient automatiquement transformés en liens cliquables.
Voici le résultat :
Code PHP
<?php
$texte = preg_replace('#http://[a-z0-9._/-]+#i', '<a href="$0">$0</a>', $texte);
?>
alt Dans le texte de remplacement, on a utilisé $0 qui, si on se souvient 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 (essayer avec U, on verra que le lien s'arrête à la première lettre !)

On remarquera qu'on a fait simple pour cette Regex. On aurait pu la faire plus complexe et plus précise.
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
Donc, on peut améliorer la Regex.

Pour finir, en cadeau bonus :
Code PHP
<?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. Admirer ! :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>

Quelques idées de Regex qu'on pourrait associer au parser :
  1. Je dit plus haut, 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
  2. On devrait aussi parser les adresses e-mail, en faisant un lien "mailto:" dessus !
  3. 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 on est allergique aux crochets, que pour nous [b] ne veut rien dire, on n'a qu'à inventer le code : {gras} {/gras}
  4. Et, si faire des Regex nous plaît, voivi un dernier défi qui devrait nous occuper un petit moment : écrire une fonction qui colore automatiquement le code HTML !
    On donne à 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…