Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/css-selector/XPath/Translator.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 1fec387a4317 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 /* | |
4 * This file is part of the Symfony package. | |
5 * | |
6 * (c) Fabien Potencier <fabien@symfony.com> | |
7 * | |
8 * For the full copyright and license information, please view the LICENSE | |
9 * file that was distributed with this source code. | |
10 */ | |
11 | |
12 namespace Symfony\Component\CssSelector\XPath; | |
13 | |
14 use Symfony\Component\CssSelector\Exception\ExpressionErrorException; | |
15 use Symfony\Component\CssSelector\Node\FunctionNode; | |
16 use Symfony\Component\CssSelector\Node\NodeInterface; | |
17 use Symfony\Component\CssSelector\Node\SelectorNode; | |
18 use Symfony\Component\CssSelector\Parser\Parser; | |
19 use Symfony\Component\CssSelector\Parser\ParserInterface; | |
20 | |
21 /** | |
22 * XPath expression translator interface. | |
23 * | |
24 * This component is a port of the Python cssselect library, | |
25 * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. | |
26 * | |
27 * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> | |
28 * | |
29 * @internal | |
30 */ | |
31 class Translator implements TranslatorInterface | |
32 { | |
33 /** | |
34 * @var ParserInterface | |
35 */ | |
36 private $mainParser; | |
37 | |
38 /** | |
39 * @var ParserInterface[] | |
40 */ | |
41 private $shortcutParsers = array(); | |
42 | |
43 /** | |
44 * @var Extension\ExtensionInterface | |
45 */ | |
46 private $extensions = array(); | |
47 | |
48 /** | |
49 * @var array | |
50 */ | |
51 private $nodeTranslators = array(); | |
52 | |
53 /** | |
54 * @var array | |
55 */ | |
56 private $combinationTranslators = array(); | |
57 | |
58 /** | |
59 * @var array | |
60 */ | |
61 private $functionTranslators = array(); | |
62 | |
63 /** | |
64 * @var array | |
65 */ | |
66 private $pseudoClassTranslators = array(); | |
67 | |
68 /** | |
69 * @var array | |
70 */ | |
71 private $attributeMatchingTranslators = array(); | |
72 | |
73 public function __construct(ParserInterface $parser = null) | |
74 { | |
75 $this->mainParser = $parser ?: new Parser(); | |
76 | |
77 $this | |
78 ->registerExtension(new Extension\NodeExtension()) | |
79 ->registerExtension(new Extension\CombinationExtension()) | |
80 ->registerExtension(new Extension\FunctionExtension()) | |
81 ->registerExtension(new Extension\PseudoClassExtension()) | |
82 ->registerExtension(new Extension\AttributeMatchingExtension()) | |
83 ; | |
84 } | |
85 | |
86 /** | |
87 * @param string $element | |
88 * | |
89 * @return string | |
90 */ | |
91 public static function getXpathLiteral($element) | |
92 { | |
93 if (false === strpos($element, "'")) { | |
94 return "'".$element."'"; | |
95 } | |
96 | |
97 if (false === strpos($element, '"')) { | |
98 return '"'.$element.'"'; | |
99 } | |
100 | |
101 $string = $element; | |
102 $parts = array(); | |
103 while (true) { | |
104 if (false !== $pos = strpos($string, "'")) { | |
105 $parts[] = sprintf("'%s'", substr($string, 0, $pos)); | |
106 $parts[] = "\"'\""; | |
107 $string = substr($string, $pos + 1); | |
108 } else { | |
109 $parts[] = "'$string'"; | |
110 break; | |
111 } | |
112 } | |
113 | |
114 return sprintf('concat(%s)', implode($parts, ', ')); | |
115 } | |
116 | |
117 /** | |
118 * {@inheritdoc} | |
119 */ | |
120 public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::') | |
121 { | |
122 $selectors = $this->parseSelectors($cssExpr); | |
123 | |
124 /** @var SelectorNode $selector */ | |
125 foreach ($selectors as $index => $selector) { | |
126 if (null !== $selector->getPseudoElement()) { | |
127 throw new ExpressionErrorException('Pseudo-elements are not supported.'); | |
128 } | |
129 | |
130 $selectors[$index] = $this->selectorToXPath($selector, $prefix); | |
131 } | |
132 | |
133 return implode(' | ', $selectors); | |
134 } | |
135 | |
136 /** | |
137 * {@inheritdoc} | |
138 */ | |
139 public function selectorToXPath(SelectorNode $selector, $prefix = 'descendant-or-self::') | |
140 { | |
141 return ($prefix ?: '').$this->nodeToXPath($selector); | |
142 } | |
143 | |
144 /** | |
145 * Registers an extension. | |
146 * | |
147 * @param Extension\ExtensionInterface $extension | |
148 * | |
149 * @return $this | |
150 */ | |
151 public function registerExtension(Extension\ExtensionInterface $extension) | |
152 { | |
153 $this->extensions[$extension->getName()] = $extension; | |
154 | |
155 $this->nodeTranslators = array_merge($this->nodeTranslators, $extension->getNodeTranslators()); | |
156 $this->combinationTranslators = array_merge($this->combinationTranslators, $extension->getCombinationTranslators()); | |
157 $this->functionTranslators = array_merge($this->functionTranslators, $extension->getFunctionTranslators()); | |
158 $this->pseudoClassTranslators = array_merge($this->pseudoClassTranslators, $extension->getPseudoClassTranslators()); | |
159 $this->attributeMatchingTranslators = array_merge($this->attributeMatchingTranslators, $extension->getAttributeMatchingTranslators()); | |
160 | |
161 return $this; | |
162 } | |
163 | |
164 /** | |
165 * @param string $name | |
166 * | |
167 * @return Extension\ExtensionInterface | |
168 * | |
169 * @throws ExpressionErrorException | |
170 */ | |
171 public function getExtension($name) | |
172 { | |
173 if (!isset($this->extensions[$name])) { | |
174 throw new ExpressionErrorException(sprintf('Extension "%s" not registered.', $name)); | |
175 } | |
176 | |
177 return $this->extensions[$name]; | |
178 } | |
179 | |
180 /** | |
181 * Registers a shortcut parser. | |
182 * | |
183 * @param ParserInterface $shortcut | |
184 * | |
185 * @return $this | |
186 */ | |
187 public function registerParserShortcut(ParserInterface $shortcut) | |
188 { | |
189 $this->shortcutParsers[] = $shortcut; | |
190 | |
191 return $this; | |
192 } | |
193 | |
194 /** | |
195 * @param NodeInterface $node | |
196 * | |
197 * @return XPathExpr | |
198 * | |
199 * @throws ExpressionErrorException | |
200 */ | |
201 public function nodeToXPath(NodeInterface $node) | |
202 { | |
203 if (!isset($this->nodeTranslators[$node->getNodeName()])) { | |
204 throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName())); | |
205 } | |
206 | |
207 return call_user_func($this->nodeTranslators[$node->getNodeName()], $node, $this); | |
208 } | |
209 | |
210 /** | |
211 * @param string $combiner | |
212 * @param NodeInterface $xpath | |
213 * @param NodeInterface $combinedXpath | |
214 * | |
215 * @return XPathExpr | |
216 * | |
217 * @throws ExpressionErrorException | |
218 */ | |
219 public function addCombination($combiner, NodeInterface $xpath, NodeInterface $combinedXpath) | |
220 { | |
221 if (!isset($this->combinationTranslators[$combiner])) { | |
222 throw new ExpressionErrorException(sprintf('Combiner "%s" not supported.', $combiner)); | |
223 } | |
224 | |
225 return call_user_func($this->combinationTranslators[$combiner], $this->nodeToXPath($xpath), $this->nodeToXPath($combinedXpath)); | |
226 } | |
227 | |
228 /** | |
229 * @param XPathExpr $xpath | |
230 * @param FunctionNode $function | |
231 * | |
232 * @return XPathExpr | |
233 * | |
234 * @throws ExpressionErrorException | |
235 */ | |
236 public function addFunction(XPathExpr $xpath, FunctionNode $function) | |
237 { | |
238 if (!isset($this->functionTranslators[$function->getName()])) { | |
239 throw new ExpressionErrorException(sprintf('Function "%s" not supported.', $function->getName())); | |
240 } | |
241 | |
242 return call_user_func($this->functionTranslators[$function->getName()], $xpath, $function); | |
243 } | |
244 | |
245 /** | |
246 * @param XPathExpr $xpath | |
247 * @param string $pseudoClass | |
248 * | |
249 * @return XPathExpr | |
250 * | |
251 * @throws ExpressionErrorException | |
252 */ | |
253 public function addPseudoClass(XPathExpr $xpath, $pseudoClass) | |
254 { | |
255 if (!isset($this->pseudoClassTranslators[$pseudoClass])) { | |
256 throw new ExpressionErrorException(sprintf('Pseudo-class "%s" not supported.', $pseudoClass)); | |
257 } | |
258 | |
259 return call_user_func($this->pseudoClassTranslators[$pseudoClass], $xpath); | |
260 } | |
261 | |
262 /** | |
263 * @param XPathExpr $xpath | |
264 * @param string $operator | |
265 * @param string $attribute | |
266 * @param string $value | |
267 * | |
268 * @return XPathExpr | |
269 * | |
270 * @throws ExpressionErrorException | |
271 */ | |
272 public function addAttributeMatching(XPathExpr $xpath, $operator, $attribute, $value) | |
273 { | |
274 if (!isset($this->attributeMatchingTranslators[$operator])) { | |
275 throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator)); | |
276 } | |
277 | |
278 return call_user_func($this->attributeMatchingTranslators[$operator], $xpath, $attribute, $value); | |
279 } | |
280 | |
281 /** | |
282 * @param string $css | |
283 * | |
284 * @return SelectorNode[] | |
285 */ | |
286 private function parseSelectors($css) | |
287 { | |
288 foreach ($this->shortcutParsers as $shortcut) { | |
289 $tokens = $shortcut->parse($css); | |
290 | |
291 if (!empty($tokens)) { | |
292 return $tokens; | |
293 } | |
294 } | |
295 | |
296 return $this->mainParser->parse($css); | |
297 } | |
298 } |