PHP DOM: How to get child elements by tag name in an elegant manner?

35,543

Solution 1

simple iteration process

$parent = $p->parentNode;

foreach ( $parent->childNodes as $pp ) {

    if ( $pp->nodeName == 'p' ) {

        if ( strlen( $pp->nodeValue ) ) {
            echo "{$pp->nodeValue}\n";
        }

    }

}

Solution 2

An elegant manner I can imagine would be using a FilterIterator that is suitable for the job. Exemplary one that is able to work on such a said DOMNodeList and (optionally) accepting a tagname to filter for as an exemplary DOMElementFilter from the Iterator Garden does:

$a = $doc->getElementsByTagName('a')->item(0);

$bs = new DOMElementFilter($a->childNodes, 'b');

foreach($bs as $b){
    echo $b->nodeValue . "\n";
}

This will give the results you're looking for:

1
2

You can find DOMElementFilter in the Development branch now. It's perhaps worth to allow * for any tagname as it's possible with getElementsByTagName("*") as well. But that's just some commentary.

Hier is a working usage example online: https://eval.in/57170

Share:
35,543
Kalmar
Author by

Kalmar

Updated on July 26, 2022

Comments

  • Kalmar
    Kalmar almost 2 years

    I'm parsing some XML with PHP DOM extension in order to store the data in some other form. Quite unsurprisingly, when I parse an element I pretty often need to obtain all children elements of some name. There is the method DOMElement::getElementsByTagName($name), but it returns all descendants with that name, not just immediate children. There is also the property DOMNode::$childNodes but (1) it contains node list, not element list, and even if I managed to turn the list items into elements (2) I'd still need to check all of them for the name. Is there really no elegant solution to get only the children of some specific name or am I missing something in the documentation?

    Some illustration:

    <?php
    
    DOMDocument();
    $document->loadXML(<<<EndOfXML
    <a>
      <b>1</b>
      <b>2</b>
      <c>
        <b>3</b>
        <b>4</b>
      </c>
    </a>
    EndOfXML
    );
    
    $bs = $document
        ->getElementsByTagName('a')
        ->item(0)
        ->getElementsByTagName('b');
    
    foreach($bs as $b){
        echo $b->nodeValue . "\n";
    }
    
    // Returns:
    //   1
    //   2
    //   3
    //   4
    // I'd like to obtain only:
    //   1
    //   2
    
    ?>