C.O.
C.O.

Reputation: 2329

PHP XPath how to wrap contents of p's in a span

I don't know if you can read JS Jquery but this is what I'd like to do server sided instead of client sided: $('p').wrapInner('<span class="contentsInP" />'); I'd like to take all existing paragraphs from a page and wrap their contents in a new span with a specific class.

Luckily all my documents are HTML5 in its XML flavour and are valid so that in PHP I can do this (simplified):

$xml=new DOMDocument();
$xml->loadXML($html);
$xpath = new DOMXPath($xml);  
// How to go on in here to wrap my p's?
$output=$xml->saveXML();

How do I get PHP's DOMXPath to do my wrapping?

EDIT: Fiddled with this based on the comment but couldn't make it work

// based on http://stackoverflow.com/questions/8426391/wrap-all-images-with-a-div-using-domdocument    
$xml=new DOMDocument();
$xml->loadXML(utf8_encode($temp));
$xpath = new DOMXPath($xml);  
//Create new wrapper div
$new_span = $xml->createElement('span');
$new_span->setAttribute('class','contentsInP');
$ps = $xml->getElementsByTagName('p');
//Find all p
//Iterate though p
foreach ($ps AS $p) {
  //Clone our created span
  $new_span_clone = $new_span->cloneNode();
  //Replace p with this wrapper span
  $p->parentNode->replaceChild($new_span_clone,$p);
  //Append the p's contents to wrapper span
  // THIS IS THE PROBLEM RIGHT NOW:
  $new_span_clone->appendChild($p);
}
$temp=$xml->saveXML();

The above wraps the p in a span but I need a span wrapping the p's contents while keeping the p around the span... Furthermore the above fails if the p has a class, then it won't be touched.

Upvotes: 1

Views: 600

Answers (1)

Michael Berkowski
Michael Berkowski

Reputation: 270677

In attempting to adapt that other answer, the primary thing that needs to change with it is to get all child nodes of the <p> element, first remove them as children from <p> then append them as children onto the <span>. Then finally, append the <span> as a child node of the <p>.

$html = <<<HTML
<!DOCTYPE html>
<html>
<head><title>xyz</title></head>
<body>
<div>
<p><a>inner 1</a></p>
<p><a>inner 2</a><div>stuff</div><div>more stuff</div></p>
</div>
</body>
</html>
HTML;

$xml=new DOMDocument();
$xml->loadXML(utf8_encode($html));

//Create new wrapper div
$new_span = $xml->createElement('span');
$new_span->setAttribute('class','contentsInP');
$ps = $xml->getElementsByTagName('p');
//Find all p
//Iterate though p
foreach ($ps AS $p) {
  //Clone our created span
  $new_span_clone = $new_span->cloneNode();

  // Get an array of child nodes from the <p>
  // (because the foreach won't work properly over a live nodelist)
  $children = array();
  foreach ($p->childNodes as $child) {
    $children[] = $child;
  }

  // Loop over that list of child nodes..
  foreach ($children as $child) {
    // Remove the child from the <p>
    $p->removeChild($child);
    // Append it to the span
    $new_span_clone->appendChild($child);
  }
  // Lastly, append the <span> as a child to the <p>
  $p->appendChild($new_span_clone);
}
$temp=$xml->saveXML();

Given the input HTML fragment, this should produce output like: (demonstration...)

<!DOCTYPE html>
<html>
<head><title>xyz</title></head>
<body>
<div>
<p><span class="contentsInP"><a>inner 1</a></span></p>
<p><span class="contentsInP"><a>inner 2</a><div>stuff</div><div>more stuff</div></span></p>
</div>
</body>
</html>

Upvotes: 1

Related Questions