715d9611a8267cbb5df811e34da411f0c04392ab
[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     private $flags;
35
36     /**
37      * @param int $flags
38      */
39     public function __construct($flags = 0)
40     {
41         $this->flags = $flags;
42     }
43
44     /**
45      * @param int  $flag
46      * @param bool $on
47      *
48      * @return $this
49      */
50     public function setFlag($flag, $on)
51     {
52         if ($on && !$this->hasFlag($flag)) {
53             $this->flags += $flag;
54         }
55
56         if (!$on && $this->hasFlag($flag)) {
57             $this->flags -= $flag;
58         }
59
60         return $this;
61     }
62
63     /**
64      * @param int $flag
65      *
66      * @return bool
67      */
68     public function hasFlag($flag)
69     {
70         return (bool) ($this->flags & $flag);
71     }
72
73     /**
74      * {@inheritdoc}
75      */
76     public function getNodeTranslators()
77     {
78         return array(
79             'Selector' => array($this, 'translateSelector'),
80             'CombinedSelector' => array($this, 'translateCombinedSelector'),
81             'Negation' => array($this, 'translateNegation'),
82             'Function' => array($this, 'translateFunction'),
83             'Pseudo' => array($this, 'translatePseudo'),
84             'Attribute' => array($this, 'translateAttribute'),
85             'Class' => array($this, 'translateClass'),
86             'Hash' => array($this, 'translateHash'),
87             'Element' => array($this, 'translateElement'),
88         );
89     }
90
91     /**
92      * @return XPathExpr
93      */
94     public function translateSelector(Node\SelectorNode $node, Translator $translator)
95     {
96         return $translator->nodeToXPath($node->getTree());
97     }
98
99     /**
100      * @return XPathExpr
101      */
102     public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator)
103     {
104         return $translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector());
105     }
106
107     /**
108      * @return XPathExpr
109      */
110     public function translateNegation(Node\NegationNode $node, Translator $translator)
111     {
112         $xpath = $translator->nodeToXPath($node->getSelector());
113         $subXpath = $translator->nodeToXPath($node->getSubSelector());
114         $subXpath->addNameTest();
115
116         if ($subXpath->getCondition()) {
117             return $xpath->addCondition(sprintf('not(%s)', $subXpath->getCondition()));
118         }
119
120         return $xpath->addCondition('0');
121     }
122
123     /**
124      * @return XPathExpr
125      */
126     public function translateFunction(Node\FunctionNode $node, Translator $translator)
127     {
128         $xpath = $translator->nodeToXPath($node->getSelector());
129
130         return $translator->addFunction($xpath, $node);
131     }
132
133     /**
134      * @return XPathExpr
135      */
136     public function translatePseudo(Node\PseudoNode $node, Translator $translator)
137     {
138         $xpath = $translator->nodeToXPath($node->getSelector());
139
140         return $translator->addPseudoClass($xpath, $node->getIdentifier());
141     }
142
143     /**
144      * @return XPathExpr
145      */
146     public function translateAttribute(Node\AttributeNode $node, Translator $translator)
147     {
148         $name = $node->getAttribute();
149         $safe = $this->isSafeName($name);
150
151         if ($this->hasFlag(self::ATTRIBUTE_NAME_IN_LOWER_CASE)) {
152             $name = strtolower($name);
153         }
154
155         if ($node->getNamespace()) {
156             $name = sprintf('%s:%s', $node->getNamespace(), $name);
157             $safe = $safe && $this->isSafeName($node->getNamespace());
158         }
159
160         $attribute = $safe ? '@'.$name : sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name));
161         $value = $node->getValue();
162         $xpath = $translator->nodeToXPath($node->getSelector());
163
164         if ($this->hasFlag(self::ATTRIBUTE_VALUE_IN_LOWER_CASE)) {
165             $value = strtolower($value);
166         }
167
168         return $translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value);
169     }
170
171     /**
172      * @return XPathExpr
173      */
174     public function translateClass(Node\ClassNode $node, Translator $translator)
175     {
176         $xpath = $translator->nodeToXPath($node->getSelector());
177
178         return $translator->addAttributeMatching($xpath, '~=', '@class', $node->getName());
179     }
180
181     /**
182      * @return XPathExpr
183      */
184     public function translateHash(Node\HashNode $node, Translator $translator)
185     {
186         $xpath = $translator->nodeToXPath($node->getSelector());
187
188         return $translator->addAttributeMatching($xpath, '=', '@id', $node->getId());
189     }
190
191     /**
192      * @return XPathExpr
193      */
194     public function translateElement(Node\ElementNode $node)
195     {
196         $element = $node->getElement();
197
198         if ($this->hasFlag(self::ELEMENT_NAME_IN_LOWER_CASE)) {
199             $element = strtolower($element);
200         }
201
202         if ($element) {
203             $safe = $this->isSafeName($element);
204         } else {
205             $element = '*';
206             $safe = true;
207         }
208
209         if ($node->getNamespace()) {
210             $element = sprintf('%s:%s', $node->getNamespace(), $element);
211             $safe = $safe && $this->isSafeName($node->getNamespace());
212         }
213
214         $xpath = new XPathExpr('', $element);
215
216         if (!$safe) {
217             $xpath->addNameTest();
218         }
219
220         return $xpath;
221     }
222
223     /**
224      * {@inheritdoc}
225      */
226     public function getName()
227     {
228         return 'node';
229     }
230
231     /**
232      * Tests if given name is safe.
233      *
234      * @param string $name
235      *
236      * @return bool
237      */
238     private function isSafeName($name)
239     {
240         return 0 < preg_match('~^[a-zA-Z_][a-zA-Z0-9_.-]*$~', $name);
241     }
242 }