Développement d'applications Web avec LAMP

Document d'accompagnement pour le cours 420-KB9-LG

Supplément pour projet de fin de session

Dernière mise à jour le 5 décembre 2022

Les expressions régulières en PHP

Les expressions régulières se retrouvent dans bon nombre de langages de programmation (vous les avez déjà utilisées en JavaScript). Elles sont utiles pour effectuer des recherches plus spécifiques sur un ensemble de caractères ou une chaîne de caractères.

D'accord, mais à quoi servent-elles exactement?

Elles sont idéales pour tester si une chaîne de caractères correspond à un motif particulier et rendre encore plus flexible la validation des données de formulaires.

Elles sont utilisées, entre autres choses, pour :

Syntaxe

La syntaxe des expressions régulières inclut l’utilisation de caractères spéciaux. Les caractères auxquels on donne une signification spéciale dans une expression régulière, sont : . * ? + [ ] ( ) { } ^ $ |.

Vous devrez échapper ces caractères chaque fois que vous voudrez les utiliser littéralement. Par exemple, si vous voulez faire correspondre ., vous devrez écrire \. Tous les autres caractères prennent automatiquement leur sens littéral.

Le tableau suivant décrit les regex les plus courants :

Motif Signification
^ Début de ligne ou de chaîne
$ Fin de ligne ou de chaîne
. N'importe quel caractère
x|y Caractère x ou y
[abc] Groupe de caractères : n'importe lequel de ceux entre crochets
[a-z] Groupe de caractères : n'importe lequel de a à z
[^0-9]Groupe de caractères : tous sauf ceux de 0 à 9
(x) Expression parenthésée (mémorisée)
* Caractère précédent de 0 à X fois
+ Caractère précédent de 1 à X fois
? Caractère précédent de 0 à 1 fois
{n} Caractère précédent exactement n fois
{n,} Caractère précédent au moins n fois
{,n} Caractère précédent au plus n fois
\ N'est pas un caractère, sert d'échappement
\\ Caractère \
\d Chiffre (équivalent à [0-9]
\D Sauf chiffres (équivalent à [^0-9]
\b Frontière de mot (espace, alinéa, ...)
\s Caractère d'espacement (espace, tabulation, saut de page, ...), équivalent à [ \f\n\r\t\v]
\S Un seul caractère, sauf un espacement
\w N'importe quel caractère alphanumérique, y compris souligné (_), équivalent à [A-Za-z0-9]
\W Tout sauf un caractère alphanumérique, équivalent à [^A-Za-z0-9_]

Délimiteur

Les principales fonctions PHP qui utilisent les expressions régulières commencent toutes par "preg". Ce sont preg_match(), preg_match_all(), preg_replace(), preg_grep() et preg_split().

La première et la plus importante chose à savoir sur les fonctions "preg" est qu’ils s’attendent à ce que vous encadriez les motifs de regex (REGulat EXpression) avec un caractère délimiteur de chaque côté.

Par exemple, si vous choisissez ~ comme délimiteur, pour le motif regex \b\w\w+\b, vous devrez donner la chaîne ~\b\w+\b~ à la fonction "preg".

Pour le délimiteur, vous pouvez choisir n’importe quel caractère à l’exception des espaces et des barres obliques inverses. Mais choisissez judicieusement, car si votre délimiteur apparaît dans le motif, il doit être échappé avec le caractère \.

Fonctions utilisant les expressions régulières

Le tableau suivant rassemble les principales fonctions PHP qui utilisent les expressions régulières.

Fonction Description
preg_match()

Recherche un motif dans la chaîne de caractères.

Retourne 1 si le motif existe, 0 sinon (et FALSE en cas d'erreur). La fonction arrête la recherche après avoir trouvé la première correspondance.

Exemple :

$chaine = "Un anneau pour les gouverner tous.";

$retour = preg_match("/ann/", $chaine);
echo $retour . "<br>";
// affiche 1

$retour = preg_match("/annn/", $chaine);
echo $retour . "<br>";
// affiche 0      
preg_match_all()

Effectue une recherche globale sur toutes les occurrences du motif dans la chaîne. La fonction continue la recherche jusqu’à la fin de la chaîne et trouve toutes les correspondances possibles au lieu de s’arrêter au premier résultat comme avec la fonction précédente.

Les correspondances sont rangées dans le tableau fourni en paramètre.

Exemple :

$chaine = "Un anneau pour les gouverner tous.";

$resultat = array();

$retour = preg_match_all("/ne[a-z]/", $chaine, $resultat);

print_r($resultat);
// affiche Array ( [0] => Array ( [0] => nea [1] => ner ) )       
preg_replace()

Effectue une recherche et un remplacement d’expressions régulières.

Exemple :

$chaine = "Un anneau pour les gouverner tous.";

$remplacement = "X";

$retour = preg_replace("/ne[a-z]/", $remplacement, $chaine);

echo $retour;
// affiche Un anXu pour les gouverX tous.       
preg_grep()

Fait une recherche parmi les éléments du tableau d’entrée, retournant tous les éléments correspondant à l'expression régulière dans un autre tableau (ou FALSE en cas d'erreur).

$tab = ["123", "qw45", "gh3", "65tt"];

$tab_retour = preg_grep("/[1-9]{2}/", $tab);

print_r($tab_retour);
// affiche Array ( [0] => 123 [1] => qw45 [3] => 65tt )       

On remarque que le tableau retourné utilise les clés du tableau d'entrée.

preg_split()

Divise ("éclate" ou "explose") une chaîne de caractères en un tableau de chaînes de caractères en utilisant une expression régulière.

Exemple :

$lotr = "Il y avait 3 anneaux pour les rois, 7 pour les nains et 9 pour les hommes.";

$tab_retour = preg_split("/[1-9]/", $lotr);

echo "<pre>";
foreach ($tab_retour as $chaine) {
  echo "[ " . $chaine . "]<br>";
}
echo "</pre>";      

Sortie :

[ Il y avait ]
[  anneaux pour les rois, ]
[  pour les nains et ]
[  pour les hommes.]    

Consultez la documentation officielle pour en savoir plus sur l'utilisation de ces fonctions.

Exemple de validation d'une URL

Voici le code nécessaire pou valider une URL :

$url = "https://prog101.com/cours/kb9/";
if (preg_match('/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:.[A-Z0-9][A-Z0-9_-]*)+):?(d+)?\/?/i', $url)) {
    echo "URL valide";
} else {
    echo "URL invalide";
}

D'accord, c'est un peu lourd. Il y a peut-être d'autres moyens...

La fonction filter_var()

La fonction filter_var() filtre une variable à l'aide d'un filtre spécifique.

Exemple :

$url = filter_var("https://prog101.com/cours/kb9/", FILTER_VALIDATE_URL);

if (! $url) {
  echo "La chaîne n'est pas une URL valide";
}

Plus simple n'est-ce pas?

Il existe une dizaine de filtres de validation :

FiltreRetourne TRUE si est...
FILTER_VALIDATE_BOOLEAN
FILTER_VALIDATE_BOOL
un booléen
FILTER_VALIDATE_DOMAINun nom de domaine
FILTER_VALIDATE_EMAILune adresse courriel
FILTER_VALIDATE_FLOATun nombre à virgule flottante
FILTER_VALIDATE_INTun nombre entier
FILTER_VALIDATE_IPune adresse IP
FILTER_VALIDATE_MACune adresse MAC
FILTER_VALIDATE_REGEXPune expression régulière
FILTER_VALIDATE_URLune URL

Il existe aussi des filtres de nettoyage. Consultez la documentation pour en savoir plus.

En guise de résumé

Appliquez ce traitement à tous les intrants de vos applications Web :


[client]
Validation du formulaire
(validation HTML / JavaScript)

[serveur]
Nettoyage et validation des paramètres
(PHP, expressions régulières, filtres, etc.)

[base de données]
Requêtes préparées ou procédures stockées

Et ne faites jamais confiance à l'utilisateur!

Exploits of a Mom
Exploits of a Mom

Nettoyer les commentaires (PFI - Livrable 3)

Si vous ne voulez pas voir toutes sortes de choses dans vos commentaires, comme des images ou des messages écrits en caractères géants, vous devez en filtrer le contenu. Et cette fois, il ne s'agit pas d'éviter les injections SQL, mais simplement de filtrer les balises HTML.

Ici deux approche sont possibles :

  1. Permettre l'utilisation de balises HTML pour agrémenter le texte. Par exemple, autoriser le texte en caractères gras ou italiques. Pour cela vous pouvez faire appel à la fonction strip_tags (déjà présentée) qui permet de filtrer l'ensemble des balises HTML ou d'en autoriser certaines.

  2. Permettre l'affichage de balises HTML à des fins explicatives. Par exemple, si un commentaire fait référence à la balise <header>, n'est-il pas approprié de pouvoir l'écrire comme tel, entourée des caractères < et >? On y arrive en faisant appel à la fonction htmlentities.

    Exemple avec une balise de titre :

    <?php
    
      // la balise <h2> sera interprétée par le navigateur
      $titre  = '<h2>Ceci est un titre de niveau 2</h2>';
      echo $titre;
    
      // la balise <h2> ne sera pas interprétée
      $titre = htmlentities('<h2>Ceci est un titre de niveau 2</h2>');
      echo $titre;
        

    Sortie :

    Ceci est un titre de niveau 2

    <h2>Ceci est un titre de niveau 2</h2>