📄 xml_domit_xpath.php
字号:
<?php/*** @package domit-xmlparser* @subpackage domit-xmlparser-main* @copyright (C) 2004 John Heinstein. All rights reserved* @license http://www.gnu.org/copyleft/lesser.html LGPL License* @author John Heinstein <johnkarl@nbnet.nb.ca>* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page* DOMIT! is Free Software**/if (!defined('DOMIT_INCLUDE_PATH')) { define('DOMIT_INCLUDE_PATH', (dirname(__FILE__) . "/"));}/** Separator for absolute path */define('DOMIT_XPATH_SEPARATOR_ABSOLUTE', '/');/** Separator for relative path */define('DOMIT_XPATH_SEPARATOR_RELATIVE', '//');/** OR separator for multiple patterns */define('DOMIT_XPATH_SEPARATOR_OR', '|');/** Constant for an absolute path search (starting at the document root) */define('DOMIT_XPATH_SEARCH_ABSOLUTE', 0);/** Constant for a relative path search (starting at the level of the calling node) */define('DOMIT_XPATH_SEARCH_RELATIVE', 1);/** Constant for a variable path search (finds all matches, regardless of place in the hierarchy) */define('DOMIT_XPATH_SEARCH_VARIABLE', 2);/*** DOMIT! XPath is an XPath parser.*/class DOMIT_XPath { /** @var Object The node from which the search is called */ var $callingNode; /** @var Object The node that is the current parent of the search */ var $searchType; /** @var array An array containing a series of path segments for which to search */ var $arPathSegments = array(); /** @var Object A DOMIT_NodeList of matching nodes */ var $nodeList; /** @var string A temporary string container */ var $charContainer; /** @var string The current character of the current pattern segment being parsed */ var $currChar; /** @var string The current pattern segment being parsed */ var $currentSegment; /** @var array A temporary node container for caching node references at the pattern level*/ var $globalNodeContainer; /** @var array A temporary node container for caching node references at the pattern segment level */ var $localNodeContainer; /** @var array Normalization table for XPath syntax */ var $normalizationTable = array('child::' => '', 'self::' => '.', 'attribute::' => '@', 'descendant::' => '*//', "\t" => ' ', "\x0B" => ' '); /** @var array A second-pass normalization table for XPath syntax */ var $normalizationTable2 = array(' =' => '=', '= ' => '=', ' <' => '<', ' >' => '>', '< ' => '<', '> ' => '>', ' !' => '!', '( ' => '(', ' )' => ')', ' ]' => ']', '] ' => ']', ' [' => '[', '[ ' => '[', ' /' => '/', '/ ' => '/', '"' => "'"); /** @var array A third-pass normalization table for XPath syntax */ var $normalizationTable3 = array('position()=' => '', '/descendant-or-self::node()/' => "//", 'self::node()' => '.', 'parent::node()' => '..'); /** * Constructor - creates an empty DOMIT_NodeList to store matching nodes */ function DOMIT_XPath() { require_once(DOMIT_INCLUDE_PATH . 'xml_domit_nodemaps.php'); $this->nodeList = new DOMIT_NodeList(); } //DOMIT_XPath /** * Parses the supplied "path"-based pattern * @param Object The node from which the search is called * @param string The pattern * @return Object The NodeList containing matching nodes */ function &parsePattern(&$node, $pattern, $nodeIndex = 0) { $this->callingNode =& $node; $pattern = $this->normalize(trim($pattern)); $this->splitPattern($pattern); $total = count($this->arPathSegments); //whole pattern level for ($i = 0; $i < $total; $i++) { $outerArray =& $this->arPathSegments[$i]; $this->initSearch($outerArray); $outerTotal = count($outerArray); $isInitialMatchAttempt = true; //variable path segment level for ($j = 0; $j < $outerTotal; $j++) { $innerArray =& $outerArray[$j]; $innerTotal = count($innerArray); if (!$isInitialMatchAttempt) { $this->searchType = DOMIT_XPATH_SEARCH_VARIABLE; } //pattern segment level for ($k = 0; $k < $innerTotal; $k++) { $currentPattern = $innerArray[$k]; if (($k == 0) && ($currentPattern == null)) { if ($innerTotal == 1) { $isInitialMatchAttempt = false; } //else just skip current step and don't alter searchType } else { if (!$isInitialMatchAttempt && ($k > 0)) { $this->searchType = DOMIT_XPATH_SEARCH_RELATIVE; } $this->currentSegment = $currentPattern; $this->processPatternSegment(); $isInitialMatchAttempt = false; } } } } if ($nodeIndex > 0) { if ($nodeIndex <= count($this->globalNodeContainer)) { return $this->globalNodeContainer[($nodeIndex - 1)]; } else { $null = null; return $null; } } if (count($this->globalNodeContainer) != 0) { foreach ($this->globalNodeContainer as $key =>$value) { $currNode =& $this->globalNodeContainer[$key]; $this->nodeList->appendNode($currNode); } } return $this->nodeList; } //parsePattern /** * Generates a new globalNodeContainer of matches */ function processPatternSegment() { $total = strlen($this->currentSegment); $this->charContainer = ''; $this->localNodeContainer = array(); for ($i = 0; $i < $total; $i++) {// $this->currChar = $this->currentSegment{$i}; $this->currChar = substr($this->currentSegment, $i, 1); switch ($this->currChar) { case '@': $this->selectAttribute(substr($this->currentSegment, ($this->currChar + 1))); $this->updateNodeContainers(); return; //break; case '*': if ($i == ($total - 1)) { $this->selectNamedChild('*'); } else { $this->charContainer .= $this->currChar; } break; case '.': $this->charContainer .= $this->currChar; if ($i == ($total - 1)) { if ($this->charContainer == '..') { $this->selectParent(); } else { return; } } break; case ')': $this->charContainer .= $this->currChar; $this->selectNodesByFunction(); break; case '[': $this->parsePredicate($this->charContainer, substr($this->currentSegment, ($i + 1))); return; //break; default: $this->charContainer .= $this->currChar; } } if ($this->charContainer != '') { $this->selectNamedChild($this->charContainer); } $this->updateNodeContainers(); } //processPatternSegment /** * Replaces the global node container with the local node container */ function updateNodeContainers() { $this->globalNodeContainer =& $this->localNodeContainer; unset($this->localNodeContainer); } //updateNodeContainers /** * Parses a predicate expression [...] * @param string The pattern segment containing the node expression * @param string The pattern segment containing the predicate expression */ function parsePredicate($nodeName, $patternSegment) { $arPredicates =& explode('][', $patternSegment); $total = count($arPredicates); $lastIndex = $total - 1; $arPredicates[$lastIndex] = substr($arPredicates[$lastIndex], 0, (strlen($arPredicates[$lastIndex]) - 1)); for ($i = 0; $i < $total; $i++) { $isRecursive = ($this->searchType == DOMIT_XPATH_SEARCH_VARIABLE) ? true : false; $currPredicate = $arPredicates[$i]; if (is_numeric($currPredicate)) { if ($i == 0) { $this->filterByIndex($nodeName, intval($currPredicate), $isRecursive); } else { $this->refilterByIndex(intval($currPredicate)); } } else { if ($i == 0) { $this->selectNamedChild($nodeName); $this->updateNodeContainers(); } $phpExpression = $this->predicateToPHP($currPredicate); $this->filterByPHPExpression($phpExpression); } $this->updateNodeContainers(); } $this->charContainer = ''; } //parsePredicate /** * Converts the predicate into PHP evaluable code * @param string The predicate * @return string The converted PHP expression */ function predicateToPHP($predicate) { $phpExpression = $predicate; $currChar = ''; $charContainer = ''; $totalChars = strlen($predicate); for ($i = 0; $i < $totalChars; $i++) { $currChar = substr($predicate, $i, 1); switch ($currChar) { case '(': case ')': case ' ': if ($charContainer != '') { $convertedPredicate = $this->expressionToPHP($charContainer); $phpExpression = str_replace($charContainer, $convertedPredicate, $phpExpression); $charContainer = ''; } break; default: $charContainer .= $currChar; } } if ($charContainer != '') { $convertedPredicate = $this->expressionToPHP($charContainer); $phpExpression = str_replace($charContainer, $convertedPredicate, $phpExpression); } return $phpExpression; } //predicateToPHP /** * Converts the predicate expression into a PHP expression * @param string The predicate expression * @return string The converted PHP expression */ function expressionToPHP($expression) { if ($expression == 'and') { $expression = '&&'; } else if ($expression == 'or') { $expression = '||'; } else if ($expression == 'not') { $expression = '!'; } else { $expression = trim($expression); if (strpos($expression, '@') !== false) { if (strpos($expression, '>=') !== false) { $expression = str_replace('@', ('floatval($' . "contextNode->getAttribute('"), $expression); $expression = str_replace('>=', "')) >= floatval(", $expression); if (!is_numeric($expression)) $expression = str_replace('floatval', '', $expression); $expression .= ')'; } else if (strpos($expression, '<=') !== false) { $expression = str_replace('@', ('floatval($' . "contextNode->getAttribute('"), $expression); $expression = str_replace('<=', "')) <= floatval(", $expression); if (!is_numeric($expression)) $expression = str_replace('floatval', '', $expression); $expression .= ')'; } else if (strpos($expression, '!=') !== false) { $expression = str_replace('@', ('$' . "contextNode->getAttribute('"), $expression); $expression = str_replace('!=', "') != ", $expression); } else if (strpos($expression, '=') !== false) { $expression = str_replace('@', ('$' . "contextNode->getAttribute('"), $expression); $expression = str_replace('=', "') == ", $expression); } else if (strpos($expression, '>') !== false) { $expression = str_replace('>', "')) > floatval(", $expression); //reverse so > doesn't get replaced $expression = str_replace('@', ('floatval($' . "contextNode->getAttribute('"), $expression); if (!is_numeric($expression)) $expression = str_replace('floatval', '', $expression); $expression .= ')';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -