/Zend/Dom/Query/Css2Xpath.php

https://github.com/ftaiolivista/Zend-Framework-Namespaced- · PHP · 174 lines · 92 code · 15 blank · 67 comment · 10 complexity · c343d28292633ac27aa17b38e4ca7b27 MD5 · raw file

  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Dom
  17. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. */
  20. /**
  21. * @namespace
  22. */
  23. namespace Zend\Dom\Query;
  24. /**
  25. * Transform CSS selectors to XPath
  26. *
  27. * @package Zend_Dom
  28. * @subpackage Query
  29. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  30. * @license http://framework.zend.com/license/new-bsd New BSD License
  31. * @version $Id: Css2Xpath.php 22044 2010-04-28 19:58:29Z matthew $
  32. */
  33. class Css2Xpath
  34. {
  35. /**
  36. * Transform CSS expression to XPath
  37. *
  38. * @param string $path
  39. * @return string
  40. */
  41. public static function transform($path)
  42. {
  43. $path = (string) $path;
  44. if (strstr($path, ',')) {
  45. $paths = explode(',', $path);
  46. $expressions = array();
  47. foreach ($paths as $path) {
  48. $xpath = self::transform(trim($path));
  49. if (is_string($xpath)) {
  50. $expressions[] = $xpath;
  51. } elseif (is_array($xpath)) {
  52. $expressions = array_merge($expressions, $xpath);
  53. }
  54. }
  55. return implode('|', $expressions);
  56. }
  57. $paths = array('//');
  58. $path = preg_replace('|\s+>\s+|', '>', $path);
  59. $segments = preg_split('/\s+/', $path);
  60. foreach ($segments as $key => $segment) {
  61. $pathSegment = self::_tokenize($segment);
  62. if (0 == $key) {
  63. if (0 === strpos($pathSegment, '[contains(')) {
  64. $paths[0] .= '*' . ltrim($pathSegment, '*');
  65. } else {
  66. $paths[0] .= $pathSegment;
  67. }
  68. continue;
  69. }
  70. if (0 === strpos($pathSegment, '[contains(')) {
  71. foreach ($paths as $key => $xpath) {
  72. $paths[$key] .= '//*' . ltrim($pathSegment, '*');
  73. $paths[] = $xpath . $pathSegment;
  74. }
  75. } else {
  76. foreach ($paths as $key => $xpath) {
  77. $paths[$key] .= '//' . $pathSegment;
  78. }
  79. }
  80. }
  81. if (1 == count($paths)) {
  82. return $paths[0];
  83. }
  84. return implode('|', $paths);
  85. }
  86. /**
  87. * Tokenize CSS expressions to XPath
  88. *
  89. * @param string $expression
  90. * @return string
  91. */
  92. protected static function _tokenize($expression)
  93. {
  94. // Child selectors
  95. $expression = str_replace('>', '/', $expression);
  96. // IDs
  97. $expression = preg_replace('|#([a-z][a-z0-9_-]*)|i', '[@id=\'$1\']', $expression);
  98. $expression = preg_replace('|(?<![a-z0-9_-])(\[@id=)|i', '*$1', $expression);
  99. // arbitrary attribute strict equality
  100. $expression = preg_replace_callback(
  101. '|\[([a-z0-9_-]+)=[\'"]([^\'"]+)[\'"]\]|i',
  102. array(__CLASS__, '_createEqualityExpression'),
  103. $expression
  104. );
  105. // arbitrary attribute contains full word
  106. $expression = preg_replace_callback(
  107. '|\[([a-z0-9_-]+)~=[\'"]([^\'"]+)[\'"]\]|i',
  108. array(__CLASS__, '_normalizeSpaceAttribute'),
  109. $expression
  110. );
  111. // arbitrary attribute contains specified content
  112. $expression = preg_replace_callback(
  113. '|\[([a-z0-9_-]+)\*=[\'"]([^\'"]+)[\'"]\]|i',
  114. array(__CLASS__, '_createContainsExpression'),
  115. $expression
  116. );
  117. // Classes
  118. $expression = preg_replace(
  119. '|\.([a-z][a-z0-9_-]*)|i',
  120. "[contains(concat(' ', normalize-space(@class), ' '), ' \$1 ')]",
  121. $expression
  122. );
  123. /** ZF-9764 -- remove double asterix */
  124. $expression = str_replace('**', '*', $expression);
  125. return $expression;
  126. }
  127. /**
  128. * Callback for creating equality expressions
  129. *
  130. * @param array $matches
  131. * @return string
  132. */
  133. protected static function _createEqualityExpression($matches)
  134. {
  135. return '[@' . strtolower($matches[1]) . "='" . $matches[2] . "']";
  136. }
  137. /**
  138. * Callback for creating expressions to match one or more attribute values
  139. *
  140. * @param array $matches
  141. * @return string
  142. */
  143. protected static function _normalizeSpaceAttribute($matches)
  144. {
  145. return "[contains(concat(' ', normalize-space(@" . strtolower($matches[1]) . "), ' '), ' "
  146. . $matches[2] . " ')]";
  147. }
  148. /**
  149. * Callback for creating a strict "contains" expression
  150. *
  151. * @param array $matches
  152. * @return string
  153. */
  154. protected static function _createContainsExpression($matches)
  155. {
  156. return "[contains(@" . strtolower($matches[1]) . ", '"
  157. . $matches[2] . "')]";
  158. }
  159. }