Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 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 int $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): static
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
    public function getNodeTranslators(): array
63
    {
64
        return [
65
            'Selector' => $this->translateSelector(...),
66
            'CombinedSelector' => $this->translateCombinedSelector(...),
67
            'Negation' => $this->translateNegation(...),
68
            'Function' => $this->translateFunction(...),
69
            'Pseudo' => $this->translatePseudo(...),
70
            'Attribute' => $this->translateAttribute(...),
71
            'Class' => $this->translateClass(...),
72
            'Hash' => $this->translateHash(...),
73
            'Element' => $this->translateElement(...),
74
        ];
75
    }
76
 
77
    public function translateSelector(Node\SelectorNode $node, Translator $translator): XPathExpr
78
    {
79
        return $translator->nodeToXPath($node->getTree());
80
    }
81
 
82
    public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator): XPathExpr
83
    {
84
        return $translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector());
85
    }
86
 
87
    public function translateNegation(Node\NegationNode $node, Translator $translator): XPathExpr
88
    {
89
        $xpath = $translator->nodeToXPath($node->getSelector());
90
        $subXpath = $translator->nodeToXPath($node->getSubSelector());
91
        $subXpath->addNameTest();
92
 
93
        if ($subXpath->getCondition()) {
94
            return $xpath->addCondition(sprintf('not(%s)', $subXpath->getCondition()));
95
        }
96
 
97
        return $xpath->addCondition('0');
98
    }
99
 
100
    public function translateFunction(Node\FunctionNode $node, Translator $translator): XPathExpr
101
    {
102
        $xpath = $translator->nodeToXPath($node->getSelector());
103
 
104
        return $translator->addFunction($xpath, $node);
105
    }
106
 
107
    public function translatePseudo(Node\PseudoNode $node, Translator $translator): XPathExpr
108
    {
109
        $xpath = $translator->nodeToXPath($node->getSelector());
110
 
111
        return $translator->addPseudoClass($xpath, $node->getIdentifier());
112
    }
113
 
114
    public function translateAttribute(Node\AttributeNode $node, Translator $translator): XPathExpr
115
    {
116
        $name = $node->getAttribute();
117
        $safe = $this->isSafeName($name);
118
 
119
        if ($this->hasFlag(self::ATTRIBUTE_NAME_IN_LOWER_CASE)) {
120
            $name = strtolower($name);
121
        }
122
 
123
        if ($node->getNamespace()) {
124
            $name = sprintf('%s:%s', $node->getNamespace(), $name);
125
            $safe = $safe && $this->isSafeName($node->getNamespace());
126
        }
127
 
128
        $attribute = $safe ? '@'.$name : sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name));
129
        $value = $node->getValue();
130
        $xpath = $translator->nodeToXPath($node->getSelector());
131
 
132
        if ($this->hasFlag(self::ATTRIBUTE_VALUE_IN_LOWER_CASE)) {
133
            $value = strtolower($value);
134
        }
135
 
136
        return $translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value);
137
    }
138
 
139
    public function translateClass(Node\ClassNode $node, Translator $translator): XPathExpr
140
    {
141
        $xpath = $translator->nodeToXPath($node->getSelector());
142
 
143
        return $translator->addAttributeMatching($xpath, '~=', '@class', $node->getName());
144
    }
145
 
146
    public function translateHash(Node\HashNode $node, Translator $translator): XPathExpr
147
    {
148
        $xpath = $translator->nodeToXPath($node->getSelector());
149
 
150
        return $translator->addAttributeMatching($xpath, '=', '@id', $node->getId());
151
    }
152
 
153
    public function translateElement(Node\ElementNode $node): XPathExpr
154
    {
155
        $element = $node->getElement();
156
 
157
        if ($element && $this->hasFlag(self::ELEMENT_NAME_IN_LOWER_CASE)) {
158
            $element = strtolower($element);
159
        }
160
 
161
        if ($element) {
162
            $safe = $this->isSafeName($element);
163
        } else {
164
            $element = '*';
165
            $safe = true;
166
        }
167
 
168
        if ($node->getNamespace()) {
169
            $element = sprintf('%s:%s', $node->getNamespace(), $element);
170
            $safe = $safe && $this->isSafeName($node->getNamespace());
171
        }
172
 
173
        $xpath = new XPathExpr('', $element);
174
 
175
        if (!$safe) {
176
            $xpath->addNameTest();
177
        }
178
 
179
        return $xpath;
180
    }
181
 
182
    public function getName(): string
183
    {
184
        return 'node';
185
    }
186
 
187
    private function isSafeName(string $name): bool
188
    {
189
        return 0 < preg_match('~^[a-zA-Z_][a-zA-Z0-9_.-]*$~', $name);
190
    }
191
}