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

Probleme de Librairie DOM

8 réponses
Avatar
DublinFrench
Salut

Je tente de remplacer des mots cles dans un contenu HTML mais je ne veux
pas remplacer des mots dans des liens, ou situés a l'interieurs de
définitions de tags (titles, ...). Je cherche a remplacer uniquement le
texte réel qui n'est pas situé a l'interieur de balises <a></a>.

Je me suis donc reporté sur la librairie DOM
http://us2.php.net/manual/en/book.dom.php

Mais j'ai des petits soucis avec cette librairie car je peux avoir du
cuntenu HTML dans mes $node->nodeValue meme si $node->hasChildNodes()
retourne false. Il y a meme des cas ou le $node->nodeValue est carrément
une balise HTML car commencant par <span ....

Voici une version réduite de mon script:

Class Parsing
{

public static $listforbiddenTags = array("a", "img", "javascript",
"css", "script");

public static function parseNodes($node, $keyWord, $replacement)
{
$node->normalize();
if ($node->hasChildNodes())
{
$subNodes = $node->childNodes;
foreach ($subNodes as $subNode)
{
parseNodes($subNode, $keyWord, $replacement);
}
}
else
{
if (!in_array($node->parentNode->nodeName,
self::$listforbiddenTags) && $node->nodeType == XML_TEXT_NODE
&& strlen(trim($node->wholeText))>=1 && used($node->nodeValue))
{
$newelement = self::$dom->createTextNode(str_replace($keyWord,
$replacement, $node->nodeValue));
$node->parentNode->replaceChild($newelement, $node);
$node->normalize();
}
}
}

}

$doc->loadHTML($content);
$root = $doc->firstChild;
$doc->normalizeDocument();
Parsing::$dom=$doc;
Parsing::parseNodes($doc, $keyWord, $replacement);


Merci pour vos idées :)

Cheers

DF




--

8 réponses

Avatar
Mickael Wolff
DublinFrench wrote:

if (!in_array($node->parentNode->nodeName,
self::$listforbiddenTags) && $node->nodeType == XML_TEXT_NODE
&& strlen(trim($node->wholeText))>=1 && used($node->nodeValue))



Tu devrais ranger ce test dans une fonction.

$newelement = self::$dom->createTextNode(str_replace($keyWord,
$replacement, $node->nodeValue));


Tu peux directement modifier DomNode::nodeValue.sur le noeud parent.

J'utiliserais domxpath :
<?php

$doc = @domdocument::load('http://localhost') ;
@$doc->validate() ;
$seeker = new domxpath($doc) ;

// je ne sais pas comment filtrer par un parent d'un certain type dans
xpath.
$txtList = $seeker->evaluate('//text()') ;

foreach($txtList as $txt)
{
if(isGoodText($txt))
replaceText($txt, array('Bienvenue', 'lettre'), array('Maljour',
'courrier')) ;
}

echo $doc->saveXML() ;

function isGoodText(domnode $txt)
{
$pn = $txt->parentNode ;
return !in_array($pn->tagName, array('a')) ;
}

function replaceText(domnode $txt, $key, $replacement)
{
$content = $txt->nodeValue ;
$txt->nodeValue = str_replace($key, $replacement, $content) ;
}

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
DublinFrench
Mickael Wolff wrote:
if (!in_array($node->parentNode->nodeName,
self::$listforbiddenTags) && $node->nodeType == XML_TEXT_NODE
&& strlen(trim($node->wholeText))>=1 &&
used($node->nodeValue))


Tu devrais ranger ce test dans une fonction.



Yep, tu as raison. Les trucs grossissent, grossissent, et apres il faut
aller les ranger. :)


J'utiliserais domxpath :
<?php



Je vais aller regarder. La librairie DOM ne me permets pas de faire ce
que je désire, le HTML est mal parcouru. Par exemple, si un node
contient une balise <a> , on ne pourra pas travailler sur la chaine qui
suit la balise </a> mais qui est toujours dans le meme node.

Merci pour ton script que je vais aller tester de ce pas.

@++

DF
Avatar
DublinFrench
DublinFrench wrote:
Merci pour ton script que je vais aller tester de ce pas.




$content = "
<ul>
<li>title: possibility to <a href="www.test.com">define the title</a>
from the link regards where this title is located</li>
</ul>
";

$doc = domdocument::load($content) ;
@$doc->validate() ;
$seeker = new domxpath($doc) ;


=> I/O warning : failed to load external entity

Bon c'est pas grave, j'ai bien avancé avec mes expressions régulières.
Cela me semble encore imparfait, mais visiblement jouer avec le DOM a
ses limites. J'ai des soucis comportementaux flagrants, du style ne pas
pouvoir accepter ce qui se situe _apres_ une balise html dans un node
qui est présenté comme étant du texte pure bien que contenant une
balise. regexp rulez for today.

@++

DF
Avatar
Mickael Wolff
DublinFrench wrote:

$doc = domdocument::load($content) ;
=> I/O warning : failed to load external entity


Il FAUT lire la documentation. Tu remarqueras que jevdonnais un
chemin vers un fichier, pas le contenu.

Bon c'est pas grave, j'ai bien avancé avec mes expressions régulières.
Cela me semble encore imparfait, mais visiblement jouer avec le DOM a
ses limites. J'ai des soucis comportementaux flagrants, du style ne pas
pouvoir accepter ce qui se situe _apres_ une balise html dans un node
qui est présenté comme étant du texte pure bien que contenant une
balise. regexp rulez for today.



