|
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 /** |
|
22 * Transform CSS selectors to XPath |
|
23 * |
|
24 * @package Zend_Dom |
|
25 * @subpackage Query |
|
26 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
27 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
28 * @version $Id: Css2Xpath.php 22044 2010-04-28 19:58:29Z matthew $ |
|
29 */ |
|
30 class Zend_Dom_Query_Css2Xpath |
|
31 { |
|
32 /** |
|
33 * Transform CSS expression to XPath |
|
34 * |
|
35 * @param string $path |
|
36 * @return string |
|
37 */ |
|
38 public static function transform($path) |
|
39 { |
|
40 $path = (string) $path; |
|
41 if (strstr($path, ',')) { |
|
42 $paths = explode(',', $path); |
|
43 $expressions = array(); |
|
44 foreach ($paths as $path) { |
|
45 $xpath = self::transform(trim($path)); |
|
46 if (is_string($xpath)) { |
|
47 $expressions[] = $xpath; |
|
48 } elseif (is_array($xpath)) { |
|
49 $expressions = array_merge($expressions, $xpath); |
|
50 } |
|
51 } |
|
52 return implode('|', $expressions); |
|
53 } |
|
54 |
|
55 $paths = array('//'); |
|
56 $path = preg_replace('|\s+>\s+|', '>', $path); |
|
57 $segments = preg_split('/\s+/', $path); |
|
58 foreach ($segments as $key => $segment) { |
|
59 $pathSegment = self::_tokenize($segment); |
|
60 if (0 == $key) { |
|
61 if (0 === strpos($pathSegment, '[contains(')) { |
|
62 $paths[0] .= '*' . ltrim($pathSegment, '*'); |
|
63 } else { |
|
64 $paths[0] .= $pathSegment; |
|
65 } |
|
66 continue; |
|
67 } |
|
68 if (0 === strpos($pathSegment, '[contains(')) { |
|
69 foreach ($paths as $key => $xpath) { |
|
70 $paths[$key] .= '//*' . ltrim($pathSegment, '*'); |
|
71 $paths[] = $xpath . $pathSegment; |
|
72 } |
|
73 } else { |
|
74 foreach ($paths as $key => $xpath) { |
|
75 $paths[$key] .= '//' . $pathSegment; |
|
76 } |
|
77 } |
|
78 } |
|
79 |
|
80 if (1 == count($paths)) { |
|
81 return $paths[0]; |
|
82 } |
|
83 return implode('|', $paths); |
|
84 } |
|
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 |
|
97 // IDs |
|
98 $expression = preg_replace('|#([a-z][a-z0-9_-]*)|i', '[@id=\'$1\']', $expression); |
|
99 $expression = preg_replace('|(?<![a-z0-9_-])(\[@id=)|i', '*$1', $expression); |
|
100 |
|
101 // arbitrary attribute strict equality |
|
102 $expression = preg_replace_callback( |
|
103 '|\[([a-z0-9_-]+)=[\'"]([^\'"]+)[\'"]\]|i', |
|
104 array(__CLASS__, '_createEqualityExpression'), |
|
105 $expression |
|
106 ); |
|
107 |
|
108 // arbitrary attribute contains full word |
|
109 $expression = preg_replace_callback( |
|
110 '|\[([a-z0-9_-]+)~=[\'"]([^\'"]+)[\'"]\]|i', |
|
111 array(__CLASS__, '_normalizeSpaceAttribute'), |
|
112 $expression |
|
113 ); |
|
114 |
|
115 // arbitrary attribute contains specified content |
|
116 $expression = preg_replace_callback( |
|
117 '|\[([a-z0-9_-]+)\*=[\'"]([^\'"]+)[\'"]\]|i', |
|
118 array(__CLASS__, '_createContainsExpression'), |
|
119 $expression |
|
120 ); |
|
121 |
|
122 // Classes |
|
123 $expression = preg_replace( |
|
124 '|\.([a-z][a-z0-9_-]*)|i', |
|
125 "[contains(concat(' ', normalize-space(@class), ' '), ' \$1 ')]", |
|
126 $expression |
|
127 ); |
|
128 |
|
129 /** ZF-9764 -- remove double asterix */ |
|
130 $expression = str_replace('**', '*', $expression); |
|
131 |
|
132 return $expression; |
|
133 } |
|
134 |
|
135 /** |
|
136 * Callback for creating equality expressions |
|
137 * |
|
138 * @param array $matches |
|
139 * @return string |
|
140 */ |
|
141 protected static function _createEqualityExpression($matches) |
|
142 { |
|
143 return '[@' . strtolower($matches[1]) . "='" . $matches[2] . "']"; |
|
144 } |
|
145 |
|
146 /** |
|
147 * Callback for creating expressions to match one or more attribute values |
|
148 * |
|
149 * @param array $matches |
|
150 * @return string |
|
151 */ |
|
152 protected static function _normalizeSpaceAttribute($matches) |
|
153 { |
|
154 return "[contains(concat(' ', normalize-space(@" . strtolower($matches[1]) . "), ' '), ' " |
|
155 . $matches[2] . " ')]"; |
|
156 } |
|
157 |
|
158 /** |
|
159 * Callback for creating a strict "contains" expression |
|
160 * |
|
161 * @param array $matches |
|
162 * @return string |
|
163 */ |
|
164 protected static function _createContainsExpression($matches) |
|
165 { |
|
166 return "[contains(@" . strtolower($matches[1]) . ", '" |
|
167 . $matches[2] . "')]"; |
|
168 } |
|
169 } |