Discussion:
en replacement de filter_sanitize_regexp
(trop ancien pour répondre)
Olivier Masson
2009-12-18 21:45:35 UTC
Permalink
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 ?
YBM
2009-12-19 18:22:47 UTC
Permalink
.[...]{2,4}$ ? et .museum alors ?
Olivier Miakinen
2009-12-19 23:28:27 UTC
Permalink
Post by Olivier Masson
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.
Post by Olivier Masson
(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.
Post by Olivier Masson
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.
Post by Olivier Masson
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.
function regexp($type, $value, $filter=false){
switch($type) {
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 !
Post by Olivier Masson
break;
$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.
Post by Olivier Masson
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) . '/';
Post by Olivier Masson
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...
Post by Olivier Masson
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)
Post by Olivier Masson
(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)
Post by Olivier Masson
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
Olivier Masson
2009-12-20 19:24:21 UTC
Permalink
Post by Olivier Miakinen
$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 ?
Olivier Miakinen
2009-12-20 22:05:05 UTC
Permalink
Post by Olivier Masson
Post by Olivier Miakinen
$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
Olivier Masson
2009-12-20 19:24:21 UTC
Permalink
Post by Olivier Miakinen
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.
Post by Olivier Miakinen
Post by Olivier Masson
(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.
Post by Olivier Miakinen
Pour n'importe quelle regexp ? Hum... J'aimerais bien voir comment il se
Ç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 !
Post by Olivier Miakinen
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).
Post by Olivier Miakinen
Post by Olivier Masson
break;
$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.
Post by Olivier Miakinen
$regex = substr_replace($regex, '', 1, 1);
$regex = substr_replace($regex, '', -2, 1);
$regex = '/' . substr($regex, 2, -2) . '/';
Oui, tu as raison.
Post by Olivier Miakinen
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.
Olivier Miakinen
2009-12-20 19:24:21 UTC
Permalink
Salut !

Ça y est, j'ai compris la fin du code. J'ai deux critiques, une sur la
forme et une sur le fond.
Post by Olivier Masson
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.
Post by Olivier Masson
}
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.
Post by Olivier Masson
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
Olivier Masson
2009-12-21 11:46:37 UTC
Permalink
Post by Olivier Miakinen
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.

Continuer la lecture sur narkive:
Loading...