]+)([^>]+)?>(.*?)<\s*\/\\1\s*>\s*([^<]+)?(.*)|\s*\s*|^\s*<\s*([^\s>]+)([^>]*)\/\s*>\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*\s*(.*)|^\s*\s*(.*)|^\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 ** 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: ** ** ** ** ** ** ** ** DA42 ** ** ** D99983FFF ** ** ** ss-839uent ** ** ** ** ** $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('/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 = ""; } else { $xmlhead = "\n "; } $search = array("/]*)>\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(); } } ?>