[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/_test/vendor/symfony/css-selector/XPath/Extension/ -> NodeExtension.php (source)

   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  }