Yaffs site version 1.1
[yaffs-website] / vendor / symfony / css-selector / XPath / Extension / NodeExtension.php
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\Extension;
13
14 use Symfony\Component\CssSelector\Node;
15 use Symfony\Component\CssSelector\XPath\Translator;
16 use Symfony\Component\CssSelector\XPath\XPathExpr;
17
18 /**
19  * XPath expression translator node extension.
20  *
21  * This component is a port of the Python cssselect library,
22  * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
23  *
24  * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
25  *
26  * @internal
27  */
28 class NodeExtension extends AbstractExtension
29 {
30     const ELEMENT_NAME_IN_LOWER_CASE = 1;
31     const ATTRIBUTE_NAME_IN_LOWER_CASE = 2;
32     const ATTRIBUTE_VALUE_IN_LOWER_CASE = 4;
33
34     /**
35      * @var int
36      */
37     private $flags;
38
39     /**
40      * Constructor.
41      *
42      * @param int $flags
43      */
44     public function __construct($flags = 0)
45     {
46         $this->flags = $flags;
47     }
48
49     /**
50      * @param int  $flag
51      * @param bool $on
52      *
53      * @return $this
54      */
55     public function setFlag($flag, $on)
56     {
57         if ($on && !$this->hasFlag($flag)) {
58             $this->flags += $flag;
59         }
60
61         if (!$on && $this->hasFlag($flag)) {
62             $this->flags -= $flag;
63         }
64
65         return $this;
66     }
67
68     /**
69      * @param int $flag
70      *
71      * @return bool
72      */
73     public function hasFlag($flag)
74     {
75         return (bool) ($this->flags & $flag);
76     }
77
78     /**
79      * {@inheritdoc}
80      */
81     public function getNodeTranslators()
82     {
83         return array(
84             'Selector' => array($this, 'translateSelector'),
85             'CombinedSelector' => array($this, 'translateCombinedSelector'),
86             'Negation' => array($this, 'translateNegation'),
87             'Function' => array($this, 'translateFunction'),
88             'Pseudo' => array($this, 'translatePseudo'),
89             'Attribute' => array($this, 'translateAttribute'),
90             'Class' => array($this, 'translateClass'),
91             'Hash' => array($this, 'translateHash'),
92             'Element' => array($this, 'translateElement'),
93         );
94     }
95
96     /**
97      * @param Node\SelectorNode $node
98      * @param Translator        $translator
99      *
100      * @return XPathExpr
101      */
102     public function translateSelector(Node\SelectorNode $node, Translator $translator)
103     {
104         return $translator->nodeToXPath($node->getTree());
105     }
106
107     /**
108      * @param Node\CombinedSelectorNode $node
109      * @param Translator                $translator
110      *
111      * @return XPathExpr
112      */
113     public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator)
114     {
115         return $translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector());
116     }
117
118     /**
119      * @param Node\NegationNode $node
120      * @param Translator        $translator
121      *
122      * @return XPathExpr
123      */
124     public function translateNegation(Node\NegationNode $node, Translator $translator)
125     {
126         $xpath = $translator->nodeToXPath($node->getSelector());
127         $subXpath = $translator->nodeToXPath($node->getSubSelector());
128         $subXpath->addNameTest();
129
130         if ($subXpath->getCondition()) {
131             return $xpath->addCondition(sprintf('not(%s)', $subXpath->getCondition()));
132         }
133
134         return $xpath->addCondition('0');
135     }
136
137     /**
138      * @param Node\FunctionNode $node
139      * @param Translator        $translator
140      *
141      * @return XPathExpr
142      */
143     public function translateFunction(Node\FunctionNode $node, Translator $translator)
144     {
145         $xpath = $translator->nodeToXPath($node->getSelector());
146
147         return $translator->addFunction($xpath, $node);
148     }
149
150     /**
151      * @param Node\PseudoNode $node
152      * @param Translator      $translator
153      *
154      * @return XPathExpr
155      */
156     public function translatePseudo(Node\PseudoNode $node, Translator $translator)
157     {
158         $xpath = $translator->nodeToXPath($node->getSelector());
159
160         return $translator->addPseudoClass($xpath, $node->getIdentifier());
161     }
162
163     /**
164      * @param Node\AttributeNode $node
165      * @param Translator         $translator
166      *
167      * @return XPathExpr
168      */
169     public function translateAttribute(Node\AttributeNode $node, Translator $translator)
170     {
171         $name = $node->getAttribute();
172         $safe = $this->isSafeName($name);
173
174         if ($this->hasFlag(self::ATTRIBUTE_NAME_IN_LOWER_CASE)) {
175             $name = strtolower($name);
176         }
177
178         if ($node->getNamespace()) {
179             $name = sprintf('%s:%s', $node->getNamespace(), $name);
180             $safe = $safe && $this->isSafeName($node->getNamespace());
181         }
182
183         $attribute = $safe ? '@'.$name : sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name));
184         $value = $node->getValue();
185         $xpath = $translator->nodeToXPath($node->getSelector());
186
187         if ($this->hasFlag(self::ATTRIBUTE_VALUE_IN_LOWER_CASE)) {
188             $value = strtolower($value);
189         }
190
191         return $translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value);
192     }
193
194     /**
195      * @param Node\ClassNode $node
196      * @param Translator     $translator
197      *
198      * @return XPathExpr
199      */
200     public function translateClass(Node\ClassNode $node, Translator $translator)
201     {
202         $xpath = $translator->nodeToXPath($node->getSelector());
203
204         return $translator->addAttributeMatching($xpath, '~=', '@class', $node->getName());
205     }
206
207     /**
208      * @param Node\HashNode $node
209      * @param Translator    $translator
210      *
211      * @return XPathExpr
212      */
213     public function translateHash(Node\HashNode $node, Translator $translator)
214     {
215         $xpath = $translator->nodeToXPath($node->getSelector());
216
217         return $translator->addAttributeMatching($xpath, '=', '@id', $node->getId());
218     }
219
220     /**
221      * @param Node\ElementNode $node
222      *
223      * @return XPathExpr
224      */
225     public function translateElement(Node\ElementNode $node)
226     {
227         $element = $node->getElement();
228
229         if ($this->hasFlag(self::ELEMENT_NAME_IN_LOWER_CASE)) {
230             $element = strtolower($element);
231         }
232
233         if ($element) {
234             $safe = $this->isSafeName($element);
235         } else {
236             $element = '*';
237             $safe = true;
238         }
239
240         if ($node->getNamespace()) {
241             $element = sprintf('%s:%s', $node->getNamespace(), $element);
242             $safe = $safe && $this->isSafeName($node->getNamespace());
243         }
244
245         $xpath = new XPathExpr('', $element);
246
247         if (!$safe) {
248             $xpath->addNameTest();
249         }
250
251         return $xpath;
252     }
253
254     /**
255      * {@inheritdoc}
256      */
257     public function getName()
258     {
259         return 'node';
260     }
261
262     /**
263      * Tests if given name is safe.
264      *
265      * @param string $name
266      *
267      * @return bool
268      */
269     private function isSafeName($name)
270     {
271         return 0 < preg_match('~^[a-zA-Z_][a-zA-Z0-9_.-]*$~', $name);
272     }
273 }