Discussion:
Circonscrire un eregi_replace ?
(trop ancien pour répondre)
paul
2007-10-01 08:20:20 UTC
Permalink
Bonjour,

j'ai un module de recherche sur un site. Et sur la page de résultats, je
surligne le résultat. OK ça marche.

$Contenu = eregi_replace($quoi, "<span class=\"highlight\">\\0</span>",
$Contenu);

Oui mais, le seul souci c'est que ça me casse le code à l'intérieur des
liens interactifs puisque ça ajoute le span class. Du style :
<a href="http://www.<span class="highlight">trucmuche</span>.com">mon
lien</a>

Comment dire $Contenu = eregi_replace sauf là où c'est encadré par <a
href et le > suivant ?...

C'est possible ça ?
Merci pour l'aide
Mickael Wolff
2007-10-01 10:49:47 UTC
Permalink
Post by paul
C'est possible ça ?
Merci pour l'aide
Attention, la solution que je vais te proposer est très bourrin, ça
peut éventuellement consommer beaucoup de mémoire. L'astuce consisterait
à utiliser un parser de HTML/XML : DOMDocument
<http://fr2.php.net/manual/fr/ref.dom.php> avec DOMXpath
<http://fr2.php.net/manual/fr/function.dom-domxpath-construct.php>, puis
en traitant les résultant de la requête avec preg_match.

Par exemple, pas testé et fait de tête (j'vais pas tout faire non plus
:p) :

<?php

$word = 'toto' ;

$doc = DOMDocument::loadHTML($output) ;
$doc->validate() ;
$finder = new DOMXPath('//text()') ;

foreach($finder as $textNode)
{
// L'expression régulière ci-dessous ne détectera que la première
occurrence du mot $word.
if(preg_match('`^(.*)\b('.$word.')\b(.*)$`', $textNode.value, $matches))
{
$hlSpan = $doc->createElement('span', $word) ;
$hlSpan->setAttribute('class', 'highlight') ;

$beforeNode = $textNode.ownderDocument.createTextNode($matches[1]) ;
$afterNode = $textNode.ownderDocument.createTextNode($matches[3]) ;

$textNode.parentNode.appendChild($beforeText) ;
$textNode.parentNode.appendChild($hlSpan) ;
$textNode.parentNode.appendChild($afterText) ;

$textNode.parentNode.removeChild($textNode) ;
}
}

echo $doc->saveHTML() ;

?>

Les remarques sur ma façon de coder sont les bienvenues, ou sur des
erreurs aussi.

Bon début de semaine à tous !
--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Mickael Wolff
2007-10-01 13:01:51 UTC
Permalink
Bonjour,

