Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

en replacement de filter_sanitize_regexp

7 réponses
Avatar
Olivier Masson
Salut,

La fonction filter_sanitize_regexp n'existe pas (je dois dire que vu le
peu de possibilité, j'ai du mal à saisir l'intérêt des filter_*).

Je voulais simplement filtrer les caractères d'une chaine qui sont dans
une expression régulière.
En fait, j'ai une fonction qui peut soit valider, soit filtrer... comme
filter_* en fait, mais avec des filtres que j'ai prédéfini.
Or, ma fonction a un paramètre $filtre qui, s'il est TRUE, efface les
caractères qui ne colle pas avec la regexp.

J'ai réussi ce que je voulais faire, mais ça m'étonnerait qu'il n'existe
pas plus simple. J'ai eu beau chercher et tenter d'utiliser un peu
toutes les fonctions PHP dispo (sauf preg_filter qui n'est que pour >=
5.3), mais rien ne va.

Donc ça donne ça :

function regexp($type, $value, $filter=false){
switch($type) {
case "email":
$regex="/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/";
break;
case "md5":
$regex="/^[0-9a-f]{32}+$/";
break;
}
/* etc. Toutes mes def ; j'ai fait un switch comme j'aurais pu faire
autre chose */

$regex = preg_replace('`^\/\^`', '/', $regex, 1);
$regex = preg_replace('`\$\/`', '/', $regex, 1);

if(preg_match_all($regex, $value, $r)) {
$chaine = '';
foreach($r[0] as $value) {
echo '<p>'.$value.'</p>';
$chaine .= $value;
}
return $chaine;
}
}
else {
if (preg_match($regex, $value)) return false;
else return $message;
}
}


C'est super bourrin, notamment la concaténation de la sortie du
preg_match_all.

Autre chose : je n'ai pas trop compris pourquoi dans mon preg_replace je
ne pouvais pas simplement mettre preg_replace('`\$$`', '', $regex, 1)
(idem pour le ^).

Si vous avez pigé qq chose à mes explications, y a-t-il quelque chose de
moins moche ?

7 réponses

Avatar
YBM
Olivier Masson a écrit :
case "email":
$regex="/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,4}$/";



.[...]{2,4}$ ? et .museum alors ?
Avatar
Olivier Miakinen
Le 18/12/2009 22:45, Olivier Masson a écrit :

La fonction filter_sanitize_regexp n'existe pas



Le filtre FILTER_SANITIZE_REGEXP, tu veux dire ? Je ne vois pas bien ce
que pourrait faire un tel filtre vu que pratiquement la totalité des
caractères Unicode peut se trouver dans une regexp.

(je dois dire que vu le
peu de possibilité, j'ai du mal à saisir l'intérêt des filter_*).



Euh... Ok pour le MD5, mais en ce qui concerne les adresses de courriel
tu as bien FILTER_VALIDATE_EMAIL et FILTER_SANITIZE_EMAIL, qui sont
certainement bien supérieures à l'immense majorité des tests que l'on
trouve sur la toile.

Je voulais simplement filtrer les caractères d'une chaine qui sont dans
une expression régulière.
En fait, j'ai une fonction qui peut soit valider, soit filtrer... comme
filter_* en fait, mais avec des filtres que j'ai prédéfini.
Or, ma fonction a un paramètre $filtre qui, s'il est TRUE, efface les
caractères qui ne colle pas avec la regexp.



Pour n'importe quelle regexp ? Hum... J'aimerais bien voir comment il se
débrouille avec la suivante :
$regexp = '/^([a-z])(?!1).$/';

Cette regexp valide toute chaîne de deux caractères dont le premier est
une lettre minuscule entre a et z, et le second est n'importe quoi
*sauf* la première lettre.

J'ai réussi ce que je voulais faire, mais ça m'étonnerait qu'il n'existe
pas plus simple. J'ai eu beau chercher et tenter d'utiliser un peu
toutes les fonctions PHP dispo (sauf preg_filter qui n'est que pour >=
5.3), mais rien ne va.

Donc ça donne ça :

function regexp($type, $value, $filterúlse){
switch($type) {
case "email":
$regex="/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,4}$/";



C'est curieux : tu contrôles l'absence de deux « . » successifs dans la
partie droite, mais pas dans la partie gauche. Par ailleurs, rien
n'interdit d'avoir un chiffre ou un trait d'union dans le TLD, et il
existe déjà des TLD de plus de 4 caractères.

Mais bon, déjà tu acceptes le « + » dans la partie gauche, ta règle ne
fait donc pas partie de la majorité des plus nulles.

Je rappelle à tout hasard que j'ai déjà parlé de ça dans la FAQ :
<http://faqfclphp.free.fr/#rub5.3>.

Oh, je rectifie ce que j'ai dit plus haut : tu acceptes aussi d'avoir
plusieurs « . » de suite dans la partie droite, la seule restriction
concernant le nombre de lettres après le dernier !

break;
case "md5":
$regex="/^[0-9a-f]{32}+$/";



Est-ce qu'il ne serait pas plus rapide de faire un strcspn avec la
chaîne "0123456789abcdef" suivi d'un strlen ? Ce n'est pas forcément le
cas, hein, je me pose juste la question.

break;
}
/* etc. Toutes mes def ; j'ai fait un switch comme j'aurais pu faire
autre chose */

$regex = preg_replace('`^/^`', '/', $regex, 1);
$regex = preg_replace('`$/`', '/', $regex, 1);



Note que tu n'as pas besoin de protéger le / puisque tu ne l'as pas
choisi comme délimiteur :
$regex = preg_replace('`^/^`', '/', $regex, 1);
$regex = preg_replace('`$/`', '/', $regex, 1);

Par ailleurs, comme tu ancres tes deux expressions (enfin... tu as
oublié pour la deuxième, mais en tout cas tu sembles espérer qu'il
prendra la dernière occurrence et donc la seule), il n'est pas
nécessaire de limiter le nombre de remplacements :
$regex = preg_replace('`^/^`', '/', $regex);
$regex = preg_replace('`$/$`', '/', $regex);

À tout hasard, quoique ça n'ajoute pas forcément de la lisibilité ici,
tu peux le faire en une seule passe :
$regex = preg_replace(array('`^/^`', '`$/$`'), '/', $regex);

Cela dit, ce qui me semblerait plus lisible est ce qui suit (à supposer
que la regexp commence et finit bien par '/^' et '$/' respectivement) :
$regex = substr_replace($regex, '/', 0, 2);
$regex = substr_replace($regex, '/', -2, 2);
Voire :
$regex = substr_replace($regex, '', 1, 1);
$regex = substr_replace($regex, '', -2, 1);
Ou encore :
$regex = '/' . substr($regex, 2, -2) . '/';

if(preg_match_all($regex, $value, $r)) {
$chaine = '';
foreach($r[0] as $value) {
echo '<p>'.$value.'</p>';
$chaine .= $value;
}
return $chaine;
}
}
else {
if (preg_match($regex, $value)) return false;
else return $message;
}
}



Je demande un peu de temps pour comprendre la fin de ce code...

Autre chose : je n'ai pas trop compris pourquoi dans mon preg_replace je
ne pouvais pas simplement mettre preg_replace('`$$`', '', $regex, 1)



Ben... parce que le caractère $ n'est pas le dernier de la chaîne ! Mais
bien sûr, si tu n'as qu'un seul caractère $ ceci devrait fonctionner :
preg_replace('`$`', '', $regex)

(idem pour le ^).



C'est ici que le paramètre limit à 1 peut servir ; que le caractère ^
soit ou non tout seul, celui que tu veux virer est le premier :
preg_replace('`^`', '', $regex, 1)

Si vous avez pigé qq chose à mes explications, y a-t-il quelque chose de
moins moche ?



Oui : adapter ton test avec $filter=TRUE au cas par cas, plutôt que
d'essayer de deviner comment faire le SANITIZE à partir du VALIDATE !
Mais bon, j'ai encore à comprendre la fin de ton code.

--
Olivier Miakinen
Avatar
Olivier Masson
Le 20/12/2009 00:28, Olivier Miakinen a écrit :

Le filtre FILTER_SANITIZE_REGEXP, tu veux dire ? Je ne vois pas bien ce
que pourrait faire un tel filtre vu que pratiquement la totalité des
caractères Unicode peut se trouver dans une regexp.




??? FILTER_VALIDATE_REGEXP n'est pas là pour valider une regexp mais
bien pour valider des données en fonction d'un regexp. Je cite "Valide
une valeur avec une expression rationnelle regexp"
Eh bien pareil pour FILTER : un filtre qui supprimerait tout ce qui ne
match pas la regexp.

(je dois dire que vu le
peu de possibilité, j'ai du mal à saisir l'intérêt des filter_*).



Euh... Ok pour le MD5, mais en ce qui concerne les adresses de courriel
tu as bien FILTER_VALIDATE_EMAIL et FILTER_SANITIZE_EMAIL, qui sont
certainement bien supérieures à l'immense majorité des tests que l'on
trouve sur la toile.




Youpi. Voilà le seul qui sert effectivement à quelque chose.

Pour n'importe quelle regexp ? Hum... J'aimerais bien voir comment il se
débrouille avec la suivante :



Ça ne fonctionne effectivement pas *du tout* avec toutes les regexp (pas
testé la tienne) et c'est bien pour ça que j'aurais aimé un
FILTER_SANITIZE_REGEXP !


Oh, je rectifie ce que j'ai dit plus haut : tu acceptes aussi d'avoir
plusieurs « . » de suite dans la partie droite, la seule restriction
concernant le nombre de lettres après le dernier !



