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