⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xpath.class.inc

📁 groupoffice
💻 INC
📖 第 1 页 / 共 5 页
字号:
   * ### Terminate should not be allowed --fab.  Should it??  N.S.
   *
   * @param $message    (string)  Error message to be displayed.
   * @param $lineNumber (int)     line number given by __LINE__
   * @param $terminate  (bool)    (default TURE) End the execution of this script.
   */
  function _displayError($message, $lineNumber='-', $file='-', $terminate=TRUE) {
    // Display the error message.
    $err = '<b>XPath error in '.basename($file).':'.$lineNumber.'</b> '.$message."<br \>\n";
    $this->_setLastError($message, $lineNumber, $file);
    if (($this->properties['verboseLevel'] > 0) OR ($terminate)) echo $err;
    // End the execution of this script.
    if ($terminate) exit;
  }

  /**
   * Displays a diagnostic message
   *
   * This method displays an error messages
   *
   * @param $message    (string)  Error message to be displayed.
   * @param $lineNumber (int)     line number given by __LINE__
   */
  function _displayMessage($message, $lineNumber='-', $file='-') {
    // Display the error message.
    $err = '<b>XPath message from '.basename($file).':'.$lineNumber.'</b> '.$message."<br \>\n";
    if ($this->properties['verboseLevel'] > 0) echo $err;
  }
  
  /**
   * Called to begin the debug run of a function.
   *
   * This method starts a <DIV><PRE> tag so that the entry to this function
   * is clear to the debugging user.  Call _closeDebugFunction() at the
   * end of the function to create a clean box round the function call.
   *
   * @author    Nigel Swinson <nigelswinson@users.sourceforge.net>
   * @author    Sam   Blum    <bs_php@infeer.com>
   * @param     $functionName (string) the name of the function we are beginning to debug
   * @return                  (array)  the output from the microtime() function.
   * @see       _closeDebugFunction()
   */
  function _beginDebugFunction($functionName) {
    $fileName = basename(__FILE__);
    static $color = array('green','blue','red','lime','fuchsia', 'aqua');
    static $colIndex = -1;
    $colIndex++;
    echo '<div style="clear:both" align="left"> ';
    echo '<pre STYLE="border:solid thin '. $color[$colIndex % 6] . '; padding:5">';
    echo '<a style="float:right;margin:5px" name="'.$this->iDebugNextLinkNumber.'Open" href="#'.$this->iDebugNextLinkNumber.'Close">Function Close '.$this->iDebugNextLinkNumber.'</a>';
    echo "<STRONG>{$fileName} : {$functionName}</STRONG>";
    echo '<hr style="clear:both">';
    array_push($this->aDebugOpenLinks, $this->iDebugNextLinkNumber);
    $this->iDebugNextLinkNumber++;
    return microtime();
  }
  
  /**
   * Called to end the debug run of a function.
   *
   * This method ends a <DIV><PRE> block and reports the time since $aStartTime
   * is clear to the debugging user.
   *
   * @author    Nigel Swinson <nigelswinson@users.sourceforge.net>
   * @param     $aStartTime   (array) the time that the function call was started.
   * @param     $return_value (mixed) the return value from the function call that 
   *                                  we are debugging
   */
  function _closeDebugFunction($aStartTime, $returnValue = "") {
    echo "<hr>";
    $iOpenLinkNumber = array_pop($this->aDebugOpenLinks);
    echo '<a style="float:right" name="'.$iOpenLinkNumber.'Close" href="#'.$iOpenLinkNumber.'Open">Function Open '.$iOpenLinkNumber.'</a>';
    if (isSet($returnValue)) {
      if (is_array($returnValue))
        echo "Return Value: ".print_r($returnValue)."\n";
      else if (is_numeric($returnValue)) 
        echo "Return Value: ".(string)$returnValue."\n";
      else if (is_bool($returnValue)) 
        echo "Return Value: ".($returnValue ? "TRUE" : "FALSE")."\n";
      else 
        echo "Return Value: \"".htmlspecialchars($returnValue)."\"\n";
    }
    $this->_profileFunction($aStartTime, "Function took");
    echo '<br style="clear:both">';
    echo " \n</pre></div>";
  }
  
  /**
   * Call to return time since start of function for Profiling
   *
   * @param     $aStartTime  (array)  the time that the function call was started.
   * @param     $alertString (string) the string to describe what has just finished happening
   */
  function _profileFunction($aStartTime, $alertString) {
    // Print the time it took to call this function.
    $now   = explode(' ', microtime());
    $last  = explode(' ', $aStartTime);
    $delta = (round( (($now[1] - $last[1]) + ($now[0] - $last[0]))*1000 ));
    echo "\n{$alertString} <strong>{$delta} ms</strong>";
  }

  /**
   * Echo an XPath context for diagnostic purposes
   *
   * @param $context   (array)   An XPath context
   */
  function _printContext($context) {
    echo "{$context['nodePath']}({$context['pos']}/{$context['size']})";
  }
  
  /**
   * This is a debug helper function. It dumps the node-tree as HTML
   *
   * *QUICK AND DIRTY*. Needs some polishing.
   *
   * @param $node   (array)   A node 
   * @param $indent (string) (optional, default=''). For internal recursive calls.
   */
  function _treeDump($node, $indent = '') {
    $out = '';
    
    // Get rid of recursion
    $parentName = empty($node['parentNode']) ? "SUPER ROOT" :  $node['parentNode']['name'];
    unset($node['parentNode']);
    $node['parentNode'] = $parentName ;
    
    $out .= "NODE[{$node['name']}]\n";
    
    foreach($node as $key => $val) {
      if ($key === 'childNodes') continue;
      if (is_Array($val)) {
        $out .= $indent . "  [{$key}]\n" . arrayToStr($val, $indent . '    ');
      } else {
        $out .= $indent . "  [{$key}] => '{$val}' \n";
      }
    }
    
    if (!empty($node['childNodes'])) {
      $out .= $indent . "  ['childNodes'] (Size = ".sizeOf($node['childNodes']).")\n";
      foreach($node['childNodes'] as $key => $childNode) {
        $out .= $indent . "     [$key] => " . $this->_treeDump($childNode, $indent . '       ') . "\n";
      }
    }
    
    if (empty($indent)) {
      return "<pre>" . htmlspecialchars($out) . "</pre>";
    }
    return $out;
  }
} // END OF CLASS XPathBase


