annotate vendor/symfony/css-selector/XPath/Extension/NodeExtension.php @ 19:fa3358dc1485 tip

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