2erlei/app/helper/classes/doc.inc.php

861 lines
21 KiB
PHP

<?php
/**
* MiniXML - PHP class library for generating and parsing XML.
*
* @package miniXML
* @author Patrick Deegan, Psychogenic.com
* @license GPL
*/
/*
#define("MINIXML_COMPLETE_REGEX",'/<\s*([^\s>]+)([^>]+)?>(.*?)<\s*\/\\1\s*>\s*([^<]+)?(.*)|\s*<!--(.+?)-->\s*|^\s*<\s*([^\s>]+)([^>]*)\/\s*>\s*([^<>]+)?|<!\[CDATA\s*\[(.*?)\]\]\s*>|<!DOCTYPE\s*([^\[]*)\[(.*?)\]\s*>|<!ENTITY\s*([^"\'>]+)\s*(["\'])([^\14]+)\14\s*>|^([^<]+)(.*)/smi');
*/
define("MINIXML_COMPLETE_REGEX",'/^\s*<\s*([^\s>]+)(\s+[^>]+)?>(.*?)<\s*\/\1\s*>\s*([^<]+)?(.*)|^\s*<!--(.+?)-->\s*(.*)|^\s*<\s*([^\s>]+)([^>]+)\/\s*>\s*(.*)|^\s*<!\[CDATA\s*\[(.*?)\]\]\s*>\s*(.*)|^\s*<!DOCTYPE\s*([^\[]*)\[(.*?)\]\s*>\s*(.*)|^\s*<!ENTITY\s*([^"\'>]+)\s*(["\'])([^\17]+)\17\s*>\s*(.*)|^([^<]+)(.*)/smi');
/*
#define("MINIXML_SIMPLE_REGEX",
# // 1 2 3 4 5 6 7 8 9 #10 11
#'/\s*<\s*([^\s>]+)([^>]+)?>(.*?)<\s*\/\\1\s*>\s*([^<]+)?(.*)|\s*<!--(.+?)-->\s*|\s*<\s*([^\s>]+)([^>]*)\/\s*>\s*([^<>]+)?|^([^<]+)(.*)/smi');
*/
define("MINIXML_SIMPLE_REGEX",'/^\s*<\s*([^\s>]+)(\s+[^>]+)?>(.*?)<\s*\/\1\s*>\s*([^<]+)?(.*)|^\s*<!--(.+?)-->\s*(.*)|^\s*<\s*([^\s>]+)([^>]+)\/\s*>\s*(.*)|^([^<]+)(.*)/smi');
require_once(MINIXML_CLASSDIR . "/element.inc.php");
/***************************************************************************************************
****************************************************************************************************
*****
***** MiniXMLDoc
*****
****************************************************************************************************
***************************************************************************************************/
/* MiniXMLDoc class
**
** The MiniXMLDoc class is the programmer's handle to MiniXML functionality.
**
** A MiniXMLDoc instance is created in every program that uses MiniXML.
** With the MiniXMLDoc object, you can access the root MiniXMLElement,
** find/fetch/create elements and read in or output XML strings.
**/
class MiniXMLDoc {
var $xxmlDoc;
var $xuseSimpleRegex;
var $xRegexIndex;
/* MiniXMLDoc [XMLSTRING]
** Constructor, create and init a MiniXMLDoc object.
**
** If the optional XMLSTRING is passed, the document will be initialised with
** a call to fromString using the XMLSTRING.
**
*/
function MiniXMLDoc ($string=NULL)
{
/* Set up the root element - note that it's name get's translated to a
** <? xml version="1.0" ?> string.
*/
$this->xxmlDoc = new MiniXMLElement("PSYCHOGENIC_ROOT_ELEMENT");
$this->xuseSimpleRegex = MINIXML_USE_SIMPLE;
if (! is_null($string))
{
$this->fromString($string);
}
}
function init ()
{
$this->xxmlDoc = new MiniXMLElement("PSYCHOGENIC_ROOT_ELEMENT");
}
/* getRoot
** Returns a reference the this document's root element
** (an instance of MiniXMLElement)
*/
function &getRoot ()
{
return $this->xxmlDoc;
}
/* setRoot NEWROOT
** Set the document root to the NEWROOT MiniXMLElement object.
**/
function setRoot (&$root)
{
if ($this->isElement($root))
{
$this->xxmlDoc = $root;
} else {
return _MiniXMLError("MiniXMLDoc::setRoot(): Trying to set non-MiniXMLElement as root");
}
}
/* isElement ELEMENT
** Returns a true value if ELEMENT is an instance of MiniXMLElement,
** false otherwise.
*/
function isElement (&$testme)
{
if (is_null($testme))
{
return 0;
}
return method_exists($testme, 'MiniXMLElement');
}
/* isNode NODE
** Returns a true value if NODE is an instance of MiniXMLNode,
** false otherwise.
*/
function isNode (&$testme)
{
if (is_null($testme))
{
return 0;
}
return method_exists($testme, 'MiniXMLNode');
}
/* createElement NAME [VALUE]
** Creates a new MiniXMLElement with name NAME.
** This element is an orphan (has no assigned parent)
** and will be lost unless it is appended (MiniXMLElement::appendChild())
** to an element at some point.
**
** If the optional VALUE (string or numeric) parameter is passed,
** the new element's text/numeric content will be set using VALUE.
**
** Returns a reference to the newly created element (use the =& operator)
*/
function &createElement ($name=NULL, $value=NULL)
{
$newElement = new MiniXMLElement($name);
if (! is_null($value))
{
if (is_numeric($value))
{
$newElement->numeric($value);
} elseif (is_string($value))
{
$newElement->text($value);
}
}
return $newElement;
}
/* getElement NAME
** Searches the document for an element with name NAME.
**
** Returns a reference to the first MiniXMLElement with name NAME,
** if found, NULL otherwise.
**
** NOTE: The search is performed like this, returning the first
** element that matches:
**
** - Check the Root Element's immediate children (in order) for a match.
** - Ask each immediate child (in order) to MiniXMLElement::getElement()
** (each child will then proceed similarly, checking all it's immediate
** children in order and then asking them to getElement())
*/
function &getElement ($name)
{
$element = $this->xxmlDoc->getElement($name);
if (MINIXML_DEBUG > 0)
{
_MiniXMLLog("MiniXMLDoc::getElement(): Returning element $element");
}
return $element;
}
/* getElementByPath PATH
** Attempts to return a reference to the (first) element at PATH
** where PATH is the path in the structure from the root element to
** the requested element.
**
** For example, in the document represented by:
**
** <partRateRequest>
** <vendor>
** <accessid user="myusername" password="mypassword" />
** </vendor>
** <partList>
** <partNum>
** DA42
** </partNum>
** <partNum>
** D99983FFF
** </partNum>
** <partNum>
** ss-839uent
** </partNum>
** </partList>
** </partRateRequest>
**
** $accessid =& $xmlDocument->getElementByPath('partRateRequest/vendor/accessid');
**
** Will return what you expect (the accessid element with attributes user = "myusername"
** and password = "mypassword").
**
** BUT be careful:
** $accessid =& $xmlDocument->getElementByPath('partRateRequest/partList/partNum');
**
** will return the partNum element with the value "DA42". Other partNums are
** inaccessible by getElementByPath() - Use MiniXMLElement::getAllChildren() instead.
**
** Returns the MiniXMLElement reference if found, NULL otherwise.
*/
function &getElementByPath ($path)
{
$element = $this->xxmlDoc->getElementByPath($path);
if (MINIXML_DEBUG > 0)
{
_MiniXMLLog("Returning element $element");
}
return $element;
}
function fromFile ($filename)
{
$modified = stat($filename);
if (! is_array($modified))
{
_MiniXMLError("Can't stat '$filename'");
return NULL;
}
if (MINIXML_USEFROMFILECACHING > 0)
{
$tmpName = MINIXML_FROMFILECACHEDIR . '/' . 'minixml-' . md5($filename);
if (MINIXML_DEBUG > 0)
{
_MiniXMLLog("Trying to open cach file $tmpName (for '$filename')");
}
$cacheFileStat = stat($tmpName);
if (is_array($cacheFileStat) && $cacheFileStat[9] > $modified[9])
{
$fp = @fopen($tmpName,"r");
if ($fp)
{
if (MINIXML_DEBUG > 0)
{
_MiniXMLLog("Reading file '$filename' from object cache instead ($tmpName)");
}
$tmpFileSize = filesize($tmpName);
$tmpFileContents = fread($fp, $tmpFileSize);
$serializedObj = unserialize($tmpFileContents);
$sRoot =& $serializedObj->getRoot();
if ($sRoot)
{
if (MINIXML_DEBUG > 0)
{
_MiniXMLLog("Restoring object from cache file $tmpName");
}
$this->setRoot($sRoot);
/* Return immediately, such that we don't refresh the cache */
return $this->xxmlDoc->numChildren();
} /* end if we got a root element from unserialized object */
} /* end if we sucessfully opened the file */
} /* end if cache file exists and is more recent */
}
ob_start();
readfile($filename);
$filecontents = ob_get_contents();
ob_end_clean();
$retVal = $this->fromString($filecontents);
if (MINIXML_USEFROMFILECACHING > 0)
{
$this->saveToCache($filename);
}
return $retVal;
}
function saveToCache ($filename)
{
$tmpName = MINIXML_FROMFILECACHEDIR . '/' . 'minixml-' . md5($filename);
$fp = @fopen($tmpName, "w");
if (MINIXML_DEBUG > 0)
{
_MiniXMLLog("Saving object to cache as '$tmpName'");
}
if ($fp)
{
$serialized = serialize($this);
fwrite($fp, $serialized);
fclose($fp);
} else {
_MiniXMLError("Could not open $tmpName for write in MiniXMLDoc::saveToCache()");
}
}
/* fromString XMLSTRING
**
** Initialise the MiniXMLDoc (and it's root MiniXMLElement) using the
** XML string XMLSTRING.
**
** Returns the number of immediate children the root MiniXMLElement now
** has.
*/
function fromString (&$XMLString)
{
$useSimpleFlag = $this->xuseSimpleRegex;
if ($this->xuseSimpleRegex || ! preg_match('/<!DOCTYPE|<!ENTITY|<!\[CDATA/smi', $XMLString))
{
$this->xuseSimpleRegex = 1;
$this->xRegexIndex = array(
'biname' => 1,
'biattr' => 2,
'biencl' => 3,
'biendtxt' => 4,
'birest' => 5,
'comment' => 6,
'uname' => 8,
'uattr' => 9,
'plaintxt' => 11,
'plainrest' => 12
);
$regex = MINIXML_SIMPLE_REGEX;
} else {
$this->xRegexIndex = array(
'biname' => 1,
'biattr' => 2,
'biencl' => 3,
'biendtxt' => 4,
'birest' => 5,
'comment' => 6,
'uname' => 8,
'uattr' => 9,
'cdata' => 11,
'doctypedef' => 13,
'doctypecont' => 14,
'entityname' => 16,
'entitydef' => 18,
'plaintxt' => 20,
'plainrest' => 21
);
$regex = MINIXML_COMPLETE_REGEX;
}
$this->fromSubString($this->xxmlDoc, $XMLString, $regex);
$this->xuseSimpleRegex = $useSimpleFlag;
return $this->xxmlDoc->numChildren();
}
function fromArray (&$init, $params=NULL)
{
$this->init();
if (! is_array($init) )
{
return _MiniXMLError("MiniXMLDoc::fromArray(): Must Pass an ARRAY to initialize from");
}
if (! is_array($params) )
{
$params = array();
}
if ( $params["attributes"] && is_array($params["attributes"]) )
{
$attribs = array();
foreach ($params["attributes"] as $attribName => $value)
{
if (! (array_key_exists($attribName, $attribs) && is_array($attribs[$attribName]) ) )
{
$attribs[$attribName] = array();
}
if (is_array($value))
{
foreach ($value as $v)
{
if (array_key_exists($v, $attribs[$attribName]))
{
$attribs[$attribName][$v]++;
} else {
$attribs[$attribName][$v] = 1;
}
}
} else {
if (array_key_exists($value,$attribs[$attribName]))
{
$attribs[$attribName][$value]++;
} else {
$attribs[$attribName][$value] = 1;
}
}
}
// completely replace old attributes by our optimized array
$params["attributes"] = $attribs;
} else {
$params["attributes"] = array();
}
foreach ($init as $keyname => $value)
{
$sub = $this->_fromArray_getExtractSub($value);
$this->$sub($keyname, $value, $this->xxmlDoc, $params);
}
return $this->xxmlDoc->numChildren();
}
function _fromArray_getExtractSub ($v)
{
// is it a string, a numerical array or an associative array?
$sub = "_fromArray_extract";
if (is_array($v))
{
if (_MiniXML_NumKeyArray($v))
{
// All numeric - assume it is a "straight" array
$sub .= "ARRAY";
} else {
$sub .= "AssociativeARRAY";
}
} else {
$sub .= "STRING";
}
return $sub;
}
function _fromArray_extractAssociativeARRAY ($name, &$value, &$parent, &$params)
{
$thisElement =& $parent->createChild($name);
foreach ($value as $key => $val)
{
$sub = $this->_fromArray_getExtractSub($val);
$this->$sub($key, $val, $thisElement, $params);
}
return;
}
function _fromArray_extractARRAY ($name, &$value, &$parent, &$params)
{
foreach ($value as $val)
{
$sub = $this->_fromArray_getExtractSub($val);
$this->$sub($name, $val, $parent, $params);
}
return;
}
function _fromArray_extractSTRING ($name, $value="", &$parent, &$params)
{
$pname = $parent->name();
if (
( array_key_exists($pname, $params['attributes']) && is_array($params['attributes'][$pname])
&& array_key_exists($name, $params['attributes'][$pname]) && $params['attributes'][$pname][$name])
|| (
array_key_exists('-all', $params['attributes']) && is_array($params['attributes']['-all'])
&& array_key_exists($name, $params['attributes']['-all']) && $params['attributes']['-all'][$name])
)
{
$parent->attribute($name, $value);
} elseif ($name == '-content') {
$parent->text($value);
} else {
$parent->createChild($name, $value);
}
return;
}
function time ($msg)
{
error_log("\nMiniXML msg '$msg', time: ". time() . "\n");
}
// fromSubString PARENTMINIXMLELEMENT XMLSUBSTRING
// private method, called recursively to parse the XMLString in little sub-chunks.
function fromSubString (&$parentElement, &$XMLString, &$regex)
{
//$this->time('fromSubStr');
if (is_null($parentElement) || empty($XMLString) || preg_match('/^\s*$/', $XMLString))
{
return;
}
if (MINIXML_DEBUG > 0)
{
_MiniXMLLog("Called fromSubString() with parent '" . $parentElement->name() . "'\n");
}
$matches = array();
if (preg_match_all( $regex, $XMLString, $matches))
{
// $this->time('a match');
$mcp = $matches;
$numMatches = count($mcp[0]);
for($i=0; $i < $numMatches; $i++)
{
if (MINIXML_DEBUG > 1)
{
_MiniXMLLog ("Got $numMatches CHECKING: ". $mcp[0][$i] . "\n");
}
$uname = $mcp[$this->xRegexIndex['uname']][$i];
$comment = $mcp[$this->xRegexIndex['comment']][$i];
if ($this->xuseSimpleRegex)
{
$cdata = NULL;
$doctypecont = NULL;
$entityname = NULL;
$tailEndIndexes = array(5, 7, 10, 12);
} else {
$cdata = $mcp[$this->xRegexIndex['cdata']][$i];
$doctypecont = $mcp[$this->xRegexIndex['doctypecont']][$i];
$entityname = $mcp[$this->xRegexIndex['entityname']][$i];
$tailEndIndexes = array(5, 7, 10, 12, 15, 19, 21);
}
$plaintext = $mcp[$this->xRegexIndex['plaintxt']][$i];
// check all the 'tailend' (i.e. rest of string) matches for more content
$moreContent = '';
$idx = 0;
while (empty($moreContent) && ($idx < count($tailEndIndexes)))
{
if (! empty($mcp[$tailEndIndexes[$idx]][$i]))
{
$moreContent = $mcp[$tailEndIndexes[$idx]][$i];
}
$idx++;
}
if ($uname)
{
// _MiniXMLLog ("Got UNARY $uname");
$newElement =& $parentElement->createChild($uname);
$this->_extractAttributesFromString($newElement, $mcp[$this->xRegexIndex['uattr']][$i]);
} elseif ($comment) {
//_MiniXMLLog ("Got comment $comment");
$parentElement->comment($comment);
} elseif ($cdata) {
//_MiniXMLLog ("Got cdata $cdata");
$newElement = new MiniXMLElementCData($cdata);
$parentElement->appendChild($newElement);
} elseif ($doctypecont) {
//_MiniXMLLog ("Got doctype $doctypedef '" . $mcp[11][$i] . "'");
$newElement = new MiniXMLElementDocType($mcp[$this->xRegexIndex['doctypedef']][$i]);
$appendedChild =& $parentElement->appendChild($newElement);
$this->fromSubString($appendedChild, $doctypecont, $regex);
} elseif ($entityname ) {
//_MiniXMLLog ("Got entity $entityname");
$newElement = new MiniXMLElementEntity ($entityname, $mcp[$this->xRegexIndex['entitydef']][$i]);
$parentElement->appendChild($newElement);
} elseif ($plaintext) {
if (! preg_match('/^\s+$/', $plaintext))
{
$parentElement->createNode($plaintext);
}
} elseif($mcp[$this->xRegexIndex['biname']]) {
// _MiniXMLLog("Got BIN NAME: " . $mcp[$this->xRegexIndex['biname']][$i]);
$nencl = $mcp[$this->xRegexIndex['biencl']][$i];
$finaltxt = $mcp[$this->xRegexIndex['biendtxt']][$i];
$newElement =& $parentElement->createChild($mcp[$this->xRegexIndex['biname']][$i]);
$this->_extractAttributesFromString($newElement, $mcp[$this->xRegexIndex['biattr']][$i]);
$plaintxtMatches = array();
if (preg_match("/^\s*([^\s<][^<]*)/", $nencl, $plaintxtMatches))
{
$txt = $plaintxtMatches[1];
$newElement->createNode($txt);
$nencl = preg_replace("/^\s*([^<]+)/", "", $nencl);
}
if ($nencl && !preg_match('/^\s*$/', $nencl))
{
$this->fromSubString($newElement, $nencl, $regex);
}
if ($finaltxt)
{
$parentElement->createNode($finaltxt);
}
} /* end switch over type of match */
if (! empty($moreContent))
{
$this->fromSubString($parentElement, $moreContent, $regex);
}
} /* end loop over all matches */
} /* end if there was a match */
} /* end method fromSubString */
/* toString [DEPTH]
** Converts this MiniXMLDoc object to a string and returns it.
**
** The optional DEPTH may be passed to set the space offset for the
** first element.
**
** If the optional DEPTH is set to MINIXML_NOWHITESPACES.
** When it is, no \n or whitespaces will be inserted in the xml string
** (ie it will all be on a single line with no spaces between the tags.
**
** Returns a string of XML representing the document.
*/
function toString ($depth=0)
{
$retString = $this->xxmlDoc->toString($depth);
if ($depth == MINIXML_NOWHITESPACES)
{
$xmlhead = "<?xml version=\"1.0\"\\1?>";
} else {
$xmlhead = "<?xml version=\"1.0\"\\1?>\n ";
}
$search = array("/<PSYCHOGENIC_ROOT_ELEMENT([^>]*)>\s*/smi",
"/<\/PSYCHOGENIC_ROOT_ELEMENT>/smi");
$replace = array($xmlhead,
"");
$retString = preg_replace($search, $replace, $retString);
if (MINIXML_DEBUG > 0)
{
_MiniXMLLog("MiniXML::toString() Returning XML:\n$retString\n\n");
}
return $retString;
}
/* toArray
**
** Transforms the XML structure currently represented by the MiniXML Document object
** into an array.
**
** More docs to come - for the moment, use var_dump($miniXMLDoc->toArray()) to see
** what's going on :)
*/
function & toArray ()
{
$retVal = $this->xxmlDoc->toStructure();
if (is_array($retVal))
{
return $retVal;
}
$retArray = array(
'-content' => $retVal,
);
return $retArray;
}
/* getValue()
** Utility function, call the root MiniXMLElement's getValue()
*/
function getValue ()
{
return $this->xxmlDoc->getValue();
}
/* dump
** Debugging aid, dump returns a nicely formatted dump of the current structure of the
** MiniXMLDoc object.
*/
function dump ()
{
return serialize($this);
}
// _extractAttributesFromString
// private method for extracting and setting the attributs from a
// ' a="b" c = "d"' string
function _extractAttributesFromString (&$element, &$attrString)
{
if (! $attrString)
{
return NULL;
}
$count = 0;
$attribs = array();
// Set the attribs
preg_match_all('/([^\s]+)\s*=\s*([\'"])([^\2]*?)\2/sm', $attrString, $attribs);
for ($i = 0; $i < count($attribs[0]); $i++)
{
$attrname = $attribs[1][$i];
$attrval = $attribs[3][$i];
if ($attrname)
{
$element->attribute($attrname, $attrval, '');
$count++;
}
}
return $count;
}
/* Destructor to keep things clean -- patch by Ilya */
function __destruct()
{
$this->xxmlDoc = null;
}
}
/***************************************************************************************************
****************************************************************************************************
*****
***** MiniXML
*****
****************************************************************************************************
***************************************************************************************************/
/* class MiniXML (MiniXMLDoc)
**
** Avoid using me - I involve needless overhead.
**
** Utility class - this is just an name aliase for the
** MiniXMLDoc class as I keep repeating the mistake of
** trying to create
**
** $xml = new MiniXML();
**
*/
class MiniXML extends MiniXMLDoc {
function MiniXML ()
{
$this->MiniXMLDoc();
}
}
?>