[php5 - dom - xpath] classe de pre-filtrage de texte

Soyez le premier à donner votre avis sur cette source.

Vue 7 181 fois - Téléchargée 140 fois

Description

Cette classe calcul un pourcentage d'apparition de certains mots pre-enregistre dans une base de donnees au format xml.
Les mots sont classes par categorie et la classe permet d'en ajouter ou d'en creer de nouvelles (pas d'effacement possible pour l'instant).
On peut utiliser cette classe pour valider une reponse dans un forum par exemple en recherchant le nombre de mots present dans la categorie 'insultes' (non presente dans le code, a creer vous meme donc) ou dans la categorie 'spamm' (avec des mots genre viagra, credits, financement... j'en passe et des meilleurs).
La fonction a utiliser est la fonction Filtre::Anaylse() qui renvoi un tableau avec pour cle le nom de chaque categorie et pour valeur le pourcentage d'apparition des mots presents dans chaque categorie (je sais pas si j'ai ete clair). La validation du texte transmis sera par contre gere par vos soins.

Le code est loin d'etre parfait (pas mal de boucle foreach() ) mais le temps d'execution reste bon, pas de probleme donc logiquement pour l'integration de cette classe dans vos projets. Un tableau de code d'erreurs est disponible mais etant donne qu'il n'y a que 2 focntions (en plus du constructeur), la gestion d'erreur est un peu particuliere.
J'ai essaye de commenter le code le plus possible mais n'hesitez pas a me demander de l'aide ou des renseignements, je le ferai avec plaisir :-)

Source / Exemple :


