PageRenderTime 29ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/includes/domit/xml_domit_xpath.php

https://bitbucket.org/dgough/annamaria-daneswood-25102012
PHP | 1047 lines | 931 code | 51 blank | 65 comment | 70 complexity | 17226e93e2b740844ce1ccb71da5f728 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * @package domit-xmlparser
  4. * @subpackage domit-xmlparser-main
  5. * @copyright (C) 2004 John Heinstein. All rights reserved
  6. * @license http://www.gnu.org/copyleft/lesser.html LGPL License
  7. * @author John Heinstein <johnkarl@nbnet.nb.ca>
  8. * @link http://www.engageinteractive.com/domit/ DOMIT! Home Page
  9. * DOMIT! is Free Software
  10. **/
  11. if (!defined('DOMIT_INCLUDE_PATH')) {
  12. define('DOMIT_INCLUDE_PATH', (dirname(__FILE__) . "/"));
  13. }
  14. /** Separator for absolute path */
  15. define('DOMIT_XPATH_SEPARATOR_ABSOLUTE', '/');
  16. /** Separator for relative path */
  17. define('DOMIT_XPATH_SEPARATOR_RELATIVE', '//');
  18. /** OR separator for multiple patterns */
  19. define('DOMIT_XPATH_SEPARATOR_OR', '|');
  20. /** Constant for an absolute path search (starting at the document root) */
  21. define('DOMIT_XPATH_SEARCH_ABSOLUTE', 0);
  22. /** Constant for a relative path search (starting at the level of the calling node) */
  23. define('DOMIT_XPATH_SEARCH_RELATIVE', 1);
  24. /** Constant for a variable path search (finds all matches, regardless of place in the hierarchy) */
  25. define('DOMIT_XPATH_SEARCH_VARIABLE', 2);
  26. /**
  27. * DOMIT! XPath is an XPath parser.
  28. */
  29. class DOMIT_XPath {
  30. /** @var Object The node from which the search is called */
  31. var $callingNode;
  32. /** @var Object The node that is the current parent of the search */
  33. var $searchType;
  34. /** @var array An array containing a series of path segments for which to search */
  35. var $arPathSegments = array();
  36. /** @var Object A DOMIT_NodeList of matching nodes */
  37. var $nodeList;
  38. /** @var string A temporary string container */
  39. var $charContainer;
  40. /** @var string The current character of the current pattern segment being parsed */
  41. var $currChar;
  42. /** @var string The current pattern segment being parsed */
  43. var $currentSegment;
  44. /** @var array A temporary node container for caching node references at the pattern level*/
  45. var $globalNodeContainer;
  46. /** @var array A temporary node container for caching node references at the pattern segment level */
  47. var $localNodeContainer;
  48. /** @var array Normalization table for XPath syntax */
  49. var $normalizationTable = array('child::' => '', 'self::' => '.',
  50. 'attribute::' => '@', 'descendant::' => '*//',
  51. "\t" => ' ', "\x0B" => ' ');
  52. /** @var array A second-pass normalization table for XPath syntax */
  53. var $normalizationTable2 = array(' =' => '=', '= ' => '=', ' <' => '<',
  54. ' >' => '>', '< ' => '<', '> ' => '>',
  55. ' !' => '!', '( ' => '(',
  56. ' )' => ')', ' ]' => ']', '] ' => ']',
  57. ' [' => '[', '[ ' => '[', ' /' => '/',
  58. '/ ' => '/', '"' => "'");
  59. /** @var array A third-pass normalization table for XPath syntax */
  60. var $normalizationTable3 = array('position()=' => '',
  61. '/descendant-or-self::node()/' => "//", 'self::node()' => '.',
  62. 'parent::node()' => '..');
  63. /**
  64. * Constructor - creates an empty DOMIT_NodeList to store matching nodes
  65. */
  66. function DOMIT_XPath() {
  67. require_once(DOMIT_INCLUDE_PATH . 'xml_domit_nodemaps.php');
  68. $this->nodeList = new DOMIT_NodeList();
  69. } //DOMIT_XPath
  70. /**
  71. * Parses the supplied "path"-based pattern
  72. * @param Object The node from which the search is called
  73. * @param string The pattern
  74. * @return Object The NodeList containing matching nodes
  75. */
  76. function &parsePattern(&$node, $pattern, $nodeIndex = 0) {
  77. $this->callingNode =& $node;
  78. $pattern = $this->normalize(trim($pattern));
  79. $this->splitPattern($pattern);
  80. $total = count($this->arPathSegments);
  81. //whole pattern level
  82. for ($i = 0; $i < $total; $i++) {
  83. $outerArray =& $this->arPathSegments[$i];
  84. $this->initSearch($outerArray);
  85. $outerTotal = count($outerArray);
  86. $isInitialMatchAttempt = true;
  87. //variable path segment level
  88. for ($j = 0; $j < $outerTotal; $j++) {
  89. $innerArray =& $outerArray[$j];
  90. $innerTotal = count($innerArray);
  91. if (!$isInitialMatchAttempt) {
  92. $this->searchType = DOMIT_XPATH_SEARCH_VARIABLE;
  93. }
  94. //pattern segment level
  95. for ($k = 0; $k < $innerTotal; $k++) {
  96. $currentPattern = $innerArray[$k];
  97. if (($k == 0) && ($currentPattern == null)) {
  98. if ($innerTotal == 1) {
  99. $isInitialMatchAttempt = false;
  100. }
  101. //else just skip current step and don't alter searchType
  102. }
  103. else {
  104. if (!$isInitialMatchAttempt && ($k > 0)) {
  105. $this->searchType = DOMIT_XPATH_SEARCH_RELATIVE;
  106. }
  107. $this->currentSegment = $currentPattern;
  108. $this->processPatternSegment();
  109. $isInitialMatchAttempt = false;
  110. }
  111. }
  112. }
  113. }
  114. if ($nodeIndex > 0) {
  115. if ($nodeIndex <= count($this->globalNodeContainer)) {
  116. return $this->globalNodeContainer[($nodeIndex - 1)];
  117. }
  118. else {
  119. $null = null;
  120. return $null;
  121. }
  122. }
  123. if (count($this->globalNodeContainer) != 0) {
  124. foreach ($this->globalNodeContainer as $key =>$value) {
  125. $currNode =& $this->globalNodeContainer[$key];
  126. $this->nodeList->appendNode($currNode);
  127. }
  128. }
  129. return $this->nodeList;
  130. } //parsePattern
  131. /**
  132. * Generates a new globalNodeContainer of matches
  133. */
  134. function processPatternSegment() {
  135. $total = strlen($this->currentSegment);
  136. $this->charContainer = '';
  137. $this->localNodeContainer = array();
  138. for ($i = 0; $i < $total; $i++) {
  139. $this->currChar = $this->currentSegment{$i};
  140. switch ($this->currChar) {
  141. case '@':
  142. $this->selectAttribute(substr($this->currentSegment, ($this->currChar + 1)));
  143. $this->updateNodeContainers();
  144. return;
  145. //break;
  146. case '*':
  147. if ($i == ($total - 1)) {
  148. $this->selectNamedChild('*');
  149. }
  150. else {
  151. $this->charContainer .= $this->currChar;
  152. }
  153. break;
  154. case '.':
  155. $this->charContainer .= $this->currChar;
  156. if ($i == ($total - 1)) {
  157. if ($this->charContainer == '..') {
  158. $this->selectParent();
  159. }
  160. else {
  161. return;
  162. }
  163. }
  164. break;
  165. case ')':
  166. $this->charContainer .= $this->currChar;
  167. $this->selectNodesByFunction();
  168. break;
  169. case '[':
  170. $this->parsePredicate($this->charContainer,
  171. substr($this->currentSegment, ($i + 1)));
  172. return;
  173. //break;
  174. default:
  175. $this->charContainer .= $this->currChar;
  176. }
  177. }
  178. if ($this->charContainer != '') {
  179. $this->selectNamedChild($this->charContainer);
  180. }
  181. $this->updateNodeContainers();
  182. } //processPatternSegment
  183. /**
  184. * Replaces the global node container with the local node container
  185. */
  186. function updateNodeContainers() {
  187. $this->globalNodeContainer =& $this->localNodeContainer;
  188. unset($this->localNodeContainer);
  189. } //updateNodeContainers
  190. /**
  191. * Parses a predicate expression [...]
  192. * @param string The pattern segment containing the node expression
  193. * @param string The pattern segment containing the predicate expression
  194. */
  195. function parsePredicate($nodeName, $patternSegment) {
  196. $arPredicates =& explode('][', $patternSegment);
  197. $total = count($arPredicates);
  198. $lastIndex = $total - 1;
  199. $arPredicates[$lastIndex] = substr($arPredicates[$lastIndex],
  200. 0, (strlen($arPredicates[$lastIndex]) - 1));
  201. for ($i = 0; $i < $total; $i++) {
  202. $isRecursive = ($this->searchType == DOMIT_XPATH_SEARCH_VARIABLE) ? true : false;
  203. $currPredicate = $arPredicates[$i];
  204. if (is_numeric($currPredicate)) {
  205. if ($i == 0) {
  206. $this->filterByIndex($nodeName, intval($currPredicate), $isRecursive);
  207. }
  208. else {
  209. $this->refilterByIndex(intval($currPredicate));
  210. }
  211. }
  212. else {
  213. if ($i == 0) {
  214. $this->selectNamedChild($nodeName);
  215. $this->updateNodeContainers();
  216. }
  217. $phpExpression = $this->predicateToPHP($currPredicate);
  218. $this->filterByPHPExpression($phpExpression);
  219. }
  220. $this->updateNodeContainers();
  221. }
  222. $this->charContainer = '';
  223. } //parsePredicate
  224. /**
  225. * Converts the predicate into PHP evaluable code
  226. * @param string The predicate
  227. * @return string The converted PHP expression
  228. */
  229. function predicateToPHP($predicate) {
  230. $phpExpression = $predicate;
  231. $currChar = '';
  232. $charContainer = '';
  233. $totalChars = strlen($predicate);
  234. for ($i = 0; $i < $totalChars; $i++) {
  235. $currChar = substr($predicate, $i, 1);
  236. switch ($currChar) {
  237. case '(':
  238. case ')':
  239. case ' ':
  240. if ($charContainer != '') {
  241. $convertedPredicate = $this->expressionToPHP($charContainer);
  242. $phpExpression = str_replace($charContainer, $convertedPredicate, $phpExpression);
  243. $charContainer = '';
  244. }
  245. break;
  246. default:
  247. $charContainer .= $currChar;
  248. }
  249. }
  250. if ($charContainer != '') {
  251. $convertedPredicate = $this->expressionToPHP($charContainer);
  252. $phpExpression = str_replace($charContainer, $convertedPredicate, $phpExpression);
  253. }
  254. return $phpExpression;
  255. } //predicateToPHP
  256. /**
  257. * Converts the predicate expression into a PHP expression
  258. * @param string The predicate expression
  259. * @return string The converted PHP expression
  260. */
  261. function expressionToPHP($expression) {
  262. if ($expression == 'and') {
  263. $expression = '&&';
  264. }
  265. else if ($expression == 'or') {
  266. $expression = '||';
  267. }
  268. else if ($expression == 'not') {
  269. $expression = '!';
  270. }
  271. else {
  272. $expression = trim($expression);
  273. if (strpos($expression, '@') !== false) {
  274. if (strpos($expression, '>=') !== false) {
  275. $expression = str_replace('@', ('floatval($' . "contextNode->getAttribute('"), $expression);
  276. $expression = str_replace('>=', "')) >= floatval(", $expression);
  277. if (!is_numeric($expression)) $expression = str_replace('floatval', '', $expression);
  278. $expression .= ')';
  279. }
  280. else if (strpos($expression, '<=') !== false) {
  281. $expression = str_replace('@', ('floatval($' . "contextNode->getAttribute('"), $expression);
  282. $expression = str_replace('<=', "')) <= floatval(", $expression);
  283. if (!is_numeric($expression)) $expression = str_replace('floatval', '', $expression);
  284. $expression .= ')';
  285. }
  286. else if (strpos($expression, '!=') !== false) {
  287. $expression = str_replace('@', ('$' . "contextNode->getAttribute('"), $expression);
  288. $expression = str_replace('!=', "') != ", $expression);
  289. }
  290. else if (strpos($expression, '=') !== false) {
  291. $expression = str_replace('@', ('$' . "contextNode->getAttribute('"), $expression);
  292. $expression = str_replace('=', "') == ", $expression);
  293. }
  294. else if (strpos($expression, '>') !== false) {
  295. $expression = str_replace('>', "')) > floatval(", $expression); //reverse so > doesn't get replaced
  296. $expression = str_replace('@', ('floatval($' . "contextNode->getAttribute('"), $expression);
  297. if (!is_numeric($expression)) $expression = str_replace('floatval', '', $expression);
  298. $expression .= ')';
  299. }
  300. else if (strpos($expression, '<') !== false) {
  301. $expression = str_replace('@', ('floatval($' . "contextNode->getAttribute('"), $expression);
  302. $expression = str_replace('<', "')) < floatval(", $expression);
  303. if (!is_numeric($expression)) $expression = str_replace('floatval', '', $expression);
  304. $expression .= ')';
  305. }
  306. else {
  307. $expression = str_replace('@', ('$' . "contextNode->hasAttribute('"), $expression);
  308. $expression.= "')";
  309. }
  310. }
  311. else {
  312. if (strpos($expression, '>=') !== false) {
  313. $signPos = strpos($expression, '>=');
  314. $elementName = trim(substr($expression, 0, $signPos));
  315. $elementValue = trim(substr($expression, ($signPos + 2)));
  316. $expression = '$' . "this->hasNamedChildElementGreaterThanOrEqualToValue(" .
  317. '$' . "contextNode, '" . $elementName . "', " .
  318. $elementValue . ')';
  319. }
  320. else if (strpos($expression, '<=') !== false) {
  321. $signPos = strpos($expression, '>=');
  322. $elementName = trim(substr($expression, 0, $signPos));
  323. $elementValue = trim(substr($expression, ($signPos + 2)));
  324. $expression = '$' . "this->hasNamedChildElementLessThanOrEqualToValue(" .
  325. '$' . "contextNode, '" . $elementName . "', " .
  326. $elementValue . ')';
  327. }
  328. else if (strpos($expression, '!=') !== false) {
  329. $signPos = strpos($expression, '>=');
  330. $elementName = trim(substr($expression, 0, $signPos));
  331. $elementValue = trim(substr($expression, ($signPos + 2)));
  332. $expression = '$' . "this->hasNamedChildElementNotEqualToValue(" .
  333. '$' . "contextNode, '" . $elementName . "', " .
  334. $elementValue . ')';
  335. }
  336. else if (strpos($expression, '=') !== false) {
  337. $signPos = strpos($expression, '=');
  338. $elementName = trim(substr($expression, 0, $signPos));
  339. $elementValue = trim(substr($expression, ($signPos + 1)));
  340. $expression = '$' . "this->hasNamedChildElementEqualToValue(" .
  341. '$' . "contextNode, '" . $elementName . "', " .
  342. $elementValue . ')';
  343. }
  344. else if (strpos($expression, '>') !== false) {
  345. $signPos = strpos($expression, '=');
  346. $elementName = trim(substr($expression, 0, $signPos));
  347. $elementValue = trim(substr($expression, ($signPos + 1)));
  348. $expression = '$' . "this->hasNamedChildElementGreaterThanValue(" .
  349. '$' . "contextNode, '" . $elementName . "', " .
  350. $elementValue . ')';
  351. }
  352. else if (strpos($expression, '<') !== false) {
  353. $signPos = strpos($expression, '=');
  354. $elementName = trim(substr($expression, 0, $signPos));
  355. $elementValue = trim(substr($expression, ($signPos + 1)));
  356. $expression = '$' . "this->hasNamedChildElementLessThanValue(" .
  357. '$' . "contextNode, '" . $elementName . "', " .
  358. $elementValue . ')';
  359. }
  360. else {
  361. $expression = '$' . "this->hasNamedChildElement(" .
  362. '$' . "contextNode, '" . $expression . "')";
  363. }
  364. }
  365. }
  366. return $expression;
  367. } //expressionToPHP
  368. /**
  369. * Selects nodes that match the predicate expression
  370. * @param string The predicate expression, formatted as a PHP expression
  371. */
  372. function filterByPHPExpression($expression) {
  373. if (count($this->globalNodeContainer) != 0) {
  374. foreach ($this->globalNodeContainer as $key =>$value) {
  375. $contextNode =& $this->globalNodeContainer[$key];
  376. if ($contextNode->nodeType == DOMIT_ELEMENT_NODE) {
  377. $evaluatedExpression = 'if (' . $expression . ") $" .
  378. 'this->localNodeContainer[] =& $' . 'contextNode;';
  379. eval($evaluatedExpression);
  380. }
  381. }
  382. }
  383. } //filterByPHPExpression
  384. /**
  385. * Selects nodes with child elements that match the specified name
  386. * @param object The parent node of the child elements to match
  387. * @param string The tag name to match on
  388. * @return boolean True if a matching child element exists
  389. */
  390. function hasNamedChildElement(&$parentNode, $nodeName) {
  391. $total = $parentNode->childCount;
  392. for ($i = 0; $i < $total; $i++) {
  393. $currNode =& $parentNode->childNodes[$i];
  394. if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) {
  395. return true;
  396. }
  397. }
  398. return false;
  399. } //hasNamedChildElement
  400. /**
  401. * Selects nodes with child elements that match the specified name and text value
  402. * @param object The parent node of the child elements to match
  403. * @param string The tag name to match on
  404. * @param string The text string to match on
  405. * @return boolean True if a matching child element exists
  406. */
  407. function hasNamedChildElementEqualToValue(&$parentNode, $nodeName, $nodeValue) {
  408. $total = $parentNode->childCount;
  409. for ($i = 0; $i < $total; $i++) {
  410. $currNode =& $parentNode->childNodes[$i];
  411. if (($currNode->nodeType == DOMIT_ELEMENT_NODE) &&
  412. ($currNode->nodeName == $nodeName) && ($currNode->getText() == $nodeValue)) {
  413. return true;
  414. }
  415. }
  416. return false;
  417. } //hasNamedChildElementEqualToValue
  418. /**
  419. * Selects nodes with child elements that are greater than or equal to the specified name and value
  420. * @param object The parent node of the child elements to match
  421. * @param string The tag name to match on
  422. * @param string The text string to match on
  423. * @return boolean True if a matching child element exists
  424. */
  425. function hasNamedChildElementGreaterThanOrEqualToValue(&$parentNode, $nodeName, $nodeValue) {
  426. $isNumeric = false;
  427. if (is_numeric($nodeValue)) {
  428. $isNumeric = true;
  429. $nodeValue = floatval($nodeValue);
  430. }
  431. $total = $parentNode->childCount;
  432. for ($i = 0; $i < $total; $i++) {
  433. $currNode =& $parentNode->childNodes[$i];
  434. if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) {
  435. if ($isNumeric) {$compareVal = floatval($currNode->getText());}
  436. else {$compareVal = $currNode->getText();}
  437. if ($compareVal >= $nodeValue) return true;
  438. }
  439. }
  440. return false;
  441. } //hasNamedChildElementGreaterThanOrEqualToValue
  442. /**
  443. * Selects nodes with child elements that are less than or equal to the specified name and value
  444. * @param object The parent node of the child elements to match
  445. * @param string The tag name to match on
  446. * @param string The text string to match on
  447. * @return boolean True if a matching child element exists
  448. */
  449. function hasNamedChildElementLessThanOrEqualToValue(&$parentNode, $nodeName, $nodeValue) {
  450. $isNumeric = false;
  451. if (is_numeric($nodeValue)) {
  452. $isNumeric = true;
  453. $nodeValue = floatval($nodeValue);
  454. }
  455. $total = $parentNode->childCount;
  456. for ($i = 0; $i < $total; $i++) {
  457. $currNode =& $parentNode->childNodes[$i];
  458. if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) {
  459. if ($isNumeric) {$compareVal = floatval($currNode->getText());}
  460. else {$compareVal = $currNode->getText();}
  461. if ($compareVal <= $nodeValue) return true;
  462. }
  463. }
  464. return false;
  465. } //hasNamedChildElementLessThanOrEqualToValue
  466. /**
  467. * Selects nodes with child elements that are not equal to the specified name and value
  468. * @param object The parent node of the child elements to match
  469. * @param string The tag name to match on
  470. * @param string The text string to match on
  471. * @return boolean True if a matching child element exists
  472. */
  473. function hasNamedChildElementNotEqualToValue(&$parentNode, $nodeName, $nodeValue) {
  474. $isNumeric = false;
  475. if (is_numeric($nodeValue)) {
  476. $isNumeric = true;
  477. $nodeValue = floatval($nodeValue);
  478. }
  479. $total = $parentNode->childCount;
  480. for ($i = 0; $i < $total; $i++) {
  481. $currNode =& $parentNode->childNodes[$i];
  482. if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) {
  483. if ($isNumeric) {$compareVal = floatval($currNode->getText());}
  484. else {$compareVal = $currNode->getText();}
  485. if ($compareVal != $nodeValue) return true;
  486. }
  487. }
  488. return false;
  489. } //hasNamedChildElementNotEqualToValue
  490. /**
  491. * Selects nodes with child elements that are greater than the specified name and value
  492. * @param object The parent node of the child elements to match
  493. * @param string The tag name to match on
  494. * @param string The text string to match on
  495. * @return boolean True if a matching child element exists
  496. */
  497. function hasNamedChildElementGreaterThanValue(&$parentNode, $nodeName, $nodeValue) {
  498. $isNumeric = false;
  499. if (is_numeric($nodeValue)) {
  500. $isNumeric = true;
  501. $nodeValue = floatval($nodeValue);
  502. }
  503. $total = $parentNode->childCount;
  504. for ($i = 0; $i < $total; $i++) {
  505. $currNode =& $parentNode->childNodes[$i];
  506. if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) {
  507. if ($isNumeric) {$compareVal = floatval($currNode->getText());}
  508. else {$compareVal = $currNode->getText();}
  509. if ($compareVal > $nodeValue) return true;
  510. }
  511. }
  512. return false;
  513. } //hasNamedChildElementGreaterThanValue
  514. /**
  515. * Selects nodes with child elements that are less than the specified name and value
  516. * @param object The parent node of the child elements to match
  517. * @param string The tag name to match on
  518. * @param string The text string to match on
  519. * @return boolean True if a matching child element exists
  520. */
  521. function hasNamedChildElementLessThanValue(&$parentNode, $nodeName, $nodeValue) {
  522. $isNumeric = false;
  523. if (is_numeric($nodeValue)) {
  524. $isNumeric = true;
  525. $nodeValue = floatval($nodeValue);
  526. }
  527. $total = $parentNode->childCount;
  528. for ($i = 0; $i < $total; $i++) {
  529. $currNode =& $parentNode->childNodes[$i];
  530. if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) {
  531. if ($isNumeric) {$compareVal = floatval($currNode->getText());}
  532. else {$compareVal = $currNode->getText();}
  533. if ($compareVal < $nodeValue) return true;
  534. }
  535. }
  536. return false;
  537. } //hasNamedChildElementLessThanValue
  538. /**
  539. * Selects named elements of the specified index
  540. * @param string The pattern segment containing the node expression
  541. * @param int The index (base 1) of the matching node
  542. * @param boolean True if the selection is to be performed recursively
  543. */
  544. function refilterByIndex($index) {
  545. if ($index > 1) {
  546. if (count($this->globalNodeContainer) != 0) {
  547. $counter = 0;
  548. $lastParentID = null;
  549. foreach ($this->globalNodeContainer as $key =>$value) {
  550. $currNode =& $this->globalNodeContainer[$key];
  551. if (($lastParentID != null) && ($currNode->parentNode->uid != $lastParentID)) {
  552. $counter = 0;
  553. }
  554. $counter++;
  555. if (($counter == $index) && ($currNode->parentNode->uid == $lastParentID)) {
  556. $this->localNodeContainer[] =& $currNode;
  557. }
  558. $lastParentID = $currNode->parentNode->uid;
  559. }
  560. }
  561. }
  562. else {
  563. $this->localNodeContainer =& $this->globalNodeContainer;
  564. }
  565. } //refilterByIndex
  566. /**
  567. * Selects named elements of the specified index
  568. * @param string The pattern segment containing the node expression
  569. * @param int The index (base 1) of the matching node
  570. * @param boolean True if the selection is to be performed recursively
  571. */
  572. function filterByIndex($nodeName, $index, $deep) {
  573. if (count($this->globalNodeContainer) != 0) {
  574. foreach ($this->globalNodeContainer as $key =>$value) {
  575. $currNode =& $this->globalNodeContainer[$key];
  576. $this->_filterByIndex($currNode, $nodeName, $index, $deep);
  577. }
  578. }
  579. } //filterByIndex
  580. /**
  581. * Selects named elements of the specified index
  582. * @param object The context node
  583. * @param string The pattern segment containing the node expression
  584. * @param int The index (base 1) of the matching node
  585. * @param boolean True if the selection is to be performed recursively
  586. */
  587. function _filterByIndex(&$contextNode, $nodeName, $index, $deep) {
  588. if (($contextNode->nodeType == DOMIT_ELEMENT_NODE) ||
  589. ($contextNode->nodeType == DOMIT_DOCUMENT_NODE)) {
  590. $total = $contextNode->childCount;
  591. $nodeCounter = 0;
  592. for ($i = 0; $i < $total; $i++) {
  593. $currChildNode =& $contextNode->childNodes[$i];
  594. if ($currChildNode->nodeName == $nodeName) {
  595. $nodeCounter++;
  596. if ($nodeCounter == $index) {
  597. $this->localNodeContainer[] =& $currChildNode;
  598. }
  599. }
  600. if ($deep) {
  601. $this->_filterByIndex($currChildNode, $nodeName, $index, $deep);
  602. }
  603. }
  604. }
  605. } //_filterByIndex
  606. /**
  607. * Selects named elements with the specified named child
  608. * @param string The pattern segment containing the node expression
  609. * @param string The tag name of the matching child
  610. * @param boolean True if the selection is to be performed recursively
  611. */
  612. function filterByChildName($nodeName, $childName, $deep) {
  613. if (count($this->globalNodeContainer) != 0) {
  614. foreach ($this->globalNodeContainer as $key =>$value) {
  615. $currNode =& $this->globalNodeContainer[$key];
  616. $this->_filterByChildName($currNode, $nodeName, $childName, $deep);
  617. }
  618. }
  619. } //filterByChildName
  620. /**
  621. * Selects named elements with the specified named child
  622. * @param object The context node
  623. * @param string The pattern segment containing the node expression
  624. * @param string The tag name of the matching child
  625. * @param boolean True if the selection is to be performed recursively
  626. */
  627. function _filterByChildName(&$contextNode, $nodeName, $childName, $deep) {
  628. if (($contextNode->nodeType == DOMIT_ELEMENT_NODE) ||
  629. ($contextNode->nodeType == DOMIT_DOCUMENT_NODE)) {
  630. $total = $contextNode->childCount;
  631. for ($i = 0; $i < $total; $i++) {
  632. $currChildNode =& $contextNode->childNodes[$i];
  633. if (($currChildNode->nodeName == $nodeName) &&
  634. ($currChildNode->nodeType == DOMIT_ELEMENT_NODE)) {
  635. $total2 = $currChildNode->childCount;
  636. for ($j = 0; $j < $total2; $j++) {
  637. $currChildChildNode =& $currChildNode->childNodes[$j];
  638. if ($currChildChildNode->nodeName == $childName) {
  639. $this->localNodeContainer[] =& $currChildNode;
  640. }
  641. }
  642. }
  643. if ($deep) {
  644. $this->_filterByChildName($currChildNode, $nodeName, $childName, $deep);
  645. }
  646. }
  647. }
  648. } //_filterByChildName
  649. /**
  650. * Selects named attributes of the current context nodes
  651. * @param string The attribute name, or * to match all attributes
  652. */
  653. function selectAttribute($attrName) {
  654. if (count($this->globalNodeContainer) != 0) {
  655. foreach ($this->globalNodeContainer as $key =>$value) {
  656. $currNode =& $this->globalNodeContainer[$key];
  657. $isRecursive = ($this->searchType == DOMIT_XPATH_SEARCH_VARIABLE) ? true : false;
  658. $this->_selectAttribute($currNode, $attrName, $isRecursive);
  659. }
  660. }
  661. $this->charContainer = '';
  662. } //selectAttribute
  663. /**
  664. * Selects all attributes of the context nodes
  665. * @param object The context node
  666. * @param string The attribute name, or * to match all attributes
  667. * @param boolean True if the selection is to be performed recursively
  668. */
  669. function _selectAttribute(&$contextNode, $attrName, $deep) {
  670. if (($contextNode->nodeType == DOMIT_ELEMENT_NODE) ||
  671. ($contextNode->nodeType == DOMIT_DOCUMENT_NODE)) {
  672. $total = $contextNode->childCount;
  673. for ($i = 0; $i < $total; $i++) {
  674. $currNode =& $contextNode->childNodes[$i];
  675. if ($currNode->nodeType == DOMIT_ELEMENT_NODE) {
  676. if ($attrName == '*') {
  677. $total2 = $currNode->attributes->getLength();
  678. for ($j = 0; $j < $total2; $j++) {
  679. $this->localNodeContainer[] =& $currNode->attributes->item($j);
  680. }
  681. }
  682. else {
  683. if ($currNode->hasAttribute($attrName)) {
  684. $this->localNodeContainer[] =& $currNode->getAttributeNode($attrName);
  685. }
  686. }
  687. }
  688. if ($deep) {
  689. $this->_selectAttribute($currNode, $attrName, $deep);
  690. }
  691. }
  692. }
  693. } //_selectAttribute
  694. /**
  695. * Selects all child nodes of the current context nodes
  696. * @param string The element name
  697. */
  698. function selectNamedChild($tagName) {
  699. if (count($this->globalNodeContainer) != 0) {
  700. foreach ($this->globalNodeContainer as $key =>$value) {
  701. $currNode =& $this->globalNodeContainer[$key];
  702. $isRecursive = ($this->searchType == DOMIT_XPATH_SEARCH_VARIABLE) ? true : false;
  703. $this->_selectNamedChild($currNode, $tagName, $isRecursive);
  704. }
  705. }
  706. $this->charContainer = '';
  707. } //selectNamedChild
  708. /**
  709. * Selects all child nodes of the context node
  710. * @param object The context node
  711. * @param string The element name
  712. * @param boolean True if the selection is to be performed recursively
  713. */
  714. function _selectNamedChild(&$contextNode, $tagName, $deep = false) {
  715. if (($contextNode->nodeType == DOMIT_ELEMENT_NODE) ||
  716. ($contextNode->nodeType == DOMIT_DOCUMENT_NODE)) {
  717. $total = $contextNode->childCount;
  718. for ($i = 0; $i < $total; $i++) {
  719. $currChildNode =& $contextNode->childNodes[$i];
  720. if (($currChildNode->nodeType == DOMIT_ELEMENT_NODE) ||
  721. ($currChildNode->nodeType == DOMIT_DOCUMENT_NODE)) {
  722. if (($tagName == '*') || ($tagName == $currChildNode->nodeName)) {
  723. $this->localNodeContainer[] =& $currChildNode;
  724. }
  725. if ($deep) {
  726. $this->_selectNamedChild($currChildNode, $tagName, $deep);
  727. }
  728. }
  729. }
  730. }
  731. } //_selectNamedChild
  732. /**
  733. * Selects parent node of the current context nodes
  734. */
  735. function selectParent() {
  736. if (count($this->globalNodeContainer) != 0) {
  737. foreach ($this->globalNodeContainer as $key =>$value) {
  738. $currNode =& $this->globalNodeContainer[$key];
  739. $isRecursive = ($this->searchType == DOMIT_XPATH_SEARCH_VARIABLE) ? true : false;
  740. $this->_selectParent($currNode, $isRecursive);
  741. }
  742. }
  743. $this->charContainer = '';
  744. } //selectParent
  745. /**
  746. * Selects parent node of the current context nodes
  747. * @param object The context node
  748. * @param boolean True if the selection is to be performed recursively
  749. */
  750. function _selectParent(&$contextNode, $deep = false) {
  751. if ($contextNode->nodeType == DOMIT_ELEMENT_NODE) {
  752. if ($contextNode->parentNode != null) {
  753. $this->localNodeContainer[] =& $contextNode->parentNode;
  754. }
  755. }
  756. if ($deep) {
  757. if (($contextNode->nodeType == DOMIT_ELEMENT_NODE) ||
  758. ($contextNode->nodeType == DOMIT_DOCUMENT_NODE)) {
  759. $total = $contextNode->childCount;
  760. for ($i = 0; $i < $total; $i++) {
  761. $currNode =& $contextNode->childNodes[$i];
  762. if ($currNode->nodeType == DOMIT_ELEMENT_NODE) {
  763. $this->_selectParent($contextNode, $deep);
  764. }
  765. }
  766. }
  767. }
  768. } //_selectParent
  769. /**
  770. * Selects any nodes of the current context nodes which match the given function
  771. */
  772. function selectNodesByFunction() {
  773. $doProcess = false;
  774. $targetNodeType = -1;
  775. switch (strtolower(trim($this->charContainer))) {
  776. case 'last()':
  777. if (count($this->globalNodeContainer) != 0) {
  778. foreach ($this->globalNodeContainer as $key =>$value) {
  779. $currNode =& $this->globalNodeContainer[$key];
  780. if ($currNode->nodeType == DOMIT_ELEMENT_NODE) {
  781. if ($currNode->lastChild != null) {
  782. $this->localNodeContainer[] =& $currNode->lastChild;
  783. }
  784. }
  785. }
  786. }
  787. break;
  788. case 'text()':
  789. $doProcess = true;
  790. $targetNodeType = DOMIT_TEXT_NODE;
  791. break;
  792. case 'comment()':
  793. $doProcess = true;
  794. $targetNodeType = DOMIT_COMMENT_NODE;
  795. break;
  796. case 'processing-instruction()':
  797. $doProcess = true;
  798. $targetNodeType = DOMIT_PROCESSING_INSTRUCTION_NODE;
  799. break;
  800. }
  801. if ($doProcess) {
  802. if (count($this->globalNodeContainer) != 0) {
  803. foreach ($this->globalNodeContainer as $key =>$value) {
  804. $currNode =& $this->globalNodeContainer[$key];
  805. if ($currNode->nodeType == DOMIT_ELEMENT_NODE) {
  806. $total = $currNode->childCount;
  807. for ($j = 0; $j < $total; $j++) {
  808. if ($currNode->childNodes[$j]->nodeType == $targetNodeType) {
  809. $this->localNodeContainer[] =& $currNode->childNodes[$j];
  810. }
  811. }
  812. }
  813. }
  814. }
  815. }
  816. $this->charContainer = '';
  817. } //selectNodesByFunction
  818. /**
  819. * Splits the supplied pattern into searchable segments
  820. * @param string The pattern
  821. */
  822. function splitPattern($pattern) {
  823. //split multiple patterns if they exist (e.g. pattern1 | pattern2 | pattern3)
  824. $this->arPathSegments =& explode(DOMIT_XPATH_SEPARATOR_OR, $pattern);
  825. //split each pattern by relative path dividers (i.e., '//')
  826. $total = count($this->arPathSegments);
  827. for ($i = 0; $i < $total; $i++) {
  828. $this->arPathSegments[$i] =& explode(DOMIT_XPATH_SEPARATOR_RELATIVE, trim($this->arPathSegments[$i]));
  829. $currArray =& $this->arPathSegments[$i];
  830. $total2 = count($currArray);
  831. for ($j = 0; $j < $total2; $j++) {
  832. $currArray[$j] =& explode(DOMIT_XPATH_SEPARATOR_ABSOLUTE, $currArray[$j]);
  833. }
  834. }
  835. } //splitPattern
  836. /**
  837. * Converts long XPath syntax into abbreviated XPath syntax
  838. * @param string The pattern
  839. * @return string The normalized pattern
  840. */
  841. function normalize($pattern) {
  842. $pattern = strtr($pattern, $this->normalizationTable);
  843. while (strpos($pattern, ' ') !== false) {
  844. $pattern = str_replace(' ', ' ', $pattern);
  845. }
  846. $pattern = strtr($pattern, $this->normalizationTable2);
  847. $pattern = strtr($pattern, $this->normalizationTable3);
  848. return $pattern;
  849. } //normalize
  850. /**
  851. * Initializes the contextNode and searchType
  852. * @param array The current array of path segments
  853. * @return int The index of the first array item to begin the search at
  854. */
  855. function initSearch(&$currArPathSegments) {
  856. $this->globalNodeContainer = array();
  857. if (is_null($currArPathSegments[0])) {
  858. if (count($currArPathSegments) == 1) {
  859. //variable path
  860. $this->searchType = DOMIT_XPATH_SEARCH_VARIABLE;
  861. $this->globalNodeContainer[] =& $this->callingNode->ownerDocument;
  862. }
  863. else {
  864. //absolute path
  865. $this->searchType = DOMIT_XPATH_SEARCH_ABSOLUTE;
  866. $this->globalNodeContainer[] =& $this->callingNode->ownerDocument;
  867. }
  868. }
  869. else {
  870. //relative path
  871. $this->searchType = DOMIT_XPATH_SEARCH_RELATIVE;
  872. if ($this->callingNode->uid != $this->callingNode->ownerDocument->uid) {
  873. $this->globalNodeContainer[] =& $this->callingNode;
  874. }
  875. else {
  876. $this->globalNodeContainer[] =& $this->callingNode->ownerDocument;
  877. }
  878. }
  879. } //initSearch
  880. } //DOMIT_XPath
  881. ?>