This is an XML handler that I wrote quite a while ago. It can either parse or create XML for you and is pretty darn fast, although I wouldn't go head to head with any of the new PHP5 XML handlers. It will run perfectly in PHP4 with no external lib requirements.
I'll put up some code on how to use it in my next post.
<?php
class xmlNode {
var $tag = "unassigned";
var $data = "";
var $children = array();
var $attributes = array();
var $parentID = 0;
var $mID = 0;
var $handler = null;
var $attributeNames = array();
var $attributeValues = array();
function xmlNode($theHandler)
{
$this->handler = $theHandler;
}
function _attribStr()
{
$out = "";
$theCount = count($this->attributeNames);
for ($i=0; $i<$theCount; $i++)
{
if ($this->attributeValues[$i])
{
$out .= ' ' . $this->attributeNames[$i] . '="' . $this->attributeValues[$i] . '"';
}
}
return $out;
}
function _nextIsClose()
{
// This function is called by nodes that want to know if the next
// item in the tag list is actually <themseleves> closing...
$ptr = $_SERVER['inputPtr'];
return (($_SERVER['inputXML'][$ptr] == '<') and ($_SERVER['inputXML'][$ptr + 1] == '/'));
}
function addChild($theTag, $theValue)
{
$myPtr = count($this->children);
$theHandler = &$this->handler;
$masterPtr = $theHandler->__newXMLNode();
$this->children[$myPtr] = $masterPtr;
$theArray = &$theHandler->__xmlNodeArray;
$tempNode = $theArray[$masterPtr];
$tempNode->parentID = $this->mID;
$tempNode->tag = $theTag;
$tempNode->data = $theValue;
return $masterPtr;
}
function addChildPtr($thePtr)
{
$ptr = count($this->children);
$this->children[$ptr] = $thePtr;
}
function attributeValue($attrName)
{
$attrName = strtolower($attrName);
for ($i=0; $i<count($this->attributeNames); $i++)
{
if ($this->attributeNames[$i] == $attrName)
{
return $this->attributeValues[$i];
}
}
return '';
}
function buildXML($indent)
{
$myIndent = '';
if (! $this->handler->forAjaxMS)
{
$myIndent = substr($this->handler->indentTemplate, 0, $indent * 4);
}
$out = $myIndent . '<' . $this->tag . $this->_attribStr();
if ( (strlen($this->data) ==0) and (!count($this->children)) )
{
$out .= '/>';
$_SERVER['outputArray'][] = $out;
return;
}
// I have children...
if ($this->children)
{
$_SERVER['outputArray'][] = $out . '>';
$out = '';
$theCount = count($this->children);
$theHandler = &$this->handler;
$theTag = $this->tag;
for ($i=0; $i<$theCount; $i++)
{
$childPtr = $this->children[$i];
$theNode = &$theHandler->__xmlNodeArray[$childPtr];
$theNode->buildXML($indent + 1);
}
$_SERVER['outputArray'][] = $myIndent . '</' . $this->tag . '>';
return false;
}
$out .= '>' . $this->data . '</' . $this->tag . '>';
$_SERVER['outputArray'][] = $out;
}
function dump()
{
$out = "tag=" . $this->tag . ', ';
$out .= 'attribs=[' . trim($this->_attribStr()) . '] ';
$out .= 'data="' . $this->data . '" ';
if ($this->children)
{
$theCount = count($this->children);
$out .= chr(10);
for ($i=0; $i<$theCount; $i++)
{
if ($i > 0) { $out .= chr(10); }
$out .= " child[" . $i . '] = ' . $this->children[$i];
}
} else $out .= " No children";
return $out;
}
function hasChildren() { return (count($this->children) > 0); }
function importNode()
{
// Get the entire opening tag...
$workPtr = $_SERVER['inputPtr'] + 1;
$workPtr--;
$startPos = $workPtr;
$endOfTag = strpos($_SERVER['inputXML'], '>', $workPtr);
$realLen = $endOfTag - $startPos;
$workStr = substr($_SERVER['inputXML'], $startPos, $realLen);
$workPtr = $endOfTag + 1;
$_SERVER['inputPtr'] = $workPtr;
// Find where the name of the tag ends and grab it...
$emptyTag = false;
if (strpos($workStr, '/') > 0)
{
$emptyTag = true;
$workStr = substr($workStr, 0, strlen($workStr) - 1);
}
$ptr = strlen($workStr);
$space = strpos($workStr, ' ');
if (!$space) { $space = 99999; }
if ($space < $ptr) { $ptr = $space; }
$this->tag = strtolower(trim(substr($workStr, 1, $ptr)));
$workStr = substr($workStr, $ptr + 1, 1024);
// set the start-of-search pointer to where we just ended the tag name...
$startSearch = $ptr;
// Time for the attributes...
$attrArr = explode('" ', $workStr);
$this->attributeNames = array();
$this->attributeValues = array();
for ($i=0; $i<count($attrArr); $i++)
{
if (! strpos($attrArr[$i], '=')) { continue; }
$ptr = count($this->attributeNames);
$parts = explode('=', $attrArr[$i]);
$this->attributeNames[$ptr] = trim(strtolower($parts[0]));
$this->attributeValues[$ptr] = str_replace('"', '', $parts[1]);
}
// If I am an empty tag then there is no more to do...
if ($emptyTag) { return false; }
// If the next character in the main buffer is anything other than a '<' then there
// is text for me to collect, and the tag must, by XML rules, be all done...
if ($_SERVER['inputXML'][$workPtr] <> '<')
{
$textEnd = strpos($_SERVER['inputXML'], '<', $workPtr);
$realLen = $textEnd - $workPtr;
$this->data = substr($_SERVER['inputXML'], $workPtr, $realLen);
$ptr = $_SERVER['inputPtr'] + $realLen + 1;
$ptr = strpos($_SERVER['inputXML'], '>', $ptr) + 1;
$_SERVER['inputPtr'] = $ptr;
return false;
}
// OK: If I am here, then either there are children OR the very next tag is <me> closing
// and I was empty after all.
while ((!$this->_nextIsClose()) && ($_SERVER['inputPtr'] < $_SERVER['inputEnd']))
{
$masterPtr = $this->addChild('', '');
$theHandler = &$this->handler;
$tempNode = &$theHandler->__xmlNodeArray[$masterPtr];
$tempNode->parentID = $this->mID;
$tempNode->importNode();
}
// Right here we have allowed all children to import themselves and
// <my> closing tag is all that is left...
$workPtr = $_SERVER['inputPtr'] + 1;
$workPtr = strpos($_SERVER['inputXML'], '>', $workPtr) + 1;
$_SERVER['inputPtr'] = $workPtr;
}
function updateAttribute($theName, $theValue)
{
$theName = strtolower($theName);
$theCount = count($this->attributeNames);
for ($i=0; $i<$theCount; $i++)
{
if ($this->attributeNames[$i] == $theName)
{
$this->attributeValues[$i] = $theValue;
return true;
}
}
// It isn't in the list yet...
$this->attributeNames[$theCount] = $theName;
$this->attributeValues[$theCount] = $theValue;
}
}
class xmlHandler {
var $__xmlNodeArray = array();
var $rootPtr = 0;
var $rootNode = null;
var $currentPtr = 0;
var $originalXML = '';
var $forAjaxMS = false; // Used when formatting output...
var $indentTemplate = ' ';
function xmlHandler() { $this->clear(); }
function __newXMLNode()
{
$ptr = count($this->__xmlNodeArray);
$tempNode = new xmlNode($this);
$tempNode->mID = $ptr;
$tempNode->handler = &$this;
$this->__xmlNodeArray[$ptr] = &$tempNode;
return $ptr;
}
function addChild($tagName, $tagValue, $stepIn=false)
{
$currentNode = &$this->__xmlNodeArray[$this->currentPtr];
$currentChildren = &$currentNode->children;
$newPtr = $this->__newXMLNode();
$newNode = &$this->__xmlNodeArray[$newPtr];
$newNode->tag = $tagName;
$newNode->data = $tagValue;
$newNode->parentID = $currentNode->mID;
$currentNode->addChildPtr($newPtr);
if ($stepIn)
{
$this->currentPtr = $newPtr;
}
return $newPtr;
}
function attributeValue($theName)
{
$theNode = &$this->__xmlNodeArray[$this->currentPtr];
return $theNode->attributeValue($theName);
}
function childData($theIDX)
{
$tempNode = &$this->__xmlNodeArray[$this->currentPtr];
$childPtr = $tempNode->children[$theIDX];
$tempNode = &$this->__xmlNodeArray[$childPtr];
return $tempNode->data;
}
function childDataNamed($tagName)
{
$retData = "";
$holdPtr = $this->currentPtr;
if ($this->stepInto($tagName))
{
$retData = $this->currentData();
$this->currentPtr = $holdPtr;
}
return $retData;
}
function &childNode($theIDX)
{
$retNode = null;
$theNode = &$this->__xmlNodeArray[$this->currentPtr];
if (($theIDX >=0) and ($theIDX < count($theNode->children)))
{
$ptr = $theNode->children[$theIDX];
$retNode = &$this->__xmlNodeArray[$ptr];
}
return $retNode;
}
function childNodeNamed($tagName)
{
$retNode = null;
$holdPtr = $this->currentPtr;
if ($this->stepInto($tagName) )
{
$retNode = &$this->__xmlNodeArray[$this->currentPtr];
$this->currentPtr = $holdPtr;
}
return $retNode;
}
function childTag($theIDX)
{
$tempNode = &$this->__xmlNodeArray[$this->currentPtr];
$childPtr = $tempNode->children[$theIDX];
$tempNode = &$this->__xmlNodeArray[$childPtr];
return $tempNode->tag;
}
function clear()
{
$this->__xmlNodeArray = array();
$this->rootPtr = $this->__newXMLNode();
$this->rootNode = &$this->__xmlNodeArray[0];