PHP Conference Nagoya 2025

La clase XMLReader

(PHP 5 >= 5.1.0, PHP 7, PHP 8)

Introducción

La extensión XMLReader es un analizador de XML. El lector actúa como un cursor yendo hacia adelante en la corriente del documento y deteniéndose en cada nodo del camino.

Sinopsis de la Clase

class XMLReader {
/* Constantes */
public const int NONE;
public const int ELEMENT;
public const int ATTRIBUTE;
public const int TEXT;
public const int CDATA;
public const int ENTITY_REF;
public const int ENTITY;
public const int PI;
public const int COMMENT;
public const int DOC;
public const int DOC_TYPE;
public const int DOC_FRAGMENT;
public const int NOTATION;
public const int WHITESPACE;
public const int END_ELEMENT;
public const int END_ENTITY;
public const int XML_DECLARATION;
public const int LOADDTD;
public const int DEFAULTATTRS;
public const int VALIDATE;
public const int SUBST_ENTITIES;
/* Propiedades */
public int $depth;
public bool $hasValue;
public string $name;
public int $nodeType;
public string $prefix;
public string $value;
/* Métodos */
public close(): true
public expand(?DOMNode $baseNode = null): DOMNode|false
public static fromStream(
    resource $stream,
    ?string $encoding = null,
    int $flags = 0,
    ?string $documentUri = null
): static
public static fromString(string $source, ?string $encoding = null, int $flags = 0): static
public static fromUri(string $uri, ?string $encoding = null, int $flags = 0): static
public getAttribute(string $name): ?string
public getAttributeNo(int $index): ?string
public getAttributeNs(string $name, string $namespace): ?string
public getParserProperty(int $property): bool
public isValid(): bool
public lookupNamespace(string $prefix): ?string
public moveToAttribute(string $name): bool
public moveToAttributeNo(int $index): bool
public moveToAttributeNs(string $name, string $namespace): bool
public next(?string $name = null): bool
public static open(string $uri, ?string $encoding = null, int $flags = 0): bool|XMLReader
public read(): bool
public readString(): string
public setParserProperty(int $property, bool $value): bool
public setRelaxNGSchema(?string $filename): bool
public setSchema(?string $filename): bool
public static XML(string $source, ?string $encoding = null, int $flags = 0): bool|XMLReader
}

Propiedades

attributeCount

El número de atributos en el nodo

baseURI

La base URI del nodo

depth

La profundidad del nodo en el árbol, empezando en 0

hasAttributes

Indica si el nodo tiene atributos

hasValue

Indica si el nodo tiene un valor de texto

isDefault

Indica si el atributo está por defecto del DTD

isEmptyElement

Indica si el nodo es un elemento vacio de etiqueta

localName

El nombre local del nodo

name

El nombre calificado del nodo

namespaceURI

El URI del namespace asociado con el nodo

nodeType

El tipo de nodo para el nodo

prefix

El prefijo del namespace asociado con el nodo

value

El valor de texto del nodo

xmlLang

El xml: El lang scope el cual el nodo reside

Constantes predefinidas

XMLReader Node Types

XMLReader::NONE

Sin tipo de nodo

XMLReader::ELEMENT

Empieza el eleménto

XMLReader::ATTRIBUTE

El atributo del nodo

XMLReader::TEXT

El nodo de texto

XMLReader::CDATA

El CDATA del nodo

XMLReader::ENTITY_REF

La entidad de refencia del nodo

XMLReader::ENTITY

La entidad de declaración del nodo

XMLReader::PI

La instrucción de proceso del nodo

XMLReader::COMMENT

El comentario del nodo

XMLReader::DOC

El documento del nodo

XMLReader::DOC_TYPE

El tipo de documento del nodo

XMLReader::DOC_FRAGMENT

El fragmento del documento del nodo

XMLReader::NOTATION

La notación del nodo

XMLReader::WHITESPACE

El espacio en blanco del nodo

XMLReader::SIGNIFICANT_WHITESPACE

El espacio en blanco significante del nodo

XMLReader::END_ELEMENT

El fin del eleménto

XMLReader::END_ENTITY

La entidad final

XMLReader::XML_DECLARATION

La declaración XML del nodo

XMLReader Parser Options

XMLReader::LOADDTD

Carga el DTD pero no lo valida

XMLReader::DEFAULTATTRS

Carga el DTD y los atributos por defecto pero no los valida

XMLReader::VALIDATE

Carga el DTD y valida el analizado en blanco

XMLReader::SUBST_ENTITIES

Substituye las entidades y expande las referencias

Tabla de contenidos

add a note

User Contributed Notes 17 notes

up
27
james dot ellis at example dot com
15 years ago
The "XML2Assoc" functions noted here should be used with caution... basically they are duplicating the functionality already present in SimpleXML. They may work but they won't scale.

Their are two main uses cases for parsing XML, each suited to either XMLReader or SimpleXML.

1. SimpleXML is an excellent tool for easy access to an XML document tree using native PHP data types. It starts to flounder with massive (> 50M or so) XML documents, as it reads the entire document into memory before it can be processed. SimpleXML will just laugh at you then die when your server runs out of memory (or it will cause a load spike).

2. Aside from the reasoning behind massive XML documents, if you have to deal with massive XML documents, use XMLReader to process them. Don't try and gather an entire XML document into a PHP data structure using XMLReader and a PHP xml2assoc() function, you are reinventing the SimpleXML wheel.
When parsing massive XML documents using XMLReader, gather the data you need to perform an operation then perform it before skipping to the next node. Do not build massive data structures from a massive XML document, your server (and it's admins) will not like you.
up
16
jart (at) mail.ru
14 years ago
Guys, I hope this example will help
you can erase prints showing the process-
and it will be a piece of nice code.

<?php
function xml2assoc($xml, $name)
{
print
"<ul>";

$tree = null;
print(
"I'm inside " . $name . "<br>");

while(
$xml->read())
{
if(
$xml->nodeType == XMLReader::END_ELEMENT)
{
print
"</ul>";
return
$tree;
}

else if(
$xml->nodeType == XMLReader::ELEMENT)
{
$node = array();

print(
"Adding " . $xml->name ."<br>");
$node['tag'] = $xml->name;

if(
$xml->hasAttributes)
{
$attributes = array();
while(
$xml->moveToNextAttribute())
{
print(
"Adding attr " . $xml->name ." = " . $xml->value . "<br>");
$attributes[$xml->name] = $xml->value;
}
$node['attr'] = $attributes;
}

if(!
$xml->isEmptyElement)
{
$childs = xml2assoc($xml, $node['tag']);
$node['childs'] = $childs;
}

print(
$node['tag'] . " added <br>");
$tree[] = $node;
}

else if(
$xml->nodeType == XMLReader::TEXT)
{
$node = array();
$node['text'] = $xml->value;
$tree[] = $node;
print
"text added = " . $node['text'] . "<br>";
}
}

print
"returning " . count($tree) . " childs<br>";
print
"</ul>";

return
$tree;
}

echo
"<PRE>";

$xml = new XMLReader();
$xml->open('test.xml');
$assoc = xml2assoc($xml, "root");
$xml->close();

print_r($assoc);
echo
"</PRE>";

?>

It reads this xml:

<test>
<hallo volume="loud"> me <br/> lala </hallo>
<hallo> me </hallo>
</test>
up
10
kula_shakerz
11 years ago
Found this in the IXmlReader docs at msdn but it's also valid for XMLReader in PHP.

You should save the value of $isEmptyElement before processing attributes, or call moveToElement to make $isEmptyElement valid after processing attributes.

$isEmptyElement returns FALSE when XMLReader is positioned on an attribute node, even if attribute's parent element is empty.
up
3
Sean Colin Ruiz
7 years ago
As japos mentioned. Take care how you use isEmptyElement. After you are done looping through the attributes: isEmptyElement will be false. You can use moveToElement() to move the cursor back to the element and then you can use isEmptyElement like normal again.
up
11
dkrnl at yandex dot ru
11 years ago
Wrapper XMLReader class, for simple SAX-reading huge xml:
https://github.com/dkrnl/SimpleXMLReader

Usage example: http://github.com/dkrnl/SimpleXMLReader/blob/master/examples/example1.php

<?php

/**
* Simple XML Reader
*
* @license Public Domain
* @author Dmitry Pyatkov(aka dkrnl) <dkrnl@yandex.ru>
* @url http://github.com/dkrnl/SimpleXMLReader
*/
class SimpleXMLReader extends XMLReader
{

/**
* Callbacks
*
* @var array
*/
protected $callback = array();

/**
* Add node callback
*
* @param string $name
* @param callback $callback
* @param integer $nodeType
* @return SimpleXMLReader
*/
public function registerCallback($name, $callback, $nodeType = XMLREADER::ELEMENT)
{
if (isset(
$this->callback[$nodeType][$name])) {
throw new
Exception("Already exists callback $name($nodeType).");
}
if (!
is_callable($callback)) {
throw new
Exception("Already exists parser callback $name($nodeType).");
}
$this->callback[$nodeType][$name] = $callback;
return
$this;
}

/**
* Remove node callback
*
* @param string $name
* @param integer $nodeType
* @return SimpleXMLReader
*/
public function unRegisterCallback($name, $nodeType = XMLREADER::ELEMENT)
{
if (!isset(
$this->callback[$nodeType][$name])) {
throw new
Exception("Unknow parser callback $name($nodeType).");
}
unset(
$this->callback[$nodeType][$name]);
return
$this;
}

/**
* Run parser
*
* @return void
*/
public function parse()
{
if (empty(
$this->callback)) {
throw new
Exception("Empty parser callback.");
}
$continue = true;
while (
$continue && $this->read()) {
if (isset(
$this->callback[$this->nodeType][$this->name])) {
$continue = call_user_func($this->callback[$this->nodeType][$this->name], $this);
}
}
}

/**
* Run XPath query on current node
*
* @param string $path
* @param string $version
* @param string $encoding
* @return array(SimpleXMLElement)
*/
public function expandXpath($path, $version = "1.0", $encoding = "UTF-8")
{
return
$this->expandSimpleXml($version, $encoding)->xpath($path);
}

/**
* Expand current node to string
*
* @param string $version
* @param string $encoding
* @return SimpleXMLElement
*/
public function expandString($version = "1.0", $encoding = "UTF-8")
{
return
$this->expandSimpleXml($version, $encoding)->asXML();
}

/**
* Expand current node to SimpleXMLElement
*
* @param string $version
* @param string $encoding
* @param string $className
* @return SimpleXMLElement
*/
public function expandSimpleXml($version = "1.0", $encoding = "UTF-8", $className = null)
{
$element = $this->expand();
$document = new DomDocument($version, $encoding);
$node = $document->importNode($element, true);
$document->appendChild($node);
return
simplexml_import_dom($node, $className);
}

/**
* Expand current node to DomDocument
*
* @param string $version
* @param string $encoding
* @return DomDocument
*/
public function expandDomDocument($version = "1.0", $encoding = "UTF-8")
{
$element = $this->expand();
$document = new DomDocument($version, $encoding);
$node = $document->importNode($element, true);
$document->appendChild($node);
return
$document;
}

}
?>
up
5
desk_ocean at msn dot com
16 years ago
make some modify from Sergey Aikinkulov's note

<?php
function xml2assoc(&$xml){
$assoc = NULL;
$n = 0;
while(
$xml->read()){
if(
$xml->nodeType == XMLReader::END_ELEMENT) break;
if(
$xml->nodeType == XMLReader::ELEMENT and !$xml->isEmptyElement){
$assoc[$n]['name'] = $xml->name;
if(
$xml->hasAttributes) while($xml->moveToNextAttribute()) $assoc[$n]['atr'][$xml->name] = $xml->value;
$assoc[$n]['val'] = xml2assoc($xml);
$n++;
}
else if(
$xml->isEmptyElement){
$assoc[$n]['name'] = $xml->name;
if(
$xml->hasAttributes) while($xml->moveToNextAttribute()) $assoc[$n]['atr'][$xml->name] = $xml->value;
$assoc[$n]['val'] = "";
$n++;
}
else if(
$xml->nodeType == XMLReader::TEXT) $assoc = $xml->value;
}
return
$assoc;
}
?>

add else if($xml->isEmptyElement)
may be some xml has emptyelement
up
8
godseth at o2 dot pl
16 years ago
Thanks rein_baarsma33 AT hotmail DOT com for bugfixes.

This is my new child of XML parsing method based on my and yours modification.

XML2ASSOC Is a complete solution for parsing ordinary XML

<?php
/**
* XML2Assoc Class to creating
* PHP Assoc Array from XML File
*
* @author godseth (AT) o2.pl & rein_baarsma33 (AT) hotmail.com (Bugfixes in parseXml Method)
* @uses XMLReader
*
*/

class Xml2Assoc {

/**
* Optimization Enabled / Disabled
*
* @var bool
*/
protected $bOptimize = false;

/**
* Method for loading XML Data from String
*
* @param string $sXml
* @param bool $bOptimize
*/

public function parseString( $sXml , $bOptimize = false) {
$oXml = new XMLReader();
$this -> bOptimize = (bool) $bOptimize;
try {

// Set String Containing XML data
$oXml->XML($sXml);

// Parse Xml and return result
return $this->parseXml($oXml);

} catch (
Exception $e) {
echo
$e->getMessage();
}
}

/**
* Method for loading Xml Data from file
*
* @param string $sXmlFilePath
* @param bool $bOptimize
*/
public function parseFile( $sXmlFilePath , $bOptimize = false ) {
$oXml = new XMLReader();
$this -> bOptimize = (bool) $bOptimize;
try {
// Open XML file
$oXml->open($sXmlFilePath);

// // Parse Xml and return result
return $this->parseXml($oXml);

} catch (
Exception $e) {
echo
$e->getMessage(). ' | Try open file: '.$sXmlFilePath;
}
}

/**
* XML Parser
*
* @param XMLReader $oXml
* @return array
*/
protected function parseXml( XMLReader $oXml ) {

$aAssocXML = null;
$iDc = -1;

while(
$oXml->read()){
switch (
$oXml->nodeType) {

case
XMLReader::END_ELEMENT:

if (
$this->bOptimize) {
$this->optXml($aAssocXML);
}
return
$aAssocXML;

case
XMLReader::ELEMENT:

if(!isset(
$aAssocXML[$oXml->name])) {
if(
$oXml->hasAttributes) {
$aAssocXML[$oXml->name][] = $oXml->isEmptyElement ? '' : $this->parseXML($oXml);
} else {
if(
$oXml->isEmptyElement) {
$aAssocXML[$oXml->name] = '';
} else {
$aAssocXML[$oXml->name] = $this->parseXML($oXml);
}
}
} elseif (
is_array($aAssocXML[$oXml->name])) {
if (!isset(
$aAssocXML[$oXml->name][0]))
{
$temp = $aAssocXML[$oXml->name];
foreach (
$temp as $sKey=>$sValue)
unset(
$aAssocXML[$oXml->name][$sKey]);
$aAssocXML[$oXml->name][] = $temp;
}

if(
$oXml->hasAttributes) {
$aAssocXML[$oXml->name][] = $oXml->isEmptyElement ? '' : $this->parseXML($oXml);
} else {
if(
$oXml->isEmptyElement) {
$aAssocXML[$oXml->name][] = '';
} else {
$aAssocXML[$oXml->name][] = $this->parseXML($oXml);
}
}
} else {
$mOldVar = $aAssocXML[$oXml->name];
$aAssocXML[$oXml->name] = array($mOldVar);
if(
$oXml->hasAttributes) {
$aAssocXML[$oXml->name][] = $oXml->isEmptyElement ? '' : $this->parseXML($oXml);
} else {
if(
$oXml->isEmptyElement) {
$aAssocXML[$oXml->name][] = '';
} else {
$aAssocXML[$oXml->name][] = $this->parseXML($oXml);
}
}
}

if(
$oXml->hasAttributes) {
$mElement =& $aAssocXML[$oXml->name][count($aAssocXML[$oXml->name]) - 1];
while(
$oXml->moveToNextAttribute()) {
$mElement[$oXml->name] = $oXml->value;
}
}
break;
case
XMLReader::TEXT:
case
XMLReader::CDATA:

$aAssocXML[++$iDc] = $oXml->value;

}
}

return
$aAssocXML;
}

/**
* Method to optimize assoc tree.
* ( Deleting 0 index when element
* have one attribute / value )
*
* @param array $mData
*/
public function optXml(&$mData) {
if (
is_array($mData)) {
if (isset(
$mData[0]) && count($mData) == 1 ) {
$mData = $mData[0];
if (
is_array($mData)) {
foreach (
$mData as &$aSub) {
$this->optXml($aSub);
}
}
} else {
foreach (
$mData as &$aSub) {
$this->optXml($aSub);
}
}
}
}

}

?>

[EDIT BY danbrown AT php DOT net: Fixes were also provided by "Alex" and (qdog AT qview DOT org) in user notes on this page (since removed).]
up
3
japos dot trash at googlemail dot com
14 years ago
Take care about how to use XMLReader::$isElementEmpty. I don't know if it is a bug or not, but $isElementEmpty is set for the current context and NOT just for the element. If you move your cursor to an attribute, $isElementEmpty will ALWAYS be false.

<?php
$xml
= new XMLReader();
$xml->XML('<tag attr="value" />');
$xml->read();
var_dump($xml->isEmptyElement);
$xml->moveToNextAttribute();
var_dump($xml->isEmptyElement);
?>

will output

(bool) true
(bool) false

So be sure to store $isEmptyElement before moving the cursor.
up
1
eef dot vreeland at gmail dot com
7 years ago
Please discard my previous note; I pressed 'Add Note' too quickly

About (non-)self-closing tags:

A) <tag></tag>
$xmlRdr->isEmptyElement => false
$xmlRdr->hasValue => false
$xmlRdr->value => ''
$xmlRdr->hasAttributes => false

B) <tag />
$xmlRdr->isEmptyElement => true
$xmlRdr->hasValue => false
$xmlRdr->value => ''
$xmlRdr->hasAttributes => false

C) <tag attribute="value"></tag>
$xmlRdr->isEmptyElement => false
$xmlRdr->hasValue => false
$xmlRdr->value => ''
$xmlRdr->hasAttributes => true

D) <tag attribute="value" />
$xmlRdr->isEmptyElement => true
$xmlRdr->hasValue => false
$xmlRdr->value => ''
$xmlRdr->hasAttributes => true

... and always use the '===' operator when testing properties
up
2
lee8oi at gmail dot com
12 years ago
Sometimes you have an unusual URL that doesn't actually point to an xml file but still returns xml as output (Like the Battlefield Heroes generated syndication urls). Using get_file_contents(url) you can retrieve the xml data from these urls and pass it as a variable for processing as an XML String.

Unfortunately simpleXML or xml DOM cannot process all xml strings. Some have error boxes added to the end of them (such as Battlefield Heroes syndicated news). These boxes cause an end of file sort of error and closes out the script. XMLReader grabs data from these strings without error.
up
0
eef dot vreeland at gmail dot com
7 years ago
Note that when:

A) <tag></tag>
$xmlRdr->isEmptyElement => false
$xmlRdr->hasValue => true
$xmlRdr->value => ''
$xmlRdr->hasAttributes => false