Ca dépend de la qualité du HTML de base. Mais noramlement, il ne
devrait pas y avoir de problème. J'ai essayé moi-meme d'utiliser les
regex pour parser du HTML (PHP4). C'est difficile, il faut beaucoup de
méthode pour arriver à faire quelque chose de correct. Et si tu
n'arrives pas à utiliser le DOM, je ne pense pas que tu ais le niveau
nécessaire.

Utilises le DOM. Je te conseillerais de lire toute la documentation
pour avoir une vue d'ensemble des fonctionnalités.

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
DublinFrench
Mickael Wolff wrote:
DublinFrench wrote:
Ca dépend de la qualité du HTML de base.



Ce point est malheureusement incontrolable. Le contenu saisie provient
d'un CK editor.


Mais noramlement, il ne
devrait pas y avoir de problème. J'ai essayé moi-meme d'utiliser les
regex pour parser du HTML (PHP4). C'est difficile, il faut beaucoup de
méthode pour arriver à faire quelque chose de correct.



Je suis arrivé a placer mes liens et tooltips sur mes mots clés non
situés dans des définitions de balises et n'étant pas déja un lien.



Et si tu
n'arrives pas à utiliser le DOM, je ne pense pas que tu ais le niveau
nécessaire.
Utilises le DOM. Je te conseillerais de lire toute la documentation
pour avoir une vue d'ensemble des fonctionnalités.



Le DOM a ses limites qui m'empechent de faire les choses correctements.

Exemple:

content = "
<ul>
<li>title: possibility to <a href="www.test.com">define the
title</a> from the link regards where this title is located</li>
</ul>
";

Class Parsing
{
public static $listforbiddenTags = array("a", "img", "javascript",
"css", "script");
public static $dom = null;

function parseNodes($node, $keyWord, $replacement)
{
if ($node->hasChildNodes())
{
$subNodes = $node->childNodes;
foreach ($subNodes as $subNode)
{
self::parseNodes($subNode, $keyWord, $replacement);

}
else if (isset($node->nodeValue))
{

$newelement self::$dom->createTextNode(str_replace($keyWord, $replacement,
$node->nodeValue));
$node->parentNode->replaceChild($newelement, $node);
}
}
}

$keyWord="title";
$replacement="GOT YOU";

$doc = new DomDocument('1.0', 'UTF-8');

$doc->loadHTML($content);
$root = $doc->firstChild;
//$doc->normalizeDocument();

Parsing::$dom=$doc;

Parsing::parseNodes($doc, $keyWord, $replacement);

$content=$doc->saveHTML();

print(($content));



/* OUTPUT

<br />node->nodeType => 3 <->
<br />node->value: title: possibility to
<br />node->nodeType => 3 <->
<br />node->value:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><ul>
<li>GOT YOU: possibility to <a href="www.test.com">define the title</a>
from the link regards where this title is located</li>
</ul></body></html>

/*ce qui ne va pas:

1 - le dernier title n'a pas été remplacé. Pire que ca, la partie
"from the link regards where this title is located" ne passe JAMAIS dans
le contenu de $node->nodeValue (j'ai fait des prints)

2 - Il y a maintenant des tags HTML supplémentaires, mais c'est pas
trop grave je peux les enlever apres.


Donc pour moi a ce stade, le DOM, c'est NIET.

Cheers

DF
Avatar
Mickael Wolff
DublinFrench wrote:

Exemple:

content = "
<ul>
<li>title: possibility to <a href="www.test.com">define the
title</a> from the link regards where this title is located</li>
</ul>
";



C'est normal que ça ne marche pas, le document n'est pas complet. Il
faut utiliser DomDocumentFragment pour gérer les fragments, me semble-t-il.

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org
Avatar
DublinFrench
Mickael Wolff wrote:
DublinFrench wrote:
Exemple:
content = "
<ul>
<li>title: possibility to <a href="www.test.com">define the
title</a> from the link regards where this title is located</li>
</ul>
";



C'est normal que ça ne marche pas, le document n'est pas complet.



J'ai absolument le même résultat avec

$content = "
<html>
<head><title>pouet</title>
</head>
<body>
<ul>
<li>title: possibility to <a href="www.test.com">define the title</a>
from the link regards where this title is located</li>
</ul>
</body>
</html>
";


Il
faut utiliser DomDocumentFragment pour gérer les fragments, me semble-t-il.



Le nom est prometteur, mais la page est assez peu explicite.
http://th2.php.net/manual/en/class.domdocumentfragment.php

Ne serait ce pas pour rattacher un fragment html a un node ?

@++

DF
Avatar
Mickael Wolff
DublinFrench wrote:

J'ai absolument le même résultat avec

$content = "
<html>
<head><title>pouet</title>
</head>
<body>
<ul>
<li>title: possibility to <a href="www.test.com">define the title</a>
from the link regards where this title is located</li>
</ul>
</body>
</html>
";



Le document est toujours incomplet. Il manque la DTD à utiliser. Si
le document génère des warning ou des erreurs à l'ouverture du fichier,
ou à sa validation, l'outil DOMDocument ne fonctionnera pas.

Il
faut utiliser DomDocumentFragment pour gérer les fragments, me semble-t-il.



Le nom est prometteur, mais la page est assez peu explicite.
http://th2.php.net/manual/en/class.domdocumentfragment.php

Ne serait ce pas pour rattacher un fragment html a un node ?



Un fragment est un node comme les autres. DomDocument::adoptChild et
DomDocument::insertChild devraient t'aider.

--
Mickaël Wolff aka Lupus Michaelis
http://lupusmic.org