PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/zendframework/zend-dom/src/Document/Query.php

https://github.com/tmccormi/openemr
PHP | 195 lines | 137 code | 16 blank | 42 comment | 15 complexity | 68bbc3fb1d7ba9e4c0541081f6a5c851 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Dom\Document;
  10. use Zend\Dom\Document;
  11. use Zend\Dom\DOMXPath;
  12. /**
  13. * Query object executable in a Zend\Dom\Document
  14. */
  15. class Query
  16. {
  17. /**#@+
  18. * Query types
  19. */
  20. const TYPE_XPATH = 'TYPE_XPATH';
  21. const TYPE_CSS = 'TYPE_CSS';
  22. /**#@-*/
  23. /**
  24. * Perform the query on Document
  25. *
  26. * @param string $expression CSS selector or XPath query
  27. * @param Document $document Document to query
  28. * @param string $type The type of $expression
  29. * @param \DOMNode $contextNode
  30. * @return NodeList
  31. */
  32. public static function execute(
  33. $expression,
  34. Document $document,
  35. $type = self::TYPE_XPATH,
  36. \DOMNode $contextNode = null
  37. ) {
  38. // Expression check
  39. if ($type === static::TYPE_CSS) {
  40. $expression = static::cssToXpath($expression);
  41. }
  42. $xpath = new DOMXPath($document->getDomDocument());
  43. $xpathNamespaces = $document->getXpathNamespaces();
  44. foreach ($xpathNamespaces as $prefix => $namespaceUri) {
  45. $xpath->registerNamespace($prefix, $namespaceUri);
  46. }
  47. if ($xpathPhpfunctions = $document->getXpathPhpFunctions()) {
  48. $xpath->registerNamespace('php', 'http://php.net/xpath');
  49. if ($xpathPhpfunctions === true) {
  50. $xpath->registerPhpFunctions();
  51. } else {
  52. $xpath->registerPhpFunctions($xpathPhpfunctions);
  53. }
  54. }
  55. $nodeList = $xpath->queryWithErrorException($expression, $contextNode);
  56. return new NodeList($nodeList);
  57. }
  58. /**
  59. * Transform CSS expression to XPath
  60. *
  61. * @param string $path
  62. * @return string
  63. */
  64. public static function cssToXpath($path)
  65. {
  66. $path = (string) $path;
  67. if (strstr($path, ',')) {
  68. $paths = explode(',', $path);
  69. $expressions = [];
  70. foreach ($paths as $path) {
  71. $xpath = static::cssToXpath(trim($path));
  72. if (is_string($xpath)) {
  73. $expressions[] = $xpath;
  74. } elseif (is_array($xpath)) {
  75. $expressions = array_merge($expressions, $xpath);
  76. }
  77. }
  78. return implode('|', $expressions);
  79. }
  80. do {
  81. $placeholder = '{' . uniqid(mt_rand(), true) . '}';
  82. } while (strpos($path, $placeholder) !== false);
  83. // Arbitrary attribute value contains whitespace
  84. $path = preg_replace_callback(
  85. '/\[\S+?([\'"])((?!\1|\\\1).*?)\1\]/',
  86. function ($matches) use ($placeholder) {
  87. return str_replace($matches[2], preg_replace('/\s+/', $placeholder, $matches[2]), $matches[0]);
  88. },
  89. $path
  90. );
  91. $paths = ['//'];
  92. $path = preg_replace('|\s*>\s*|', '>', $path);
  93. $segments = preg_split('/\s+/', $path);
  94. $segments = str_replace($placeholder, ' ', $segments);
  95. foreach ($segments as $key => $segment) {
  96. $pathSegment = static::_tokenize($segment);
  97. if (0 == $key) {
  98. if (0 === strpos($pathSegment, '[contains(')) {
  99. $paths[0] .= '*' . ltrim($pathSegment, '*');
  100. } else {
  101. $paths[0] .= $pathSegment;
  102. }
  103. continue;
  104. }
  105. if (0 === strpos($pathSegment, '[contains(')) {
  106. foreach ($paths as $pathKey => $xpath) {
  107. $paths[$pathKey] .= '//*' . ltrim($pathSegment, '*');
  108. $paths[] = $xpath . $pathSegment;
  109. }
  110. } else {
  111. foreach ($paths as $pathKey => $xpath) {
  112. $paths[$pathKey] .= '//' . $pathSegment;
  113. }
  114. }
  115. }
  116. if (1 == count($paths)) {
  117. return $paths[0];
  118. }
  119. return implode('|', $paths);
  120. }
  121. // @codingStandardsIgnoreStart
  122. /**
  123. * Tokenize CSS expressions to XPath
  124. *
  125. * @param string $expression
  126. * @return string
  127. */
  128. protected static function _tokenize($expression)
  129. {
  130. // @codingStandardsIgnoreEnd
  131. // Child selectors
  132. $expression = str_replace('>', '/', $expression);
  133. // IDs
  134. $expression = preg_replace('|#([a-z][a-z0-9_-]*)|i', '[@id=\'$1\']', $expression);
  135. $expression = preg_replace('|(?<![a-z0-9_-])(\[@id=)|i', '*$1', $expression);
  136. // arbitrary attribute strict equality
  137. $expression = preg_replace_callback(
  138. '/\[@?([a-z0-9_-]+)=([\'"])((?!\2|\\\2).*?)\2\]/i',
  139. function ($matches) {
  140. return sprintf("[@%s='%s']", strtolower($matches[1]), str_replace("'", "\\'", $matches[3]));
  141. },
  142. $expression
  143. );
  144. // arbitrary attribute contains full word
  145. $expression = preg_replace_callback(
  146. '/\[([a-z0-9_-]+)~=([\'"])((?!\2|\\\2).*?)\2\]/i',
  147. function ($matches) {
  148. return "[contains(concat(' ', normalize-space(@" . strtolower($matches[1]) . "), ' '), ' "
  149. . $matches[3] . " ')]";
  150. },
  151. $expression
  152. );
  153. // arbitrary attribute contains specified content
  154. $expression = preg_replace_callback(
  155. '/\[([a-z0-9_-]+)\*=([\'"])((?!\2|\\\2).*?)\2\]/i',
  156. function ($matches) {
  157. return "[contains(@" . strtolower($matches[1]) . ", '"
  158. . $matches[3] . "')]";
  159. },
  160. $expression
  161. );
  162. // Classes
  163. if (false === strpos($expression, "[@")) {
  164. $expression = preg_replace(
  165. '|\.([a-z][a-z0-9_-]*)|i',
  166. "[contains(concat(' ', normalize-space(@class), ' '), ' \$1 ')]",
  167. $expression
  168. );
  169. }
  170. /** ZF-9764 -- remove double asterisk */
  171. $expression = str_replace('**', '*', $expression);
  172. return $expression;
  173. }
  174. }