B) <tag />
$xmlRdr->isEmptyElement => true
$xmlRdr->hasValue => false
$xmlRdr->value => ''
$xmlRdr->hasAttributes => false

C) <tag attribute="value"></tag>
$xmlRdr->isEmptyElement => false
$xmlRdr->hasValue => false
$xmlRdr->value => ''
$xmlRdr->hasAttributes => true

D) <tag attribute="value" />
$xmlRdr->isEmptyElement => true
$xmlRdr->hasValue => false
$xmlRdr->value => ''
$xmlRdr->hasAttributes => true
up
0
casella dot email at google dot mail dot com
12 years ago
To verify that all nodes are read without error/warning you can use this code:
<?php
$endofxml
= false;
$xml_url = "example.xml";
$reader = new XMLReader();
if(!
$reader->open($xml_url)){
print
"Error to open XML: $xml_url\n";
} else {
while (
$reader->read()) {
$firstnode = (!isset($firstnode)) ? $reader->name : $firstnode;
/*
DO SOMETHING
*/
if ($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == $firstnode) {
$endofxml = true;
}
}
}
if(
$endofxml) {
print
"no error found";
} else {
print
"error found";
}
?>

This code is useful to trap $reader->read() error/warning.
up
0
PxL
15 years ago
A basic parser

