📄 xpath.class.php
字号:
// These variable used during the parse XML source var $nodeStack = array(); // The elements that we have still to close. var $parseStackIndex = 0; // The current element of the nodeStack[] that we are adding to while // parsing an XML source. Corresponds to the depth of the xml node. // in our input data. var $parseOptions = array(); // Used to set the PHP's XML parser options (see xml_parser_set_option) var $parsedTextLocation = ''; // A reference to where we have to put char data collected during XML parsing var $parsInCData = 0 ; // Is >0 when we are inside a CDATA section. var $parseSkipWhiteCache = 0; // A cache of the skip whitespace parse option to speed up the parse. // This is the array of error strings, to keep consistency. var $errorStrings = array( 'AbsoluteXPathRequired' => "The supplied xPath '%s' does not *uniquely* describe a node in the xml document.", 'NoNodeMatch' => "The supplied xPath-query '%s' does not match *any* node in the xml document.", 'RootNodeAlreadyExists' => "An xml document may have only one root node." ); /** * Constructor * * Optionally you may call this constructor with the XML-filename to parse and the * XML option vector. Each of the entries in the option vector will be passed to * xml_parser_set_option(). * * A option vector sample: * $xmlOpt = array(XML_OPTION_CASE_FOLDING => FALSE, * XML_OPTION_SKIP_WHITE => TRUE); * * @param $userXmlOptions (array) (optional) Vector of (<optionID>=><value>, * <optionID>=><value>, ...). See PHP's * xml_parser_set_option() docu for a list of possible * options. * @see importFromFile(), importFromString(), setXmlOptions() */ function XPathEngine($userXmlOptions=array()) { parent::XPathBase(); // Default to not folding case $this->parseOptions[XML_OPTION_CASE_FOLDING] = FALSE; // And not skipping whitespace $this->parseOptions[XML_OPTION_SKIP_WHITE] = FALSE; // Now merge in the overrides. // Don't use PHP's array_merge! if (is_array($userXmlOptions)) { foreach($userXmlOptions as $key => $val) $this->parseOptions[$key] = $val; } } /** * Resets the object so it's able to take a new xml sting/file * * Constructing objects is slow. If you can, reuse ones that you have used already * by using this reset() function. */ function reset() { parent::reset(); $this->properties['xmlFile'] = ''; $this->parseStackIndex = 0; $this->parsedTextLocation = ''; $this->parsInCData = 0; $this->nodeIndex = array(); $this->nodeRoot = array(); $this->nodeStack = array(); $this->aLiterals = array(); $this->_indexIsDirty = FALSE; } //----------------------------------------------------------------------------------------- // XPathEngine ------ Get / Set Stuff ------ //----------------------------------------------------------------------------------------- /** * Returns the property/ies you want. * * if $param is not given, all properties will be returned in a hash. * * @param $param (string) the property you want the value of, or NULL for all the properties * @return (mixed) string OR hash of all params, or NULL on an unknown parameter. */ function getProperties($param=NULL) { $this->properties['hasContent'] = !empty($this->nodeRoot); $this->properties['caseFolding'] = $this->parseOptions[XML_OPTION_CASE_FOLDING]; $this->properties['skipWhiteSpaces'] = $this->parseOptions[XML_OPTION_SKIP_WHITE]; if (empty($param)) return $this->properties; if (isSet($this->properties[$param])) { return $this->properties[$param]; } else { return NULL; } } /** * Set an xml_parser_set_option() * * @param $optionID (int) The option ID (e.g. XML_OPTION_SKIP_WHITE) * @param $value (int) The option value. * @see XML parser functions in PHP doc */ function setXmlOption($optionID, $value) { if (!is_numeric($optionID)) return; $this->parseOptions[$optionID] = $value; } /** * Sets a number of xml_parser_set_option()s * * @param $userXmlOptions (array) An array of parser options. * @see setXmlOption */ function setXmlOptions($userXmlOptions=array()) { if (!is_array($userXmlOptions)) return; foreach($userXmlOptions as $key => $val) { $this->setXmlOption($key, $val); } } /** * Alternative way to control whether case-folding is enabled for this XML parser. * * Short cut to setXmlOptions(XML_OPTION_CASE_FOLDING, TRUE/FALSE) * * When it comes to XML, case-folding simply means uppercasing all tag- * and attribute-names (NOT the content) if set to TRUE. Note if you * have this option set, then your XPath queries will also be case folded * for you. * * @param $onOff (bool) (default TRUE) * @see XML parser functions in PHP doc */ function setCaseFolding($onOff=TRUE) { $this->parseOptions[XML_OPTION_CASE_FOLDING] = $onOff; } /** * Alternative way to control whether skip-white-spaces is enabled for this XML parser. * * Short cut to setXmlOptions(XML_OPTION_SKIP_WHITE, TRUE/FALSE) * * When it comes to XML, skip-white-spaces will trim the tag content. * An XML file with no whitespace will be faster to process, but will make * your data less human readable when you come to write it out. * * Running with this option on will slow the class down, so if you want to * speed up your XML, then run it through once skipping white-spaces, then * write out the new version of your XML without whitespace, then use the * new XML file with skip whitespaces turned off. * * @param $onOff (bool) (default TRUE) * @see XML parser functions in PHP doc */ function setSkipWhiteSpaces($onOff=TRUE) { $this->parseOptions[XML_OPTION_SKIP_WHITE] = $onOff; } /** * Get the node defined by the $absoluteXPath. * * @param $absoluteXPath (string) (optional, default is 'super-root') xpath to the node. * @return (array) The node, or FALSE if the node wasn't found. */ function &getNode($absoluteXPath='') { if ($absoluteXPath==='/') $absoluteXPath = ''; if (!isSet($this->nodeIndex[$absoluteXPath])) return FALSE; if ($this->_indexIsDirty) $this->reindexNodeTree(); return $this->nodeIndex[$absoluteXPath]; } /** * Get a the content of a node text part or node attribute. * * If the absolute Xpath references an attribute (Xpath ends with @ or attribute::), * then the text value of that node-attribute is returned. * Otherwise the Xpath is referencing a text part of the node. This can be either a * direct reference to a text part (Xpath ends with text()[<nr>]) or indirect reference * (a simple abs. Xpath to a node). * 1) Direct Reference (xpath ends with text()[<part-number>]): * If the 'part-number' is omitted, the first text-part is assumed; starting by 1. * Negative numbers are allowed, where -1 is the last text-part a.s.o. * 2) Indirect Reference (a simple abs. Xpath to a node): * Default is to return the *whole text*; that is the concated text-parts of the matching * node. (NOTE that only in this case you'll only get a copy and changes to the returned * value wounld have no effect). Optionally you may pass a parameter * $textPartNr to define the text-part you want; starting by 1. * Negative numbers are allowed, where -1 is the last text-part a.s.o. * * NOTE I : The returned value can be fetched by reference * E.g. $text =& wholeText(). If you wish to modify the text. * NOTE II: text-part numbers out of range will return FALSE * SIDENOTE:The function name is a suggestion from W3C in the XPath specification level 3. * * @param $absoluteXPath (string) xpath to the node (See above). * @param $textPartNr (int) If referring to a node, specifies which text part * to query. * @return (&string) A *reference* to the text if the node that the other * parameters describe or FALSE if the node is not found. */ function &wholeText($absoluteXPath, $textPartNr=NULL) { $status = FALSE; $text = NULL; if ($this->_indexIsDirty) $this->reindexNodeTree(); do { // try-block if (preg_match(";(.*)/(attribute::|@)([^/]*)$;U", $absoluteXPath, $matches)) { $absoluteXPath = $matches[1]; $attribute = $matches[3]; if (!isSet($this->nodeIndex[$absoluteXPath]['attributes'][$attribute])) { $this->_displayError("The $absoluteXPath/attribute::$attribute value isn't a node in this document.", __LINE__, __FILE__, FALSE); break; // try-block } $text =& $this->nodeIndex[$absoluteXPath]['attributes'][$attribute]; $status = TRUE; break; // try-block } // Xpath contains a 'text()'-function, thus goes right to a text node. If so interpret the Xpath. if (preg_match(":(.*)/text\(\)(\[(.*)\])?$:U", $absoluteXPath, $matches)) { $absoluteXPath = $matches[1]; if (!isSet($this->nodeIndex[$absoluteXPath])) { $this->_displayError("The $absoluteXPath value isn't a node in this document.", __LINE__, __FILE__, FALSE); break; // try-block } // Get the amount of the text parts in the node. $textPartSize = sizeOf($this->nodeIndex[$absoluteXPath]['textParts']); // default to the first text node if a text node was not specified $textPartNr = isSet($matches[2]) ? substr($matches[2],1,-1) : 1; // Support negative indexes like -1 === last a.s.o. if ($textPartNr < 0) $textPartNr = $textPartSize + $textPartNr +1; if (($textPartNr <= 0) OR ($textPartNr > $textPartSize)) { $this->_displayError("The $absoluteXPath/text()[$textPartNr] value isn't a NODE in this document.", __LINE__, __FILE__, FALSE); break; // try-block } $text =& $this->nodeIndex[$absoluteXPath]['textParts'][$textPartNr - 1]; $status = TRUE; break; // try-block } // At this point we have been given an xpath with neither a 'text()' nor 'attribute::' axis at the end // So we assume a get to text is wanted and use the optioanl fallback parameters $textPartNr if (!isSet($this->nodeIndex[$absoluteXPath])) { $this->_displayError("The $absoluteXPath value isn't a node in this document.", __LINE__, __FILE__, FALSE); break; // try-block } // Get the amount of the text parts in the node. $textPartSize = sizeOf($this->nodeIndex[$absoluteXPath]['textParts']); // If $textPartNr == NULL we return a *copy* of the whole concated text-parts if (is_null($textPartNr)) { unset($text); $text = implode('', $this->nodeIndex[$absoluteXPath]['textParts']); $status = TRUE; break; // try-block } // Support negative indexes like -1 === last a.s.o. if ($textPartNr < 0) $textPartNr = $textPartSize + $textPartNr +1; if (($textPartNr <= 0) OR ($textPartNr > $textPartSize)) { $this->_displayError("The $absoluteXPath has no text part at pos [$textPartNr] (Note: text parts start with 1).", __LINE__, __FILE__, FALSE); break; // try-block } $text =& $this->nodeIndex[$absoluteXPath]['textParts'][$textPartNr -1]; $status = TRUE; } while (FALSE); // END try-block if (!$status) return FALSE; return $text; } /** * Obtain the string value of an object * * http://www.w3.org/TR/xpath#dt-string-value * * "For every type of node, there is a way of determining a string-value for a node of that type. * For some types of node, the string-value is part of the node; for other types of node, the * string-value is computed from the string-value of descendant nodes." * * @param $node (node) The node we have to convert * @return (string) The string value of the node. "" if the object has no evaluatable * string value */ function _stringValue($node) { // Decode the entitites and then add the resulting literal string into our array. return $this->_addLiteral($this->decodeEntities($this->wholeText($node))); } //----------------------------------------------------------------------------------------- // XPathEngine ------ Export the XML Document ------ //----------------------------------------------------------------------------------------- /** * Returns the containing XML as marked up HTML with specified nodes hi-lighted * * @param $absoluteXPath (string) The address of the node you would like to export. * If empty the whole document will be exported. * @param $hilighXpathList (array) A list of nodes that you would like to highlight * @return (mixed) The Xml document marked up as HTML so that it can
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -