From: pkiddie <pk...@us...> - 2005-09-09 11:00:39
|
Update of /cvsroot/stack/stack-1-0/scripts/rqp In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13797/scripts/rqp Added Files: nb_easyxml_lite.php Log Message: --- NEW FILE: nb_easyxml_lite.php --- <?PHP /*!< This set of classes and function is to provide a simple method of * dealing with XML that does not depend on optional libraries */ define("START_TAG",1); define("END_TAG",2); define("CLOSED_TAG",3); define("CONTENT",4); define("XML_ERROR",0); define("XML_DECLARATION",5); // <?xml define("XML_DOCTYPE",6); // <!DOCTYPE ... > define("XML_INSTRUCTION",7); // <? define("XML_CDATA",8); // <[CDATA[ ... ]]> define("XML_COMMENT",9); // <!-- ... --> /*! Base class from which tag and content classes are derived. Never used directly. */ class nb_xml_part{ var $type; //!< Indicates the type of entity - tag, content etc. var $depth; //!< Indicates how deep in the xml tree this entity is. //! A 'pure virtual' function. Derived classes impliment their own version. function dump() { return "<font color='red'>This should never be called!</font><br>"; } }; //! Class for tags in the list of the XML. class nb_xml_tag extends nb_xml_part{ var $tagname; //!< The name of the tag, no namespace prefix var $attribs; //!< An associative array containing attributes keyed by name var $cleansource; //!< the tidy source for the tag. // Next four lines added 28Jun05 NSFB var $nsURI; //!< The namespace URI of the tag if it exists var $nsPrefix; //!< The namespace prefix of the tag if it was used // this is only really meaningful in start elements, potentially used in closed though var $nsDetail; //!< an array of namespaces declared here ['!'] used as prefix for default; /*! Constructor takes a XML tag (without < >) sets $tagname, * $type (START_TAG|END_TAG|CLOSED_TAG), and $attribs, * an associative array of attributes. */ function nb_xml_tag($tagsource) { // initialise to default values $this->nsDetail = array(); $this->nsPrefix = ""; $this->nsURI = false; // "" would be empty, false is undefined // tidy up the ends and make sure the < and > are gone. $tagsource=trim($tagsource); if($tagsource[0]=="<") { $tagsource = substr($tagsource,1); } if($tagsource[strlen($tagsource)-1]==">") { $tagsource = substr($tagsource,0,strlen($tagsource)-1); } $tagsource=trim($tagsource); $this->cleansource=$tagsource; // now check what sort of tag it is... // echo $tagsource[0]; // debug switch($tagsource[0]) { case '?': if(substr($tagsource,0,4)=="?xml"){ $this->type = XML_DECLARATION;} else{ $this->type = XML_INSTRUCTION;} break; case '!': if(substr($tagsource,0,8)=="!DOCTYPE"){ $this->type = XML_DOCTYPE;} elseif(substr($tagsource,0,3)=="!--"){ $this->type = XML_COMMENT;} else{ $this->type = XML_ERROR;} break; case '[': if(substr($tagsource,0,7)=="[CDATA["){ $this->type = XML_CDATA;} else{ $this->type = XML_ERROR;} break; case '/': $this->type = END_TAG; $this->tagname = substr($tagsource,1); // split off namespace part if it exists, added 28Jun05 NSFB if(strpos($this->tagname,":") !== FALSE) list($this->nsPrefix,$this->tagname) = explode(":",$this->tagname,2); else $this->nsPrefix = ""; break; default: //it's a normal tag! if($tagsource[strlen($tagsource)-1]=='/'){ $this->type = CLOSED_TAG; $tagsource = substr($tagsource,0,strlen($tagsource)-1); } else{ $this->type = START_TAG;} // need to extract name and attributes here. if(strpos($tagsource," ")==false) { $nm=$tagsource; $attrs=""; } else list($nm,$attrs)=explode(" ",$tagsource,2); $attrs = preg_replace("/\s+/"," ",$attrs); $attrs = str_replace("= \"","=\"",$attrs); $attrs = str_replace(" =\"","=\"",$attrs); $attrs=trim($attrs); while(strlen($attrs)>2) { if((strpos($attrs," ")!==false)&&((strpos($attrs," ")<strpos($attrs,"=")))) //== would indicate end of attribs, no val { list($attrname,$attrs)=explode(" ",$attrs,2); $attrval=true; } else { list($attrname,$attrs)=explode("=",$attrs,2); $echar=$attrs[0]; $attrs=trim($attrs); if(($echar=="\"")||($echar=="'")) { $attrs = substr($attrs,1); list($attrval,$attrs)=explode($echar,$attrs,2); } else list($attrval,$attrs)=explode(" ",$attrs,2); if($attrval=="") $attrval=true; } $this->attribs[trim($attrname)]=$attrval; $attrs=trim($attrs); // do the namespace collecting stuff, added 28Jun05, NSFB if(strpos($attrname,":")!==false) list($attrname, $prefix) = explode(":",$attrname,2); else $prefix = ""; // so there's a key use this instead of ""; if($attrname=="xmlns") { $this->nsDetail[$prefix] = trim($attrval); } // end of namespace collecting stuff } $this->tagname = $nm; if(strpos($this->tagname,":") !== FALSE) list($this->nsPrefix,$this->tagname) = explode(":",$this->tagname,2); else $this->nsPrefix = ""; // alocate namespace URI if possible , added 28Jun05, NSFB if(array_key_exists($this->nsPrefix,$this->nsDetail)) $this->nsURI = $this->nsDetail[$this->nsPrefix]; break; } } /*! Creates a tidy version of the tag for output. Start, end and closed tags are all correctly formated. Attributes will not nessesaraly be in the original order. \return A string containing the well formated tag. */ function dump() { switch($this->type) { case START_TAG: $rstr = "<".$this->tagname; if(is_array($this->attribs)) { while (list($akey, $aval)=each($this->attribs)) { if(strstr($aval,"\"")===false){ $rstr .= " ".$akey."=\"".$aval."\"";} else{ $rstr .= " ".$akey."='".$aval."'";} } } return $rstr.">"; break; case CLOSED_TAG: $rstr = "<".$this->tagname; if(is_array($this->attribs)) { while (list($akey, $aval)=each($this->attribs)) { if(strstr($aval,"\"")===false){ $rstr .= " ".$akey."=\"".$aval."\"";} else{ $rstr .= " ".$akey."='".$aval."'";} } } else { if(strlen($this->attribs)>0){$rstr .= " ".$this->attribs;} } return $rstr."/>"; break; case END_TAG: return "</".$this->tagname.">"; break; default: return "<".$this->cleansource.">"; break; } } }; /*! Class for content in the XML list - content is everything between tags, but does not include child tags, they become the next item in the list. */ class nb_xml_content extends nb_xml_part{ var $content; /*! constructor takes the content and sets the block type. \param $source is the content. */ function nb_xml_content($source) { $this->content = $source; $this->type = CONTENT; } //! returns the content. function dump() { return $this->content; } } /*! The main class of the XML library reads in XML and provides methods for manipulating it. The XML is held as a list of tags and content, each with a type and depth. */ class nb_easyxml { var $entities; var $source; // C++ private: equiv /*! Get a 'block' of xml from $this->source, either a tag or DATA/CDATA starting * from $point, update point to first char after the 'block' */ function getblock(&$point) { if($this->source[$point]=='<') { //print("<br>tag at $point<br>");//debug switch($this->source[$point+1]) { case '?': $estr = '?'.'>'; // split to be nice to PHP parser; break; case '!': if(substr($this->source,$point+1,3)=="!--"){ $estr = "-->";} elseif($this->source[$point+2]=="["){ $estr = "]]>";} else{ $estr = ">";} break; default: $estr = ">"; break; } $epoint = strpos($this->source,$estr,$point); if($epoint === false) { $point = strlen($this->source); return ""; } $epoint += strlen($estr); $block = substr($this->source,$point,$epoint-$point); $point = $epoint; return $block; } else { $epoint = strpos($this->source,'<',$point); if($epoint === false) { $epoint = strlen($this->source); } $block = substr($this->source,$point,$epoint-$point); $point = $epoint; return $block; } } // C++ protected: equiv /*! Parses XML into the list and does a limited amount of checking. Not really intended to be used from outside the library. \param $source is the xml to be parsed. */ function parsein($source) { $this->entities=""; // this clears out old data. $this->source = $source; $prspt=0; // counter for where in the xml the parser has got to (PaRSe PoinT). $level=0; // how far down the xml tree I am! $nsInfo = array(); // namespace allocation array while($prspt < strlen($this->source)) { $block = $this->getblock($prspt); $printblk = htmlentities($block); // echo "<p><b>".$prspt."</b> ".strlen($block)."<br>".$printblk."</p>";//debug if($block[0] == '<') { $tagcls = new nb_xml_tag($block); $tagcls->depth = $level; if($tagcls->type== START_TAG) { $tagatlev[$level]=$tagcls->tagname; //printf("At level $level got tag '$tagcls->tagname'<br>"); // debug // store namespace info $nsInfo[$level] = $tagcls->nsDetail; $level++; $testlev = $level - 1; // no need to test this level as tag parser does it. while(($tagcls->nsURI === false)&&($testlev >= 0)) { if(array_key_exists($tagcls->nsPrefix, $nsInfo[$testlev])) $tagcls->nsURI = $nsInfo[$testlev][$tagcls->nsPrefix]; $testlev--; } //printf("At level $level got tag '$tagcls->tagname' in namespace {$tagcls->nsURI}<br>"); // debug } $this->entities[] = $tagcls; //$sz = sizeof($this->entities); //debug //echo $this->entities[$sz-1]->depth.": tag - ".$tagcls->tagname."<br>"; // debug if($tagcls->type== END_TAG) { $level--; if($tagatlev[$level]!=$tagcls->tagname) { die("XML error - unmatching start and end tags (/".$tagatlev[$level]." expected, found /".$tagcls->tagname.")"); } } } else { $tblock = trim($block); if($tblock != "") // eliminate all whitespace blocks. { $contcls = new nb_xml_content($block); $contcls->depth = $level; $this->entities[] = $contcls; //echo $contcls->depth.": some content<br>"; // debug } } } } /*! Finds the root tag of the xml. \return the index of the root tag. */ function root() { $index=0; while(($index<=sizeof($this->entities))&&($this->entities[$index]->type!=START_TAG)) { $index++; } if($index<=sizeof($this->entities)){ return $index;} else{ return false;} } /*! Finds the index of the first occurence of a tag after an index point. \param $tagnm is the tag name to search for \param $startpt is the point to start the search from (defaults to 0) \return the index of the first occurence of the tag or false */ function findElement($tagnm, $startpt=0, $namespace = false) { $n = $startpt; $found = false; while(($n < sizeof($this->entities))&&($found===false)) { if(($this->entities[$n]->type==START_TAG)&&($this->entities[$n]->tagname==$tagnm)) { if(($namespace==false)||($namespace==$this->entities[$n]->nsURI)) $found=true; } if($found==false) $n++; } if($found===true){ return $n; } else{ return false;} } function getContent($idx) { if($this->entities[$idx]->type==CLOSED_TAG) return ""; elseif($this->entities[$idx]->type==START_TAG) { $n = $idx+1; $rstr = ""; while(($n < sizeof($this->entities))&&($this->entities[$n]->depth > $this->entities[$idx]->depth)) { if($this->entities[$n]->type==CONTENT) //# need to deal with CDATA too { $rstr .= $this->entities[$n]->dump(); } $n++; } return $rstr; } else return false; } /*! Finds either an immediate child or a peer (same depth) tag. If $tagnm is "" (default) the first child or peer tag is returned, otherwise only one with a matching name. This is the basic function for FindChildTag, FindNextPeerTag etc. This should be considered private, nicer functions exist that call this. \return index of the found tag or false. */ function findclosetag($parent, $tagnm="", $child=false, $namespace=false) { if($parent===false) die("false passed as parent in findclosetag"); $n = $parent+1; $seekdepth=$this->entities[$parent]->depth; //echo "$n parent [$parent], depth $seekdepth;<br>"; if($child) $seekdepth++; $found = false; while(($n < sizeof($this->entities))&&($found===false)&&($this->entities[$n]->depth>=$seekdepth)) { if((($this->entities[$n]->type==START_TAG)|| ($this->entities[$n]->type==CLOSED_TAG)) &&($this->entities[$n]->depth==$seekdepth) &&(($tagnm=="")||($this->entities[$n]->tagname==$tagnm)) &&(($namespace==false)||($namespace==$this->entities[$n]->nsURI)) ){ $found=true;} else{ $n++;} } if($found===true){ return $n; } else{ return false;} } /*! Finds imediate child tags of a particular tag (indicated by index). The tag index returned will either be the first child tag, or the first with a particular name. \param $parent is the index of the tag to find children of. \param $tagnm if provided is the tagname of child to search for. \return the index of the child tag or false */ function findChildElement($parent, $tagnm="", $namespace=false) { return $this->findclosetag($parent, $tagnm, true, $namespace); } /*! Finds the next peer tag of a particular tag (indicated by index) The tag index returned will either be the first peer tag, or the first with a particular name. \param $searchfrom is the index of the tag to search from. \param $tagnm is the tagname of peer to search for - if not given returns the first child index \return the index of the child tag or false */ function findNextPeer($searchfrom, $tagnm="", $namespace=false) { return $this->findclosetag($searchfrom, $tagnm, false, $namespace); } /*! Converts a block into a string of XML. The block goes from the start tag index provided to the matching end tag. \param $tagnum is the index of the start tag of the block to be dumped \return a string containing the block of XML */ function dumpblock($tagnum) { if($tagnum===false) return ""; $n = $tagnum+1; $rstr = $this->entities[$tagnum]->dump(); while(($n < sizeof($this->entities))&&($this->entities[$n]->depth > $this->entities[$tagnum]->depth)) { $rstr .= $this->entities[$n]->dump(); $n++; } return $rstr; } function getName($index) { return $this->entities[$index]->tagname; } function getNamespace($index) { return $this->entities[$index]->nsURI; } function getAttribute($index, $attribname) { if((is_array($this->entities[$index]->attribs))&&(array_key_exists($attribname,$this->entities[$index]->attribs))) return $this->entities[$index]->attribs[$attribname]; else return ""; } }; function nb_easyxmldoc($inp) { $dom = new nb_easyxml; $dom->parsein($inp); return $dom; } ?> |