annotate vendor/symfony/css-selector/XPath/Extension/NodeExtension.php @ 0:4c8ae668cc8c

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