L'erreur pour les domaines en 6 lettres, ok, je veux bien (ce n'était
pas le propos du fil ; heureusement que j'ai pas posté la douzaine de
regexp qu'il y a dans le code !). Pour le reste, c'est absolument
volontaire. J'avais une regexp très complète (celle officielle tout
simplement) mais je n'en veux plus car je *ne* souhaite *pas* avoir
certains caractères farfelus (pour des raisons que je ne vais pas
exposer puisque ce n'était pas du tout le propos de ce fil).


break;
case "md5":
$regex="/^[0-9a-f]{32}+$/";



Est-ce qu'il ne serait pas plus rapide de faire un strcspn avec la
chaîne "0123456789abcdef" suivi d'un strlen ? Ce n'est pas forcément le
cas, hein, je me pose juste la question.




Et après ça se moque quand on compare "" à '' :)
Ici, le but est la concision.


Voire :
$regex = substr_replace($regex, '', 1, 1);
$regex = substr_replace($regex, '', -2, 1);
Ou encore :
$regex = '/' . substr($regex, 2, -2) . '/';




Oui, tu as raison.


Oui : adapter ton test avec $filter=TRUE au cas par cas, plutôt que
d'essayer de deviner comment faire le SANITIZE à partir du VALIDATE !
Mais bon, j'ai encore à comprendre la fin de ton code.




C'est dommage mais ça semble être la seule possibilité.
Je ne sais pas ce que tu essais de comprendre. La fonction renvoie un
message d'erreur (traduit selon la langue), sinon c'est que c'est ok.
Avatar
Olivier Miakinen
Salut !

Ça y est, j'ai compris la fin du code. J'ai deux critiques, une sur la
forme et une sur le fond.


Sur la forme d'abord :

Le 18/12/2009 22:45, Olivier Masson a écrit :

if(preg_match_all($regex, $value, $r)) {
$chaine = '';
foreach($r[0] as $value) {
echo '<p>'.$value.'</p>';
$chaine .= $value;
}
return $chaine;
}



Attention, il y a une accolade fermante en trop ici.

}
else {
if (preg_match($regex, $value)) return false;



ÀMHA, ce preg_match est complètement inutile. En effet, pour qu'il serve
à quelque chose il faudrait qu'il retourne 1 alors que le preg_match_all
qui précède aurait retourné 0 ou FALSE, or je ne vois pas comment le
preg_match pourrait retourner autre chose que 0 ou FALSE dans les mêmes
conditions.

else return $message;
}
}




Maintenant sur le fond. Ton SANITIZE généré automatiquement à partir du
VALIDATE me semble inapproprié. Prenons par exemple le cas du MD5 :
$regex="/^[0-9a-f]{32}+$/";

Un SANITIZE devrait logiquement supprimer tous les caractères autres que
des chiffres hexa et rien que ceux-là. Supposons une série de 128
chiffres hexa représentant les 64 valeurs de 00 à 3f, et supposons que
le formatage (par exemple dans un courriel) place 40 chiffres hexa par
ligne :

000102030405060708090a0b0c0d0e0f10111213
1415161718191a1b1c1d1e1f2021222324252627
28292a2b2c2d2e2f303132333435363738393a3b
3c3d3e3f

On doit s'attendre logiquement à ce que le SANITIZE donne ceci :
000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f

Or, en réalité, le tien donnera cela :
000102030405060708090a0b0c0d0e0f1415161718191a1b1c1d1e1f2021222328292a2b2c2d2e2f3031323334353637

Moi, j'appelle ça un bug... Et donc, j'en reviens à mon conseil
précédent : écrire le test de SANITIZE indépendamment du test de
VALIDATE au lieu d'essayer de déduire celui-là de celui-ci.


Cordialement,
--
Olivier Miakinen
Avatar
Olivier Masson
Le 20/12/2009 00:28, Olivier Miakinen a écrit :

$regex = '/' . substr($regex, 2, -2) . '/';




Au fait, non parce qu'il y a parfois des options. Mais qu'est-ce donc
que ce Olivier qui ne tient pas compte de toutes les possibilités
existantes ?
Avatar
Olivier Miakinen
Le 20/12/2009 20:24, Olivier Masson a écrit :

$regex = '/' . substr($regex, 2, -2) . '/';




Au fait, non parce qu'il y a parfois des options.



Ah, donc ce n'était pas un oubli de ne pas ancrer la deuxième regexp !

Dans ce cas, remplace au moins le preg_replace par un str_replace :
$regex = str_replace('$/', '/', $regex);


--
Olivier Miakinen
Avatar
Olivier Masson
Le 20/12/2009 20:24, Olivier Miakinen a écrit :

Moi, j'appelle ça un bug... Et donc, j'en reviens à mon conseil
précédent : écrire le test de SANITIZE indépendamment du test de
VALIDATE au lieu d'essayer de déduire celui-là de celui-ci.





Oui, ça me semble une option plus réaliste. Je pensais que faire un
SANITIZE serait plus simple mais comme je bosse depuis 3j en étant
malade, j'ai un peu de mal...
Merci de ton aide.