<?php
function xml2assoc($xml) {
$arr = array();
if (!
preg_match_all('|\<\s*?(\w+).*?\>(.*)\<\/\s*\\1.*?\>|s', $xml, $m)) return $xml;
if (
is_array($m[1]))
for (
$i = 0;$i < sizeof($m[1]); $i++) $arr[$m[1][$i]] = xml2assoc($m[2][$i]);
else
$arr[$m[1]] = xml2assoc($m[2]);

return
$arr;
}
?>
up
0
boukeversteegh at gmail dot com
15 years ago
XML to ASSOCIATIVE ARRAY

Improved algorithm based on Sergey Aikinkulov's. The problem was that it would overwrite nodes if they had the same tag name. Because of that <a><b/><b/><a> would be read as if <a><b/><a/>. This algorithm handles it better and outputs an easy to understand array:

<?php
function xml2assoc($xml) {
$tree = null;
while(
$xml->read())
switch (
$xml->nodeType) {
case
XMLReader::END_ELEMENT: return $tree;
case
XMLReader::ELEMENT:
$node = array('tag' => $xml->name, 'value' => $xml->isEmptyElement ? '' : xml2assoc($xml));
if(
$xml->hasAttributes)
while(
$xml->moveToNextAttribute())
$node['attributes'][$xml->name] = $xml->value;
$tree[] = $node;
break;
case
XMLReader::TEXT:
case
XMLReader::CDATA:
$tree .= $xml->value;
}
return
$tree;
}