Petite correction rapide, car j'ai bien évidemment oublié une étape
cruciale en allant trop vite, qui est l'évaluation du XPath.
Post by Mickael Wolff
$doc = DOMDocument::loadHTML($output) ;
$doc->validate() ;
- $finder = new DOMXPath('//text()') ;
+ $finder = new DOMXPath($doc) ;
Post by Mickael Wolff
- foreach($nodes as $textNode)
+ foreach($finder->evaluate('//text()') as $textNode)
Post by Mickael Wolff
{
--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Olivier Miakinen
2007-10-01 11:17:04 UTC
Permalink
Post by paul
j'ai un module de recherche sur un site. Et sur la page de résultats, je
surligne le résultat. OK ça marche.
$Contenu = eregi_replace($quoi, "<span class=\"highlight\">\\0</span>",
$Contenu);
Oui mais, le seul souci c'est que ça me casse le code à l'intérieur des
<a href="http://www.<span class="highlight">trucmuche</span>.com">mon
lien</a>
Comment dire $Contenu = eregi_replace sauf là où c'est encadré par <a
href et le > suivant ?...
Le plus simple (et le plus sûr) me semble être d'utiliser un analyseur
syntaxique de HTML qui te construit un arbre, et tu n'as plus qu'à faire
le remplacement dans les feuilles avant de générer le résultat. Avec des
expressions rationnelles tu ne peux faire que du bricolage.


Essayons tout de même de bricoler. Je suppose que $quoi contient un
mot, avec donc des lettres le plus souvent, mais pas de signes de
ponctuation et surtout pas d'espaces. Pour éviter le problème que tu
signales, tu peux vérifier avec une « assertion arrière » que le mot
est précédé d'un espace ou du caractère « > » et rien d'autre :
(?<=\s|>)
Par la même occasion tu peux aussi vérifier qu'il se termine à une
frontière de mot :
\b
Cela donnera donc :
$contenu = preg_replace("/?<=\s|>)$quoi\b/",
"<span class='highlight'>$0</span>", $contenu);


Note que l'on ne peut pas à ma connaissance utiliser d'assertion arrière
allant chercher le « a href » car elle ne serait pas de longueur fixe.


Une autre méthode consisterait à rechercher toutes les chaînes comprises
entre « > » et « < » et à effectuer le remplacement que tu souhaites
dans chacune de ces chaînes. Par exemple :
function ma_fonction($matches) {
return preg_replace("/$quoi/", "<span class='highlight'>$0</span>",
$matches[0]);
}
$contenu = preg_replace_callback("/>.*</sU", "ma_fonction", $contenu);
Olivier Miakinen
2007-10-01 13:01:51 UTC
Permalink
Post by Olivier Miakinen
(?<=\s|>)
Ok à priori.
Post by Olivier Miakinen
$contenu = preg_replace("/?<=\s|>)$quoi\b/",
^^
Il manque une parenthèse ici. Il y a peut-être d'autres erreurs car je
n'ai rien testé.
paul
2007-10-02 07:35:45 UTC
Permalink
Post by Olivier Miakinen
Post by paul
j'ai un module de recherche sur un site. Et sur la page de résultats, je
surligne le résultat. OK ça marche.
$Contenu = eregi_replace($quoi, "<span class=\"highlight\">\\0</span>",
$Contenu);
Oui mais, le seul souci c'est que ça me casse le code à l'intérieur des
<a href="http://www.<span class="highlight">trucmuche</span>.com">mon
lien</a>
Comment dire $Contenu = eregi_replace sauf là où c'est encadré par <a
href et le > suivant ?...
Essayons tout de même de bricoler. Je suppose que $quoi contient un
mot, avec donc des lettres le plus souvent, mais pas de signes de
ponctuation et surtout pas d'espaces. Pour éviter le problème que tu
signales, tu peux vérifier avec une « assertion arrière » que le mot
est précédé d'un espace ou du caractère « > » et rien d'autre
Ben je n'interdis pas de faire des recherches sur plusieurs mots et
éventuellement avec de la ponctuation... ça semble bien fonctionner
d'ailleurs...
Post by Olivier Miakinen
Une autre méthode consisterait à rechercher toutes les chaînes comprises
entre « > » et « < » et à effectuer le remplacement que tu souhaites
function ma_fonction($matches) {
return preg_replace("/$quoi/", "<span class='highlight'>$0</span>",
$matches[0]);
}
$contenu = preg_replace_callback("/>.*</sU", "ma_fonction", $contenu);
Pas mal vu, sauf qu'il n'y a malheureusement pas forcément de lien dans
le contenu !...
;-)



C'est pour ça que je disais que s'il y a un ou des liens il faudrait ne
pas intervenir avec le eregi_replace entre les <a href et le > suivant.
Olivier Miakinen
2007-10-02 10:15:51 UTC
Permalink
Post by paul
Ben je n'interdis pas de faire des recherches sur plusieurs mots et
éventuellement avec de la ponctuation... ça semble bien fonctionner
d'ailleurs...
Donc, tu autorises aussi les recherches sur « link rel="stylesheet" »,
sur « meta http-equiv » et ainsi de suite ? Dans ce cas je ne vois pas
pourquoi le contenu des attributs href serait le seul à protéger.
Post by paul
Post by Olivier Miakinen
Une autre méthode consisterait à rechercher toutes les chaînes comprises
entre « > » et « < » et à effectuer le remplacement que tu souhaites
function ma_fonction($matches) {
return preg_replace("/$quoi/", "<span class='highlight'>$0</span>",
$matches[0]);
}
$contenu = preg_replace_callback("/>.*</sU", "ma_fonction", $contenu);
Pas mal vu, sauf qu'il n'y a malheureusement pas forcément de lien dans
le contenu !...
Je ne comprends pas ta remarque. Il n'y aura *jamais* de lien dans le
contenu, en tout cas jamais de balises <a> ou </a>, et justement il me
semble que c'est ce que tu cherches : modifier le contenu pour mettre
certains mots en évidence, mais ne surtout pas toucher aux balises HTML,
dont les liens.
Post by paul
C'est pour ça que je disais que s'il y a un ou des liens il faudrait ne
pas intervenir avec le eregi_replace entre les <a href et le > suivant.
Si tu ne prends que ce qui se trouve entre un > fermant et le < ouvrant
qui suit, ça ne changera pas ce qui se trouve entre un < ouvrant et le >
fermant correspondant. Bien sûr, il faudrait peut-être affiner si jamais
tu t'autorisais à mettre dans le contenu des « > » non encodés en
« &gt; », mais j'ai supposé que ce n'était pas le cas.
paul
2007-10-02 16:49:57 UTC
Permalink
Post by Olivier Miakinen
Post by paul
Post by Olivier Miakinen
Une autre méthode consisterait à rechercher toutes les chaînes comprises
entre « > » et « < » et à effectuer le remplacement que tu souhaites
function ma_fonction($matches) {
return preg_replace("/$quoi/", "<span class='highlight'>$0</span>",
$matches[0]);
}
$contenu = preg_replace_callback("/>.*</sU", "ma_fonction", $contenu);
Pas mal vu, sauf qu'il n'y a malheureusement pas forcément de lien dans
le contenu !...
Je ne comprends pas ta remarque.
cf plus bas
Post by Olivier Miakinen
Post by paul
C'est pour ça que je disais que s'il y a un ou des liens il faudrait ne
pas intervenir avec le eregi_replace entre les <a href et le > suivant.
Si tu ne prends que ce qui se trouve entre un > fermant et le < ouvrant
qui suit, ça ne changera pas ce qui se trouve entre un < ouvrant et le >
fermant correspondant.
Mais euh... s'il n'y a pas de lien dans le contenu, le preg_replace ne
retournera rien, non ?...
Olivier Miakinen
2007-10-02 17:06:09 UTC
Permalink
Post by paul
Post by Olivier Miakinen
function ma_fonction($matches) {
return preg_replace("/$quoi/", "<span class='highlight'>$0</span>",
$matches[0]);
}
$contenu = preg_replace_callback("/>.*</sU", "ma_fonction", $contenu);
Mais euh... s'il n'y a pas de lien dans le contenu, le preg_replace ne
retournera rien, non ?...
Ben si, il retournera la chaîne inchangée, exactement comme avec
eregi_replace (que tu peux d'ailleurs garder, même si c'est en général
plus lent).

Au fait, comme tu fais une recherche insensible à la casse,
il faut aussi rajouter un « i » si tu choisis preg au lieu de ereg :
preg_replace("/$quoi/i", ...

Par ailleurs, si tu peux avoir des « / » dans les mots cherchés, il vaut
mieux utiliser un autre caractère de séparation. Je suggère « < » qui ne
peut pas s'y trouver (il doit être remplacé par &lt;), donc :
preg_replace("<$quoi<i", ...

Continuer la lecture sur narkive:
Loading...