Search code examples
phpxmllibxml2xmlreader

Detect XML self closing tags with PHP XMLReader


I'd like to parse one XML document using XMLReader. I have a case switch with all the Constants. However, if a Tag is self-closing XMLReader only fires ELEMENT, not ELEMENT and than END_ELEMENT like expected.

Detection through class property $isEmptyElement does also not work because the tag has attributes.

Therefore my question: How to detect a self-closing XML tag with XMLReader in PHP?

Related but no solution: XmlReader - Self-closing element does not fire a EndElement event?

Example Node:

<mynode name="somenamestring" code="intstring" option="intstring3"/>

My Code:

$xmlReader->open($url,NULL);
$xmlWriter = new XMLWriter();
$xmlWriter->openMemory();
$xmlWriter->startDocument('1.0', 'UTF-8');
$xmlWriter->setIndent(true);
$xmlWriter->setIndentString('    ');
while ($xmlReader->read()) {
    switch ($xmlReader->nodeType) {
        case 1: #element
            $xmlWriter->startElement($xmlReader->name);
            if ($xmlReader->hasAttributes) {
                while ($xmlReader->moveToNextAttribute()) {
                    $xmlWriter->writeAttribute($xmlReader->name,$xmlReader->value);
                }
            }
            if ($xmlReader->isEmptyElement) {
                $xmlWriter->endElement();
            }
            break;

        case 3: #text
            $xmlWriter->text($xmlReader->value);
            break;

        case 4: #cdata
            $xmlWriter->writeCData($xmlReader->value);
            break;

        case 14: #whitespace
            break;

        case 15: #end element
            $xmlWriter->endElement();
            break;

        default:
            print('[WARN] NodeType not in case-switch: '.(string)$xmlReader->nodeType."\n");
            break;
    }
}

Solution

  • Detection through class property $isEmptyElement does also not work because the tag has attributes.

    That's simply wrong. An empty element with attributes is still empty and $isEmptyElement will reflect that. The problem with your code is that you test $isEmptyElement after moving to the attributes. This will change the current node to an attribute node which isn't an empty element. Something like the following should work:

            $isEmpty = $xmlReader->isEmptyElement;
            if ($xmlReader->hasAttributes) {
                while ($xmlReader->moveToNextAttribute()) {
                    ...
                }
            }
            if ($isEmpty) {
                $xmlWriter->endElement();
            }
    

    Or, alternatively:

            if ($xmlReader->hasAttributes) {
                while ($xmlReader->moveToNextAttribute()) {
                   ...
                }
                $xmlReader->moveToElement();
            }
            if ($xmlReader->isEmptyElement) {
                $xmlWriter->endElement();
            }