/************************************************************************************************
* ===============================================================================================
*                             X P a t h E n g i n e  -  Class                                    
* ===============================================================================================
************************************************************************************************/

class XPathEngine extends XPathBase {
  
  // List of supported XPath axes.
  // What a stupid idea from W3C to take axes name containing a '-' (dash)
  // NOTE: We replace the '-' with '_' to avoid the conflict with the minus operator.
  //       We will then do the same on the users Xpath querys
  //   -sibling => _sibling
  //   -or-     =>     _or_
  //  
  // This array contains a list of all valid axes that can be evaluated in an
  // XPath query.
  var $axes = array ( 'ancestor', 'ancestor_or_self', 'attribute', 'child', 'descendant', 
                        'descendant_or_self', 'following', 'following_sibling',  
                        'namespace', 'parent', 'preceding', 'preceding_sibling', 'self' 
     );
  
  // List of supported XPath functions.
  // What a stupid idea from W3C to take function name containing a '-' (dash)
  // NOTE: We replace the '-' with '_' to avoid the conflict with the minus operator.
  //       We will then do the same on the users Xpath querys 
  //   starts-with      => starts_with
  //   substring-before => substring_before
  //   substring-after  => substring_after
  //   string-length    => string_length
  //
  // This array contains a list of all valid functions that can be evaluated
  // in an XPath query.
  var $functions = array ( 'last', 'position', 'count', 'id', 'name',
    'string', 'concat', 'starts_with', 'contains', 'substring_before',
    'substring_after', 'substring', 'string_length', 'normalize_space', 'translate',
    'boolean', 'not', 'true', 'false', 'lang', 'number', 'sum', 'floor',
    'ceiling', 'round', 'x_lower', 'x_upper', 'generate_id' );
    
  // List of supported XPath operators.
  //
  // This array contains a list of all valid operators that can be evaluated
  // in a predicate of an XPath query. The list is ordered by the
  // precedence of the operators (lowest precedence first).
  var $operators = array( ' or ', ' and ', '=', '!=', '<=', '<', '>=', '>',
    '+', '-', '*', ' div ', ' mod ', ' | ');

  // List of literals from the xPath string.
  var $axPathLiterals = array();
  
  // The index and tree that is created during the analysis of an XML source.
  var $nodeIndex = array();
  var $nodeRoot  = array();
  var $emptyNode = array(
                     'name'        => '',       // The tag name. E.g. In <FOO bar="aaa"/> it would be 'FOO'
                     'attributes'  => array(),  // The attributes of the tag E.g. In <FOO bar="aaa"/> it would be array('bar'=>'aaa')
                     'childNodes'  => array(),  // Array of pointers to child nodes.
                     'textParts'   => array(),  // Array of text parts between the cilderen E.g. <FOO>aa<A>bb<B/>cc</A>dd</FOO> -> array('aa','bb','cc','dd')
                     'parentNode'   => NULL,     // Pointer to parent node or NULL if this node is the 'super root'
                     //-- *!* Following vars are set by the indexer and is for optimisation only *!*
                     'depth'       => 0,  // The tag depth (or tree level) starting with the root tag at 0.
                     'pos'         => 0,  // Is the zero-based position this node has in the parents 'childNodes'-list.
                     'contextPos'  => 1,  // Is the one-based position this node has by counting the siblings tags (tags with same name)
                     'xpath'       => ''  // Is the abs. XPath to this node.
                   );
  var $_indexIsDirty = FALSE;

  
  // 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.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -