0

I have an HTML string, and I want to remove from it all DIVs whose class is "toremove".

This is trivial to do on the client side with jQuery etc., but I want to do it on the server side with PHP.

A simple aegular expression won't work, because divs may be nested...

Stephen
  • 18,827
  • 9
  • 60
  • 98
Erel Segal-Halevi
  • 33,955
  • 36
  • 114
  • 183

4 Answers4

2

You could use the DOM object and xPath to remove the DIVs.

/** UNTESTED **/
$doc = new DOMDocument();
$doc->loadHTMLFile($file);

$xpath = new DOMXpath($doc);
$elements = $xpath->query("*/div[@class='yourClasshere']");

foreach($elements as $e){
    $doc->removeChild($e);
}
$doc->saveHTMLFile($file);

You can replace the load from file and save to file with load from and save to string if you prefer.

jebbench
  • 283
  • 1
  • 4
  • 12
  • Thank you, but see my improvements here: http://stackoverflow.com/questions/12859315/removing-all-divs-with-a-certain-class/12860211#12860211 – Erel Segal-Halevi Oct 14 '12 at 17:04
1

Here's a snippet of code I'm using to remove content from pages:

/**
 * A method to remove unwanted parts of an HTML-page. Can remove elements by 
 * id, tag name and/or class names. 
 *
 * @param string $html The HTML to manipulate
 * @param array $partsToRemove An array of arrays, with the keys specifying 
 * what type of values the array holds. The following keys are used:
 * 'elements' - An array of element ids to remove from the html 
 * 'tags' - An array of tag names to remove from the html
 * 'classNames' - An array of class names. Each tag that contains one of the 
 * class names will be removed from the html.
 *
 * Also, note that descendants of the removed document will also be removed.
 * 
 * @return string The manipulated HTML content
 *
 * @example removeHtmlParts($html, array (
 *  'elements' => array ('headerSection', 'nav', 'footerSection'),
 *  'tags' => array ('form'),
 *  'classNames' => array ('promotion')
 *  ));
 */

--

public function removeHtmlParts ($html, array $toRemove = array())
{
$document = new \DOMDocument('1.0', 'UTF-8');
$document->encoding = 'UTF-8';
// Hack to force DOMDocument to load the HTML using UTF-8.
@$document->loadHTML('<?xml encoding="UTF-8">' . $response->getBody());
$partsToRemove = array ();
if(isset($toRemove['elements']))
{
  $partsToRemove['elements'] = $toRemove['element'];
}
if(isset($toRemove['tags']))
{
  $partsToRemove['tags'] = $toRemove['tags'];
}
if(isset($toRemove['classNames']))
{
  $partsToRemove['classNames'] = $toRemove['classNames'];
}

foreach ($partsToRemove as $type => $content)
{
    if($type == 'elements')
    {
        foreach ($content as $elementId)
        {
            $element = $document->getElementById($elementId);
            if($element)
            {
                $element->parentNode->removeChild($element);
            }
        }
    }
    elseif($type == 'tags')
    {
        foreach($content as $tagName)
        {
            $tags = $document->getElementsByTagName($tagName);
            while($tags->length)
            {
                $tag = $tags->item(0);
                if($tag)
                {
                    $tag->parentNode->removeChild($tag);
                }
            }
        }
    }
    elseif($type == 'classNames')
    {
        foreach ($content as $className)
        {
            $xpath = new \DOMXPath($document);
                    $xpathExpression = sprintf(
                       '//*[contains(@class,"%1")]', 
                       $className
                    ); 
            $domNodeList = $xpath->evaluate($xpathExpression);
            for($i = 0; $i < $domNodeList->length; $i++)
            {
                $node = $domNodeList->item($i);
                if($node && $node->parentNode)
                {
                    $node->parentNode->removeChild($node);
                }
            }
        }
    }
}
return $document->saveHTML();
}

Note:

  • This code has not undergone proper unit testing and probably contains bugs in edge cases
  • This method should be refactored into a class, and the contents of the method split into separate methods to ease testing.
PatrikAkerstrand
  • 45,315
  • 11
  • 79
  • 94
1

Based on the short answer of jebbench and the long answer of PatrikAkerstrand, I created a medium function that exactly solves my problem:

/**
 * remove, from the given xhtml string, all divs with the given class.
 */
function remove_divs_with_class($xhtml, $class) {
    $doc = new DOMDocument();

    // Hack to force DOMDocument to load the HTML using UTF-8:
$doc->loadHTML('<?xml encoding="UTF-8">'.$xhtml); 

    $xpath = new DOMXpath($doc);
    $elements = $xpath->query("//*[contains(@class,'$class')]");

    foreach  ($elements as $element)
        $element->parentNode->removeChild($element);

    return $doc->saveHTML();
}

/* UNIT TEST */
if (basename(__FILE__)==basename($_SERVER['PHP_SELF'])) {
    $xhtml = "<div class='near future'>near future</div><div>start</div><div class='future'>future research</div><div class='summary'>summary</div><div class='a future b'>far future</div>";
    $xhtml2 = remove_divs_with_class($xhtml, "future");
    print "<h2>before</h2>$xhtml<h2>after</h2>$xhtml2";
}

/* OUTPUT:

before

near future
start
future research
summary
far future

after

start
summary

*/
Erel Segal-Halevi
  • 33,955
  • 36
  • 114
  • 183
-1

Never, ever try and use regex to parse XML/HTML. Instead use a parsing library. Apparently, one for PHP is http://sourceforge.net/projects/simplehtmldom/files/

Gaurav Dadhania
  • 5,167
  • 8
  • 42
  • 61