<?php
	/*
		|------------|
		| MODELE XML |
		|------------|
		
		<filtre>
			<cat1>
				<value>mot1</value>
				<value>mot2</value>
				<value>mot3</value>
				<value>mot4</value>
			</cat1>
			<cat2>
				<value>mot1</value>
				<value>mot2</value>
				<value>mot3</value>
				<value>mot4</value>
			</cat2>
			<cat3>
				<value>mot1</value>
				<value>mot2</value>
				<value>mot3</value>
				<value>mot4</value>
			</cat3>
		</filtre>		
		
		Auteur : IoNAce (jean84)
		Version PHP : 5
		Lib utilise : DOM & XPATH (incluses par defaut dans php5)
		Version classe : 1.4

  • /
class Filtre { /**********************/ /* Nom du fichier XML */ /**********************/ private $File; /*************************/ /* Handle du fichier XML */ /*************************/ private $Handle; /*********************************************/ /* Tableau d'erreurs renvoyees par la classe */ /*********************************************/ private static $TableOfErrors = array( 'CANT_CREATE_XML_FILE', 'CANT_LOAD_XML_FILE', 'LOAD_SUCCESS', 'CREATE_SUCCESS', 'CANT_OPEN_FILE', 'CANT_READ_FILE', 'CANT_SAVE_XML_FILE', 'NO_ERROR', 'CANT_SEPARATE_STRING' ); /********************************************************************************/ /* Rapport d'erreur de la classe */ /* A consulter pour connaitre le resutlat de certaines actions du constructeur */ /********************************************************************************/ public $LastError; /****************************************************************/ /* Constructeur de la classe */ /* Param 1 : $sNameOfFile */ /* -> Nom et path du fichier xml a charger (ou a creer) */ /****************************************************************/ function __construct($NameOfFile) { // Creation d'un nouvel handle $this->Handle = new DOMDocument('1.0', 'iso-8859-1'); $this->Handle->formatOutput = TRUE; // Si le fichier n'existe pas, on le cree if ( !file_exists((string)$NameOfFile) ) { $HandleDoc = $this->Handle; // Creation du noeud racine $Racine = $HandleDoc->createElement('filtre'); $Racine = $HandleDoc->appendChild($Racine); // On sauvegarde le fichier if ( $HandleDoc->save($NameOfFile) === FALSE ) $this->LastError = $this->TableOfErrors[0]; else $this->LastError = $this->TableOfErrors[3]; } else { // Sinon on le charge if ( !$this->Handle->load($NameOfFile) ) $this->LastError = $TableOfErrors[1]; else $this->LastError = $this->TableOfErrors[2]; } $this->File = $NameOfFile; } /************************************************************************************************************/ /* Fonction recherchant les mots de $LenWords caracteres afin de les enregistrer dans le fichier xml */ /* La fonction decoupe les mots grace aux espaces. Pensez donc a les inclures ! */ /* Param 1 : $String */ /* -> Chaine de caractere ou recuperer les mots */ /* Param 2 : $LenWords */ /* -> Nombre de caractere minimum par mot (si = 0, on recupere tout. Attention donc !) */ /* Param 3 : $Cat */ /* -> Nom de la categorie definissant le type de mots a enregistre */ /* Param 4 : $IsFile */ /* -> Si = TRUE, $String est le nom d'un fichier qu'il faudra lire en premier */ /* -> Par defaut, la valeur est mise a FALSE pour faire simple */ /* Attention a ne pas creer de categorie nommee 'foo' ou 'final', reserve a la classe ! */ /************************************************************************************************************/ public function SaveWords($String, $LenWords, $Cat, $IsFile=FALSE) { // Lecture du fichier if ( $IsFile ) { $File = fopen($String, 'r'); if ( $File === FALSE ) return $this->TableOfErrors[4]; $Buffer = fread($File, filesize($String)); if ( $Buffer === FALSE ) return $this->TableOfErrors[5]; fclose($File); $String = $Buffer; } $Compteur = 0; $Table = array(); $String = $this->Clean($String); $SubString = explode(' ', $String); if ( $SubString === FALSE ) return $this->TableOfErrors[8]; $NbrCases = count($SubString); for ( $i=0; $i<$NbrCases; $i++ ) { if ( strlen($SubString[$i]) >= $LenWords ) { if ( !empty($SubString[$i]) ) { $Table[$Compteur] = strtolower($SubString[$i]); $Compteur += 1; } } } // Recherche de la balise <$Cat> dans le fichier xml $XPath = new DOMXPath($this->Handle); $Query = "boolean(/filtre/$Cat) = false"; $Result = $XPath->evaluate($Query); $Handle = $this->Handle; $Handle->formatOutput = TRUE; if ( $Result === TRUE ) { // Recupere la racine $Racine = $Handle->getElementsByTagName('filtre')->item(0); $Categorie = $Handle->createElement($Cat); $Categorie = $Racine->appendChild($Categorie); // Sauvegarde du fichier if ( $Handle->save($this->File) === FALSE ) return $this->TableOfErrors[6]; } // Recupere la balise <$Cat> $Racine = $Handle->getElementsByTagName($Cat)->item(0); // On ajoute les elements foreach ( $Table as $Key => $Value ) { $Value = soundex($Value); // Verifie que le mot n'est pas deja enregistre $Query = '/filtre/*/word[@value="'.$Value.'"]'; $Result = $XPath->query($Query); if ( empty($Result->item(0)->nodeValue) ) { $BaliseElement = $Handle->createElement('word'); $BaliseElement = $Racine->appendChild($BaliseElement); $BaliseElement->setAttribute('value', $Value); } } // Sauvegarde du fichier if ( $Handle->save($this->File) === FALSE ) return $this->TableOfErrors[6]; else return $this->TableOfErrors[7]; } /****************************************************************************************************************************/ /* Cette fonction calcule (en pourcentage) le taux d'apparition de chaque mot et place le resultat dans un tableau. */ /* Le tableau renvoye index ses cases avec le nom des categories et le pourcentage de mots de cette catehorie comme valeur */ /* Param 1 : $String */ /* -> Texte a analyser */ /* Param 2 : $Cat */ /* -> Precise la categorie ou rechercher. Dans ce cas, le pourcentage ne sera calcule que pour cette categorie */ /* -> /!\ Si la categorie n'existe pas, le pourcentage sera toujours = 0 */ /* Dans le tableau renvoye, il y a une case speciale appellee 'foo' qui contient le nombre de mots trouvees par la fonction.*/ /* Permet de pouvoir apprehender le resultat final. Attention donc a ne pas creer de categorie 'foo' sous peine de voir le */ /* resultat errone. Une autre case appelle 'final' contient la somme de tous les pourcentages calcules jusque la. */ /****************************************************************************************************************************/ public function Analyse($String, $Cat=NULL) { // Preparation d'une instance de la classe DOMXPath $XPath = new DOMXPath($this->Handle); // Compteur de mots $Comtpeur = 0; // Preparation du tableau $Table = array(); if ( $Cat === NULL ) { $Query = '/filtre/*'; $Result = $XPath->query($Query); foreach ( $Result as $Key => $Value ) $Table[$Value->nodeName] = 0; } else { $Table[$Cat] = 0; } // Cree une case 'final' qu'on fait partir a 0 $Table['final'] = 0; $String = $this->Clean($String); // Exctraction des mots $SubString = explode(' ', $String); if ( $SubString === FALSE ) return $this->TableOfErrors[8]; $NbrCases = count($SubString); for ( $i=0; $i<$NbrCases; $i++ ) { if ( !empty($SubString[$i]) ) { $SubString[$i] = soundex($SubString[$i]); // On verifie que ce mot est enregistre dans une categorie (celle precise ou toutes) if ( $Cat === NULL ) $Query = '/filtre/*/word[@value="'.$SubString[$i].'"]'; else $Query = '/filtre/'.$Cat.'/word[@value="'.$SubString[$i].'"]'; $Result = $XPath->query($Query); if ( !empty($Result->item(0)->nodeValue) ) { // Si c'est le cas, on cherche les categories foreach ( $Table as $Key => $Value ) { $Query = '/filtre/'.$Key.'/word[@value="'.$SubString[$i].'"]'; $Result = $XPath->query($Query); // Si la categorie en cours contient bien la valeur obtenue if ( !empty($Result->item(0)->nodeValue) ) { $Table[$Key] += 1; break; } } } $Compteur += 1; } } // Calcul des pourcentages foreach ( $Table as $Key => $Value ) { if ( $Key != 'final' ) { if ( $Value != 0 ) $Table[$Key] = round((($Value * 100) / $Compteur), 2); $Table['final'] += $Table[$Key]; } } // Cree une case 'foo' permettant a l'utilisateur de conaitre le nombre de mots pris en compte par la fonction $Table['foo'] = $Compteur; // Retourne le tableau return $Table; } /************************************************/ /* Enleve la plupart des signes de ponctuation */ /* Param 1 : $Value */ /* -> Chaine a nettoyer */ /************************************************/ private function Clean($Table) { $Array = array(',', '!', ',', ';', '?', '*', '+', '-', '/', '²', '=', ')', '(', '{', '}', '"', '\'', '\\', '`', ':', ' '); return str_replace($Array, '', $Table); } } ?>

Conclusion :


Pas de bugs connus (je pense avoir fait le tour) mais pas de probleme pour les mises a jours si jamais vous en trouvez. Idem pour les fonctions a rajouter (je pense que je vais integre la possibilite d'effacer certains mots enregistres ou certaines categories, suivant les besoins).

Pour le niveau (mis en initie), j'ai fait ce choix car j'utilise XPath et DOM (de maniere simple). Que les debutants ne se frustrent pas, le code est tres accessible (de mon point de vue c'est vrai mais normalement y a pas trop de difficulte) et je reste de toute facon disponible.

En esperant que cela vous plaise :p

@++

Codes Sources

A voir également

Ajouter un commentaire

Commentaires

cs_jean84
Messages postés
450
Date d'inscription
jeudi 26 août 2004
Statut
Membre
Dernière intervention
5 mars 2009
-
Voila alors le code a ete mis a jour.
Petit plus : la fonction Filtre::Analyse() renvoi toujours un tableau sauf que maintenant, 2 cases sont predefinies, il s'agit de 'foo' et 'final'. 'foo' est une case qui contient le nombre de mots trouves par la classe, 'final' est la somme de tous les pourcentages calcules par la classe (ce qui permet de savoir connaitre le pourcentage final de mots interdits dans une phrase sans en avoir fait le calcul)

@+
cs_jean84
Messages postés
450
Date d'inscription
jeudi 26 août 2004
Statut
Membre
Dernière intervention
5 mars 2009
-
Alalala... j'avais raison ! Comme d'hab tu m'en apporte plus qu'il ne m'en faut... ^^

J'ai trouve tes classes et j'ai lu les topics aussi j'avais quelques questions :
- je vais coder ma propre implementation de soundex (non pas que la tienne n'est pas bonne au contraire mais je desire comprendre).
Voila ce que j'ai trouve sur php.net
Soundex()
=> http://www.php.net/manual/fr/function.soundex.php
Levenshtein()
=> http://www.php.net/manual/fr/function.levenshtein.php
Metaphone()
=> http://www.php.net/manual/fr/function.metaphone.php

Je comprend l'interet de soundex() et metaphone() mais je ne comprend pas levenshtein. L'algo mathematique me donne mal a la tronche et l'exemple ne me parle pas franchement. Tu pourrais m'expliquer STP ?

"OUUUUUUUUUF!! ^^"
J'imagine bien ton soulagement ;-)

Je crois que je vais recoder ma classe en implementant au moins la focntion soundex histoire d'etre un peu plus precis.

A bientot ;-)
malalam
Messages postés
10843
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
17 -
"Donc en conclusion, je dirais que cette classe n'a pas vocation de bot virtuel et qu'elle ne remplacera jamais le travail d'un bon admin sur un forum"

OUUUUUUUUUF!! ^^

Pour le reste, je sais...je disais juste ça comme ça ;-) On ne peut pas, dans ce domaine, trouver LA parade. Pas encore en tous cas.

A propos de phonétique,
"J'ai voulu egalement utilise la phonetique pour pallier ce genre de probleme mais je n'ai pas eu le temps de poussees mes recherches",
fouille mes sources...;-)
cs_jean84
Messages postés
450
Date d'inscription
jeudi 26 août 2004
Statut
Membre
Dernière intervention
5 mars 2009
-
Malalam >
"juste pour dire que je testerai ta classe et la lirai en détail"
J'esperai vraiment que tu me dises sa, tes conseils m'on toujours ete utile ! Tiens moi au courant :p

"ce qui manquera de toutes façons à un tel code, c'est prévois les erreurs humaines [...] Et puis, les internautes récalcitrants s'amuseront aussi : mairde! ou merdeuh"
J'en ai bien conscience aussi j'ai pris soins de virer tout signes de ponctuation de chaque mot extrait. La liste est modifiable a souhait, on l'a reconnait du premier coup d'oeil ;-). J'ai voulu egalement utilise la phonetique pour pallier ce genre de probleme mais je n'ai pas eu le temps de poussees mes recherches. Je sais que mysql le gere mais je ne voulais pas utiliser une base sql. Je continuerai mes recherches un peu plus tard car j'ai pas mal de taf a la bourre mais promis je garderai l'idee jusqu'au moment venu.
Pour tous les trucs genre "merdeuh", il faut d'abord comprendre le contexte. J'ai code cette classe pour un site qui se fait constament spamme par des internautes vraiment pas tres intelligent. Des membres du site du zero pense que nous avons copie leurs sites (http://www.inzeprog.info) et nous recevions (le livre d'or a ferme) beaucoup de messages du style : bande de copieur, vous etes trop null ect...
Les messages ete globalement les memes donc une telle protection aurait ete largement suffisante. Ensuite, j'aurais beau travailer a la nasa, je ne serai jamais a l'abri de ce genre de contours et personne d'ailleurs ne le sera, il faut se l'avouer ;-)
C'est pour cela que j'ai qualifie ce code de "pre-filtrage". Il ne fera qu'informer les webmasters qu'un message est potentiellement non conforme a l'ethique. Ainsi, si jamais un developpeur code l'envoi d'un mail a chaque alerte, un modo pourra alors cibler le message et aviser en fonction.
Un travail de mise a jour de la base sera egalement necessaire afin de se conformer au mieux aux propos des internautes qui frequent un site protege par cette classe (si on peut parler de protection evidement) ainsi qu'une rigueur dans le choix des categories du fichier xml.

