Les filtres c'est comme les moulins : y'en a à café, et y'en a à paroleComment ça un filtre? Et oui, un filtre. La classe de la leçon précédente ne possède que des méthodes qui vont transformer un contenu, chaque méthode va appliquer une modification particulière. Prenez le cas d'un envoi de formulaire, vous savez bien qu'il faut toujours se méfier de ce que quelqu'un peut saisir dans un formulaire. Pour cette raison, il est nécessaire de pratiquer plusieurs opérations, et pas toujours les mêmes, sur chacun des champs pour se protéger de certains caractères spéciaux par exemple, ou d'un format de date inadapté, ou d'un texte un peu trop long, etc... Toutes ces opérations visant, non pas à vérifier la validité d'un contenu, mais à le transformer pour qu'il remplisse certaines conditions s'appellent des filtres. Et le regroupement de plusieurs filtres...s'appelle aussi un filtre.
Nous allons donc reprendre notre exemple précédent, et le rendre plus flexible et mieux pensé. Je vous préviens d'emblée, le code qui va en ressortir ne sera pas plus concis, ce n'est pas le but cette fois-ci. Si j'avais voulu être concis, j'aurais fait cela :
- Code:
-
echo '<div style="border:1px solid #DDDDDD; padding: 4px;">'.
nl2br(strip_tags(preg_replace('@<script[^>]*?>.*?</script>@si','',ucfirst(trim($texte))))).'
</div>';
Mais ce n'est pas du tout ce que l'on veut. Et en plus c'est pas super lisible et c'est loin d'être réutilisable... Non vraiment, c'est pas bien de faire comme ça. Le but de l'exercice c'est de mettre en place une méthode de définition et d'appels de filtres, de pouvoir décider d'en appliquer une série dans un ordre particulier pour tel champ, et une autre série pour tel autre champ.
Pour arriver à ce but, je vais donc créer une classe parente "MainFilter", et distinguer chaque type de filtre dans une classe enfant "FilterTypeDeFiltre". Ma classe MainFilter devra avoir deux méthodes : la première me permettra d'agréger d'autres filtres, et l'autre méthode, me permettant d'exécuter le filtre, et/ou ses enfants par la même occasion.
Nous allons également profiter de cet exemple pour introduire la notion d'interface, une interface est une sorte de classe abstraite possédant des méthodes sans implémentation. Concrètement on définit les méthodes que doivent implémenter les classes enfants, mais elles n'héritent pas de ses méthodes. L'implémentation d'une interface se fait grâce au mot-clef "implements". Nous allons l'utiliser ici pour indiquer qu'un filtre doit absolument posséder une méthode nommée doFilter() afin d'être certain que tous les filtres parlent le même langage.
Voici donc notre interface :
- Code:
-
interface FilterInterface
{
public function doFilter($toFilter);
}
C'est tout. Et maintenant la définition de ma classe mère.
- Code:
-
class MainFilter implements FilterInterface
{
// Ici je vais stocker les sous-filtres
public $filters = array();
// La méthode pour ajouter un sous-filtre
public function addFilter($filter)
{
array_push($this->filters,$filter);
// On peut chaîner cette méthode
return $this;
}
// La méthode qui exécute le filtre
public function doFilter($toFilter)
{
// Je liste tous mes objets filtres
foreach($this->filters as $filter)
{
// J'exécute la méthode doFilter() de chaque filtre
$toFilter = $filter->doFilter($toFilter);
}
return $toFilter;
}
}
Et quelques exemples de filtres basiques :
- Code:
-
// Conserve uniquement les caractères alphanumériques
class FilterAlphaNumeric implements FilterInterface
{
public function doFilter($toFilter)
{
return preg_replace('/[^a-zA-Z0-9\s]/','', $toFilter);
}
}
// Supprime les espaces
class FilterStripSpaces implements FilterInterface
{
public function doFilter($toFilter)
{
return str_replace(' ','', $toFilter);
}
}
// Transforme un texte en CamelCase vers un texte avec_des_underscores
class FilterCamelCaseToUnderscore implements FilterInterface
{
public function doFilter($toFilter)
{
return implode('_',array_map('strtolower',preg_split('/([A-Z][^A-Z]*)/', $toFilter, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)));
}
}
// Transforme le HTML en caractères spéciaux
class FilterHtmlEntities implements FilterInterface
{
public function doFilter($toFilter)
{
return htmlentities($toFilter);
}
}
// Un filtre numérique cette fois-ci qui arrondit à 2 zéros après la virgule et affiche avec une virgule et non un point
class FilterRound implements FilterInterface
{
public function doFilter($toFilter)
{
return number_format($toFilter,2,',',' ');
}
}
Et maintenant à l'utilisation voici comme cela se passe :
- Code:
-
$monFiltre = new MainFilter();
echo $monFiltre->addFilter(new FilterAlphaNumeric())
->addFilter(new FilterStripSpaces())
->addFilter(new FilterCamelCaseToUnderscore())
->doFilter('jePeuxMettreNImporteQuoi &"(§!CommeC°!%*$¨^a');
Comme vous pouvez le voir, à l'utilisation, on gagne tout de même un peu en lecture, à l'inverse, nous avons créé une librairie de classes qui peut paraître assez conséquente pour pas grand chose finalement. Mais le bénéfice de ce genre de méthodes est réel dès qu'on a traité un certain nombre de données...
Voyez plutôt, après avoir utiliser mon filtre pour un texte d'exemple si j'ai besoin de filtrer un autre texte je n'ai qu'à faire ceci :
- Code:
-
echo $monFiltre->doFilter('voiciUnAutreTexte');
C'est plutôt efficace au réemploi. Maintenant, on pourrait imaginer d'autres utilisations possibles.
Par exemple, créer un filtre standard qui en regroupe d'autres, comme celui que je viens de configurer.
- Code:
-
// Je créé donc un nouveau filtre qui cette fois-ci va hériter de MainFilter afin de pouvoir agréger d'autres filtres
class FilterClassNameToFileName extends MainFilter
{
public function __construct()
{
$this ->addFilter(new FilterAlphaNumeric())
->addFilter(new FilterStripSpaces())
->addFilter(new FilterCamelCaseToUnderscore());
}
}
Maintenant si je reviens sur l'utilisation, c'est encore plus simple qu'avant :
- Code:
-
$monFiltre = new FilterClassNameToFileName();
echo $monFiltre->doFilter('jePeuxMettreNImporteQuoi &"(§!CommeC°!%*$¨^a');
echo $monFiltre->doFilter('voiciUnAutreTexte');
Par la suite, on pourra également envisager d'autres utilisations possiblescomme par exemple une méthode permettant de passer directement un tableau de variables plutôt que de les passer une à une.
Mais ce n'était pas le but de cet exercice, cette méthode s'apparente plus au motif Collection que nous verrons un peu plus tard ;-)