chovy
chovy

Reputation: 75666

replace html using DOMDocument in PHP

I'm trying to cleanup some bad html using DOMDocument. The html has an <div class="article"> element, with <br/><br/> instead of </p><p> -- I want to regex these into paragraphs...but can't seem to get my node back into the original document:

//load entire doc
$doc = new DOMDocument();
$doc->loadHTML($htm);
$xpath = new DOMXpath($doc);
//get the article
$article = $xpath->query("//div[@class='article']")->parentNode;
//get as string
$article_htm =   $doc->saveXML($article);
//regex the bad markup
$article_htm2 = preg_replace('/<br\/><br\/>/i', '</p><p>', $article_htm);

//create new doc w/ new html string
$doc2 = new DOMDocument();
$doc2->loadHTML($article_htm2);
$xpath2 = new DOMXpath($doc2);

//get the original article node
$article_old = $xpath->query("//div[@class='article']");
//get the new article node
$article_new = $xpath2->query("//div[@class='article']");

//replace original node with new node
$article->replaceChild($article_old, $article_new);
$article_htm_new = $doc->saveXML();

//dump string
var_dump($article_htm_new);

all i get is a 500 internal server error...not sure what I'm doing wrong.

Upvotes: 0

Views: 294

Answers (2)

Dr.Molle
Dr.Molle

Reputation: 117334

There are several issues:

  1. $xpath->query returns a nodeList, not a node. You must select an item from the nodeList
  2. replaceChild() expects as 1st argument the new node, and as 2nd the node to replace
  3. $article_new is part of another document, you first must import the node into $doc

Fixed code:

//load entire doc
$doc = new DOMDocument();
$doc->loadHTML($htm);
$xpath = new DOMXpath($doc);
//get the article
$article = $xpath->query("//div[@class='article']")->item(0)->parentNode;
//get as string
$article_htm =   $doc->saveXML($article);
//regex the bad markup
$article_htm2 = preg_replace('/<br\/><br\/>/i', '</p>xxx<p>', $article_htm);

//create new doc w/ new html string
$doc2 = new DOMDocument();
$doc2->loadHTML($article_htm2);
$xpath2 = new DOMXpath($doc2);

//get the original article node
$article_old = $xpath->query("//div[@class='article']")->item(0);
//get the new article node
$article_new = $xpath2->query("//div[@class='article']")->item(0);

//import the new node into $doc
$article_new=$doc->importNode($article_new,true);

//replace original node with new node
$article->replaceChild($article_new, $article_old);
$article_htm_new = $doc->saveHTML();

//dump string
var_dump($article_htm_new);

Instead of using 2 documents you may create a DocumentFragment of $article_htm2 and use this fragment as replacement.

Upvotes: 2

Musa
Musa

Reputation: 97672

I think it should be

$article->parentNode->replaceChild($article_old, $article_new);

the article is not a child of itself.

Upvotes: 1

Related Questions