Donc en conclusion, je dirais que cette classe n'a pas vocation de bot virtuel et qu'elle ne remplacera jamais le travail d'un bon admin sur un forum. Elle pourra par contre etre utilise dans un webmail perso (je ne vise pas les gros sites genre laposte ou gmail, j'en suis parfaitement conscient) et peut permettre d'acceder a un certains niveau de confort a de petit developpeur voulant proposer de bons services sans chercher a devenir milliardaire ^^

C'est a peu de chose pres mon etat d'esprit quand je code et que je poste sur CS.

J'espere ne pas avoir dit trop de betises :p

@++
malalam
Messages postés
10843
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
17 -
Hello,

juste pour dire que je testerai ta classe et la lirai en détail. mais je suis désolé, en ce moment, je ne trouve pas franchement le temps :-( !
Comme ta classe datetime, FhX... :-) T'as avancé ?

Jean => ce qui manquera de toutes façons à un tel code, c'est prévois les erreurs humaines : un mec peut écrire "mede" au lieu de "merde" sans faire attention (mais il voulait bien écrire merde...à moins qu'il ne voulait parler de cette charmante (sic) petite ville dans ma régione, la Mède... ;-)).
Et puis, les internautes récalcitrants s'amuseront aussi : mairde! ou merdeuh...
enfin bref, tu m'as compris... ;-)

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.