?>

Usage:

myxml.xml:
------
<PERSON>
<NAME>John</NAME>
<PHONE type="home">555-555-555</PHONE>
</PERSON>
----

<?
$xml = new XMLReader();
$xml->open('myxml.xml');
$assoc = xml2assoc($xml);
$xml->close();
print_r($assoc);
?>

Outputs:
Array
(
[0] => Array
(
[tag] => PERSON
[value] => Array
(
[0] => Array
(
[tag] => NAME
[value] => John
)

[1] => Array
(
[tag] => PHONE
[value] => 555-555-555
[attributes] => Array
(
[type] => home
)

)

)

)

)

For reasons that have to do with recursion, it returns an array with the ROOT xml node as the first childNode, rather than to return only the ROOT node.
up
0
andrei_antal at yahoo dot com
15 years ago
<?php
//Pull certain elements
$reader = new XMLReader();
$reader->open($xmlfile);
while (
$reader->read()) {
switch (
$reader->nodeType) {
case (
XMLREADER::ELEMENT):

if (
$reader->name == "Code")
{
$reader->read();
$code = trim($reader->value);
echo
"$code\n";
break;
}

if (
$reader->name == "Name")
{
$reader->read();
$customername = trim( $reader->value );
echo
"$name\n";
break;
}

if (
$reader->name == "Camp")
{
$camp = trim($reader->getAttribute("ID"));
echo
"$camp\n";
break;
}
}
}
?>
up
0
Sergey Aikinkulov
16 years ago
Next version xml2assoc with some improve fixes:
- no doubled data
- no buffer arrays

<?php
/*
Read XML structure to associative array
--
Using:
$xml = new XMLReader();
$xml->open([XML file]);
$assoc = xml2assoc($xml);
$xml->close();
*/
function xml2assoc($xml) {
$assoc = null;
while(
$xml->read()){
switch (
$xml->nodeType) {
case
XMLReader::END_ELEMENT: return $assoc;
case
XMLReader::ELEMENT:
$assoc[$xml->name][] = array('value' => $xml->isEmptyElement ? '' : xml2assoc($xml));
if(
$xml->hasAttributes){
$el =& $assoc[$xml->name][count($assoc[$xml->name]) - 1];
while(
$xml->moveToNextAttribute()) $el['attributes'][$xml->name] = $xml->value;
}
break;
case
XMLReader::TEXT:
case
XMLReader::CDATA: $assoc .= $xml->value;
}
}
return
$assoc;
}
?>
up
0
itari
16 years ago
<?php
function parseXML($node,$seq,$path) {
global
$oldpath;
if (!
$node->read())
return;
if (
$node->nodeType != 15) {
print
'<br/>'.$node->depth;
print
'-'.$seq++;
print
' '.$path.'/'.($node->nodeType==3?'text() = ':$node->name);
print
$node->value;
if (
$node->hasAttributes) {
print
' [hasAttributes: ';
while (
$node->moveToNextAttribute()) print '@'.$node->name.' = '.$node->value.' ';
print
']';
}
if (
$node->nodeType == 1) {
$oldpath=$path;
$path.='/'.$node->name;
}
parseXML($node,$seq,$path);
}
else
parseXML($node,$seq,$oldpath);
}

$source = "<tag1>this<tag2 id='4' name='foo'>is</tag2>a<tag2 id='5'>common</tag2>record</tag1>";
$xml = new XMLReader();
$xml->XML($source);
print
htmlspecialchars($source).'<br/>';
parseXML($xml,0,'');
?>

Output:

<tag1>this<tag2 id='4' name='foo'>is</tag2>a<tag2 id='5'>common</tag2>record</tag1>

0-0 /tag1
1-1 /tag1/text() = this
1-2 /tag1/tag2 [hasAttributes: @id = 4 @name = foo ]
2-3 /tag1/text() = is
1-4 /text() = a
1-5 /tag2 [hasAttributes: @id = 5 ]
2-6 /text() = common
1-7 /text() = record
To Top