Completed
Branch updates-from-cafe (53b467)
by
unknown
17:55 queued 12:28
created
vendor/symfony/css-selector/Parser/TokenStream.php 1 patch
Indentation   +127 added lines, -127 removed lines patch added patch discarded remove patch
@@ -26,131 +26,131 @@
 block discarded – undo
26 26
  */
27 27
 class TokenStream
28 28
 {
29
-    /**
30
-     * @var Token[]
31
-     */
32
-    private array $tokens = [];
33
-
34
-    /**
35
-     * @var Token[]
36
-     */
37
-    private array $used = [];
38
-
39
-    private int $cursor = 0;
40
-    private ?Token $peeked;
41
-    private bool $peeking = false;
42
-
43
-    /**
44
-     * Pushes a token.
45
-     *
46
-     * @return $this
47
-     */
48
-    public function push(Token $token): static
49
-    {
50
-        $this->tokens[] = $token;
51
-
52
-        return $this;
53
-    }
54
-
55
-    /**
56
-     * Freezes stream.
57
-     *
58
-     * @return $this
59
-     */
60
-    public function freeze(): static
61
-    {
62
-        return $this;
63
-    }
64
-
65
-    /**
66
-     * Returns next token.
67
-     *
68
-     * @throws InternalErrorException If there is no more token
69
-     */
70
-    public function getNext(): Token
71
-    {
72
-        if ($this->peeking) {
73
-            $this->peeking = false;
74
-            $this->used[] = $this->peeked;
75
-
76
-            return $this->peeked;
77
-        }
78
-
79
-        if (!isset($this->tokens[$this->cursor])) {
80
-            throw new InternalErrorException('Unexpected token stream end.');
81
-        }
82
-
83
-        return $this->tokens[$this->cursor++];
84
-    }
85
-
86
-    /**
87
-     * Returns peeked token.
88
-     */
89
-    public function getPeek(): Token
90
-    {
91
-        if (!$this->peeking) {
92
-            $this->peeked = $this->getNext();
93
-            $this->peeking = true;
94
-        }
95
-
96
-        return $this->peeked;
97
-    }
98
-
99
-    /**
100
-     * Returns used tokens.
101
-     *
102
-     * @return Token[]
103
-     */
104
-    public function getUsed(): array
105
-    {
106
-        return $this->used;
107
-    }
108
-
109
-    /**
110
-     * Returns next identifier token.
111
-     *
112
-     * @throws SyntaxErrorException If next token is not an identifier
113
-     */
114
-    public function getNextIdentifier(): string
115
-    {
116
-        $next = $this->getNext();
117
-
118
-        if (!$next->isIdentifier()) {
119
-            throw SyntaxErrorException::unexpectedToken('identifier', $next);
120
-        }
121
-
122
-        return $next->getValue();
123
-    }
124
-
125
-    /**
126
-     * Returns next identifier or null if star delimiter token is found.
127
-     *
128
-     * @throws SyntaxErrorException If next token is not an identifier or a star delimiter
129
-     */
130
-    public function getNextIdentifierOrStar(): ?string
131
-    {
132
-        $next = $this->getNext();
133
-
134
-        if ($next->isIdentifier()) {
135
-            return $next->getValue();
136
-        }
137
-
138
-        if ($next->isDelimiter(['*'])) {
139
-            return null;
140
-        }
141
-
142
-        throw SyntaxErrorException::unexpectedToken('identifier or "*"', $next);
143
-    }
144
-
145
-    /**
146
-     * Skips next whitespace if any.
147
-     */
148
-    public function skipWhitespace()
149
-    {
150
-        $peek = $this->getPeek();
151
-
152
-        if ($peek->isWhitespace()) {
153
-            $this->getNext();
154
-        }
155
-    }
29
+	/**
30
+	 * @var Token[]
31
+	 */
32
+	private array $tokens = [];
33
+
34
+	/**
35
+	 * @var Token[]
36
+	 */
37
+	private array $used = [];
38
+
39
+	private int $cursor = 0;
40
+	private ?Token $peeked;
41
+	private bool $peeking = false;
42
+
43
+	/**
44
+	 * Pushes a token.
45
+	 *
46
+	 * @return $this
47
+	 */
48
+	public function push(Token $token): static
49
+	{
50
+		$this->tokens[] = $token;
51
+
52
+		return $this;
53
+	}
54
+
55
+	/**
56
+	 * Freezes stream.
57
+	 *
58
+	 * @return $this
59
+	 */
60
+	public function freeze(): static
61
+	{
62
+		return $this;
63
+	}
64
+
65
+	/**
66
+	 * Returns next token.
67
+	 *
68
+	 * @throws InternalErrorException If there is no more token
69
+	 */
70
+	public function getNext(): Token
71
+	{
72
+		if ($this->peeking) {
73
+			$this->peeking = false;
74
+			$this->used[] = $this->peeked;
75
+
76
+			return $this->peeked;
77
+		}
78
+
79
+		if (!isset($this->tokens[$this->cursor])) {
80
+			throw new InternalErrorException('Unexpected token stream end.');
81
+		}
82
+
83
+		return $this->tokens[$this->cursor++];
84
+	}
85
+
86
+	/**
87
+	 * Returns peeked token.
88
+	 */
89
+	public function getPeek(): Token
90
+	{
91
+		if (!$this->peeking) {
92
+			$this->peeked = $this->getNext();
93
+			$this->peeking = true;
94
+		}
95
+
96
+		return $this->peeked;
97
+	}
98
+
99
+	/**
100
+	 * Returns used tokens.
101
+	 *
102
+	 * @return Token[]
103
+	 */
104
+	public function getUsed(): array
105
+	{
106
+		return $this->used;
107
+	}
108
+
109
+	/**
110
+	 * Returns next identifier token.
111
+	 *
112
+	 * @throws SyntaxErrorException If next token is not an identifier
113
+	 */
114
+	public function getNextIdentifier(): string
115
+	{
116
+		$next = $this->getNext();
117
+
118
+		if (!$next->isIdentifier()) {
119
+			throw SyntaxErrorException::unexpectedToken('identifier', $next);
120
+		}
121
+
122
+		return $next->getValue();
123
+	}
124
+
125
+	/**
126
+	 * Returns next identifier or null if star delimiter token is found.
127
+	 *
128
+	 * @throws SyntaxErrorException If next token is not an identifier or a star delimiter
129
+	 */
130
+	public function getNextIdentifierOrStar(): ?string
131
+	{
132
+		$next = $this->getNext();
133
+
134
+		if ($next->isIdentifier()) {
135
+			return $next->getValue();
136
+		}
137
+
138
+		if ($next->isDelimiter(['*'])) {
139
+			return null;
140
+		}
141
+
142
+		throw SyntaxErrorException::unexpectedToken('identifier or "*"', $next);
143
+	}
144
+
145
+	/**
146
+	 * Skips next whitespace if any.
147
+	 */
148
+	public function skipWhitespace()
149
+	{
150
+		$peek = $this->getPeek();
151
+
152
+		if ($peek->isWhitespace()) {
153
+			$this->getNext();
154
+		}
155
+	}
156 156
 }
Please login to merge, or discard this patch.
vendor/symfony/css-selector/Parser/Parser.php 1 patch
Indentation   +323 added lines, -323 removed lines patch added patch discarded remove patch
@@ -27,327 +27,327 @@
 block discarded – undo
27 27
  */
28 28
 class Parser implements ParserInterface
29 29
 {
30
-    private Tokenizer $tokenizer;
31
-
32
-    public function __construct(Tokenizer $tokenizer = null)
33
-    {
34
-        $this->tokenizer = $tokenizer ?? new Tokenizer();
35
-    }
36
-
37
-    /**
38
-     * {@inheritdoc}
39
-     */
40
-    public function parse(string $source): array
41
-    {
42
-        $reader = new Reader($source);
43
-        $stream = $this->tokenizer->tokenize($reader);
44
-
45
-        return $this->parseSelectorList($stream);
46
-    }
47
-
48
-    /**
49
-     * Parses the arguments for ":nth-child()" and friends.
50
-     *
51
-     * @param Token[] $tokens
52
-     *
53
-     * @throws SyntaxErrorException
54
-     */
55
-    public static function parseSeries(array $tokens): array
56
-    {
57
-        foreach ($tokens as $token) {
58
-            if ($token->isString()) {
59
-                throw SyntaxErrorException::stringAsFunctionArgument();
60
-            }
61
-        }
62
-
63
-        $joined = trim(implode('', array_map(function (Token $token) {
64
-            return $token->getValue();
65
-        }, $tokens)));
66
-
67
-        $int = function ($string) {
68
-            if (!is_numeric($string)) {
69
-                throw SyntaxErrorException::stringAsFunctionArgument();
70
-            }
71
-
72
-            return (int) $string;
73
-        };
74
-
75
-        switch (true) {
76
-            case 'odd' === $joined:
77
-                return [2, 1];
78
-            case 'even' === $joined:
79
-                return [2, 0];
80
-            case 'n' === $joined:
81
-                return [1, 0];
82
-            case !str_contains($joined, 'n'):
83
-                return [0, $int($joined)];
84
-        }
85
-
86
-        $split = explode('n', $joined);
87
-        $first = $split[0] ?? null;
88
-
89
-        return [
90
-            $first ? ('-' === $first || '+' === $first ? $int($first.'1') : $int($first)) : 1,
91
-            isset($split[1]) && $split[1] ? $int($split[1]) : 0,
92
-        ];
93
-    }
94
-
95
-    private function parseSelectorList(TokenStream $stream): array
96
-    {
97
-        $stream->skipWhitespace();
98
-        $selectors = [];
99
-
100
-        while (true) {
101
-            $selectors[] = $this->parserSelectorNode($stream);
102
-
103
-            if ($stream->getPeek()->isDelimiter([','])) {
104
-                $stream->getNext();
105
-                $stream->skipWhitespace();
106
-            } else {
107
-                break;
108
-            }
109
-        }
110
-
111
-        return $selectors;
112
-    }
113
-
114
-    private function parserSelectorNode(TokenStream $stream): Node\SelectorNode
115
-    {
116
-        [$result, $pseudoElement] = $this->parseSimpleSelector($stream);
117
-
118
-        while (true) {
119
-            $stream->skipWhitespace();
120
-            $peek = $stream->getPeek();
121
-
122
-            if ($peek->isFileEnd() || $peek->isDelimiter([','])) {
123
-                break;
124
-            }
125
-
126
-            if (null !== $pseudoElement) {
127
-                throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector');
128
-            }
129
-
130
-            if ($peek->isDelimiter(['+', '>', '~'])) {
131
-                $combinator = $stream->getNext()->getValue();
132
-                $stream->skipWhitespace();
133
-            } else {
134
-                $combinator = ' ';
135
-            }
136
-
137
-            [$nextSelector, $pseudoElement] = $this->parseSimpleSelector($stream);
138
-            $result = new Node\CombinedSelectorNode($result, $combinator, $nextSelector);
139
-        }
140
-
141
-        return new Node\SelectorNode($result, $pseudoElement);
142
-    }
143
-
144
-    /**
145
-     * Parses next simple node (hash, class, pseudo, negation).
146
-     *
147
-     * @throws SyntaxErrorException
148
-     */
149
-    private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = false): array
150
-    {
151
-        $stream->skipWhitespace();
152
-
153
-        $selectorStart = \count($stream->getUsed());
154
-        $result = $this->parseElementNode($stream);
155
-        $pseudoElement = null;
156
-
157
-        while (true) {
158
-            $peek = $stream->getPeek();
159
-            if ($peek->isWhitespace()
160
-                || $peek->isFileEnd()
161
-                || $peek->isDelimiter([',', '+', '>', '~'])
162
-                || ($insideNegation && $peek->isDelimiter([')']))
163
-            ) {
164
-                break;
165
-            }
166
-
167
-            if (null !== $pseudoElement) {
168
-                throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector');
169
-            }
170
-
171
-            if ($peek->isHash()) {
172
-                $result = new Node\HashNode($result, $stream->getNext()->getValue());
173
-            } elseif ($peek->isDelimiter(['.'])) {
174
-                $stream->getNext();
175
-                $result = new Node\ClassNode($result, $stream->getNextIdentifier());
176
-            } elseif ($peek->isDelimiter(['['])) {
177
-                $stream->getNext();
178
-                $result = $this->parseAttributeNode($result, $stream);
179
-            } elseif ($peek->isDelimiter([':'])) {
180
-                $stream->getNext();
181
-
182
-                if ($stream->getPeek()->isDelimiter([':'])) {
183
-                    $stream->getNext();
184
-                    $pseudoElement = $stream->getNextIdentifier();
185
-
186
-                    continue;
187
-                }
188
-
189
-                $identifier = $stream->getNextIdentifier();
190
-                if (\in_array(strtolower($identifier), ['first-line', 'first-letter', 'before', 'after'])) {
191
-                    // Special case: CSS 2.1 pseudo-elements can have a single ':'.
192
-                    // Any new pseudo-element must have two.
193
-                    $pseudoElement = $identifier;
194
-
195
-                    continue;
196
-                }
197
-
198
-                if (!$stream->getPeek()->isDelimiter(['('])) {
199
-                    $result = new Node\PseudoNode($result, $identifier);
200
-
201
-                    continue;
202
-                }
203
-
204
-                $stream->getNext();
205
-                $stream->skipWhitespace();
206
-
207
-                if ('not' === strtolower($identifier)) {
208
-                    if ($insideNegation) {
209
-                        throw SyntaxErrorException::nestedNot();
210
-                    }
211
-
212
-                    [$argument, $argumentPseudoElement] = $this->parseSimpleSelector($stream, true);
213
-                    $next = $stream->getNext();
214
-
215
-                    if (null !== $argumentPseudoElement) {
216
-                        throw SyntaxErrorException::pseudoElementFound($argumentPseudoElement, 'inside ::not()');
217
-                    }
218
-
219
-                    if (!$next->isDelimiter([')'])) {
220
-                        throw SyntaxErrorException::unexpectedToken('")"', $next);
221
-                    }
222
-
223
-                    $result = new Node\NegationNode($result, $argument);
224
-                } else {
225
-                    $arguments = [];
226
-                    $next = null;
227
-
228
-                    while (true) {
229
-                        $stream->skipWhitespace();
230
-                        $next = $stream->getNext();
231
-
232
-                        if ($next->isIdentifier()
233
-                            || $next->isString()
234
-                            || $next->isNumber()
235
-                            || $next->isDelimiter(['+', '-'])
236
-                        ) {
237
-                            $arguments[] = $next;
238
-                        } elseif ($next->isDelimiter([')'])) {
239
-                            break;
240
-                        } else {
241
-                            throw SyntaxErrorException::unexpectedToken('an argument', $next);
242
-                        }
243
-                    }
244
-
245
-                    if (empty($arguments)) {
246
-                        throw SyntaxErrorException::unexpectedToken('at least one argument', $next);
247
-                    }
248
-
249
-                    $result = new Node\FunctionNode($result, $identifier, $arguments);
250
-                }
251
-            } else {
252
-                throw SyntaxErrorException::unexpectedToken('selector', $peek);
253
-            }
254
-        }
255
-
256
-        if (\count($stream->getUsed()) === $selectorStart) {
257
-            throw SyntaxErrorException::unexpectedToken('selector', $stream->getPeek());
258
-        }
259
-
260
-        return [$result, $pseudoElement];
261
-    }
262
-
263
-    private function parseElementNode(TokenStream $stream): Node\ElementNode
264
-    {
265
-        $peek = $stream->getPeek();
266
-
267
-        if ($peek->isIdentifier() || $peek->isDelimiter(['*'])) {
268
-            if ($peek->isIdentifier()) {
269
-                $namespace = $stream->getNext()->getValue();
270
-            } else {
271
-                $stream->getNext();
272
-                $namespace = null;
273
-            }
274
-
275
-            if ($stream->getPeek()->isDelimiter(['|'])) {
276
-                $stream->getNext();
277
-                $element = $stream->getNextIdentifierOrStar();
278
-            } else {
279
-                $element = $namespace;
280
-                $namespace = null;
281
-            }
282
-        } else {
283
-            $element = $namespace = null;
284
-        }
285
-
286
-        return new Node\ElementNode($namespace, $element);
287
-    }
288
-
289
-    private function parseAttributeNode(Node\NodeInterface $selector, TokenStream $stream): Node\AttributeNode
290
-    {
291
-        $stream->skipWhitespace();
292
-        $attribute = $stream->getNextIdentifierOrStar();
293
-
294
-        if (null === $attribute && !$stream->getPeek()->isDelimiter(['|'])) {
295
-            throw SyntaxErrorException::unexpectedToken('"|"', $stream->getPeek());
296
-        }
297
-
298
-        if ($stream->getPeek()->isDelimiter(['|'])) {
299
-            $stream->getNext();
300
-
301
-            if ($stream->getPeek()->isDelimiter(['='])) {
302
-                $namespace = null;
303
-                $stream->getNext();
304
-                $operator = '|=';
305
-            } else {
306
-                $namespace = $attribute;
307
-                $attribute = $stream->getNextIdentifier();
308
-                $operator = null;
309
-            }
310
-        } else {
311
-            $namespace = $operator = null;
312
-        }
313
-
314
-        if (null === $operator) {
315
-            $stream->skipWhitespace();
316
-            $next = $stream->getNext();
317
-
318
-            if ($next->isDelimiter([']'])) {
319
-                return new Node\AttributeNode($selector, $namespace, $attribute, 'exists', null);
320
-            } elseif ($next->isDelimiter(['='])) {
321
-                $operator = '=';
322
-            } elseif ($next->isDelimiter(['^', '$', '*', '~', '|', '!'])
323
-                && $stream->getPeek()->isDelimiter(['='])
324
-            ) {
325
-                $operator = $next->getValue().'=';
326
-                $stream->getNext();
327
-            } else {
328
-                throw SyntaxErrorException::unexpectedToken('operator', $next);
329
-            }
330
-        }
331
-
332
-        $stream->skipWhitespace();
333
-        $value = $stream->getNext();
334
-
335
-        if ($value->isNumber()) {
336
-            // if the value is a number, it's casted into a string
337
-            $value = new Token(Token::TYPE_STRING, (string) $value->getValue(), $value->getPosition());
338
-        }
339
-
340
-        if (!($value->isIdentifier() || $value->isString())) {
341
-            throw SyntaxErrorException::unexpectedToken('string or identifier', $value);
342
-        }
343
-
344
-        $stream->skipWhitespace();
345
-        $next = $stream->getNext();
346
-
347
-        if (!$next->isDelimiter([']'])) {
348
-            throw SyntaxErrorException::unexpectedToken('"]"', $next);
349
-        }
350
-
351
-        return new Node\AttributeNode($selector, $namespace, $attribute, $operator, $value->getValue());
352
-    }
30
+	private Tokenizer $tokenizer;
31
+
32
+	public function __construct(Tokenizer $tokenizer = null)
33
+	{
34
+		$this->tokenizer = $tokenizer ?? new Tokenizer();
35
+	}
36
+
37
+	/**
38
+	 * {@inheritdoc}
39
+	 */
40
+	public function parse(string $source): array
41
+	{
42
+		$reader = new Reader($source);
43
+		$stream = $this->tokenizer->tokenize($reader);
44
+
45
+		return $this->parseSelectorList($stream);
46
+	}
47
+
48
+	/**
49
+	 * Parses the arguments for ":nth-child()" and friends.
50
+	 *
51
+	 * @param Token[] $tokens
52
+	 *
53
+	 * @throws SyntaxErrorException
54
+	 */
55
+	public static function parseSeries(array $tokens): array
56
+	{
57
+		foreach ($tokens as $token) {
58
+			if ($token->isString()) {
59
+				throw SyntaxErrorException::stringAsFunctionArgument();
60
+			}
61
+		}
62
+
63
+		$joined = trim(implode('', array_map(function (Token $token) {
64
+			return $token->getValue();
65
+		}, $tokens)));
66
+
67
+		$int = function ($string) {
68
+			if (!is_numeric($string)) {
69
+				throw SyntaxErrorException::stringAsFunctionArgument();
70
+			}
71
+
72
+			return (int) $string;
73
+		};
74
+
75
+		switch (true) {
76
+			case 'odd' === $joined:
77
+				return [2, 1];
78
+			case 'even' === $joined:
79
+				return [2, 0];
80
+			case 'n' === $joined:
81
+				return [1, 0];
82
+			case !str_contains($joined, 'n'):
83
+				return [0, $int($joined)];
84
+		}
85
+
86
+		$split = explode('n', $joined);
87
+		$first = $split[0] ?? null;
88
+
89
+		return [
90
+			$first ? ('-' === $first || '+' === $first ? $int($first.'1') : $int($first)) : 1,
91
+			isset($split[1]) && $split[1] ? $int($split[1]) : 0,
92
+		];
93
+	}
94
+
95
+	private function parseSelectorList(TokenStream $stream): array
96
+	{
97
+		$stream->skipWhitespace();
98
+		$selectors = [];
99
+
100
+		while (true) {
101
+			$selectors[] = $this->parserSelectorNode($stream);
102
+
103
+			if ($stream->getPeek()->isDelimiter([','])) {
104
+				$stream->getNext();
105
+				$stream->skipWhitespace();
106
+			} else {
107
+				break;
108
+			}
109
+		}
110
+
111
+		return $selectors;
112
+	}
113
+
114
+	private function parserSelectorNode(TokenStream $stream): Node\SelectorNode
115
+	{
116
+		[$result, $pseudoElement] = $this->parseSimpleSelector($stream);
117
+
118
+		while (true) {
119
+			$stream->skipWhitespace();
120
+			$peek = $stream->getPeek();
121
+
122
+			if ($peek->isFileEnd() || $peek->isDelimiter([','])) {
123
+				break;
124
+			}
125
+
126
+			if (null !== $pseudoElement) {
127
+				throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector');
128
+			}
129
+
130
+			if ($peek->isDelimiter(['+', '>', '~'])) {
131
+				$combinator = $stream->getNext()->getValue();
132
+				$stream->skipWhitespace();
133
+			} else {
134
+				$combinator = ' ';
135
+			}
136
+
137
+			[$nextSelector, $pseudoElement] = $this->parseSimpleSelector($stream);
138
+			$result = new Node\CombinedSelectorNode($result, $combinator, $nextSelector);
139
+		}
140
+
141
+		return new Node\SelectorNode($result, $pseudoElement);
142
+	}
143
+
144
+	/**
145
+	 * Parses next simple node (hash, class, pseudo, negation).
146
+	 *
147
+	 * @throws SyntaxErrorException
148
+	 */
149
+	private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = false): array
150
+	{
151
+		$stream->skipWhitespace();
152
+
153
+		$selectorStart = \count($stream->getUsed());
154
+		$result = $this->parseElementNode($stream);
155
+		$pseudoElement = null;
156
+
157
+		while (true) {
158
+			$peek = $stream->getPeek();
159
+			if ($peek->isWhitespace()
160
+				|| $peek->isFileEnd()
161
+				|| $peek->isDelimiter([',', '+', '>', '~'])
162
+				|| ($insideNegation && $peek->isDelimiter([')']))
163
+			) {
164
+				break;
165
+			}
166
+
167
+			if (null !== $pseudoElement) {
168
+				throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector');
169
+			}
170
+
171
+			if ($peek->isHash()) {
172
+				$result = new Node\HashNode($result, $stream->getNext()->getValue());
173
+			} elseif ($peek->isDelimiter(['.'])) {
174
+				$stream->getNext();
175
+				$result = new Node\ClassNode($result, $stream->getNextIdentifier());
176
+			} elseif ($peek->isDelimiter(['['])) {
177
+				$stream->getNext();
178
+				$result = $this->parseAttributeNode($result, $stream);
179
+			} elseif ($peek->isDelimiter([':'])) {
180
+				$stream->getNext();
181
+
182
+				if ($stream->getPeek()->isDelimiter([':'])) {
183
+					$stream->getNext();
184
+					$pseudoElement = $stream->getNextIdentifier();
185
+
186
+					continue;
187
+				}
188
+
189
+				$identifier = $stream->getNextIdentifier();
190
+				if (\in_array(strtolower($identifier), ['first-line', 'first-letter', 'before', 'after'])) {
191
+					// Special case: CSS 2.1 pseudo-elements can have a single ':'.
192
+					// Any new pseudo-element must have two.
193
+					$pseudoElement = $identifier;
194
+
195
+					continue;
196
+				}
197
+
198
+				if (!$stream->getPeek()->isDelimiter(['('])) {
199
+					$result = new Node\PseudoNode($result, $identifier);
200
+
201
+					continue;
202
+				}
203
+
204
+				$stream->getNext();
205
+				$stream->skipWhitespace();
206
+
207
+				if ('not' === strtolower($identifier)) {
208
+					if ($insideNegation) {
209
+						throw SyntaxErrorException::nestedNot();
210
+					}
211
+
212
+					[$argument, $argumentPseudoElement] = $this->parseSimpleSelector($stream, true);
213
+					$next = $stream->getNext();
214
+
215
+					if (null !== $argumentPseudoElement) {
216
+						throw SyntaxErrorException::pseudoElementFound($argumentPseudoElement, 'inside ::not()');
217
+					}
218
+
219
+					if (!$next->isDelimiter([')'])) {
220
+						throw SyntaxErrorException::unexpectedToken('")"', $next);
221
+					}
222
+
223
+					$result = new Node\NegationNode($result, $argument);
224
+				} else {
225
+					$arguments = [];
226
+					$next = null;
227
+
228
+					while (true) {
229
+						$stream->skipWhitespace();
230
+						$next = $stream->getNext();
231
+
232
+						if ($next->isIdentifier()
233
+							|| $next->isString()
234
+							|| $next->isNumber()
235
+							|| $next->isDelimiter(['+', '-'])
236
+						) {
237
+							$arguments[] = $next;
238
+						} elseif ($next->isDelimiter([')'])) {
239
+							break;
240
+						} else {
241
+							throw SyntaxErrorException::unexpectedToken('an argument', $next);
242
+						}
243
+					}
244
+
245
+					if (empty($arguments)) {
246
+						throw SyntaxErrorException::unexpectedToken('at least one argument', $next);
247
+					}
248
+
249
+					$result = new Node\FunctionNode($result, $identifier, $arguments);
250
+				}
251
+			} else {
252
+				throw SyntaxErrorException::unexpectedToken('selector', $peek);
253
+			}
254
+		}
255
+
256
+		if (\count($stream->getUsed()) === $selectorStart) {
257
+			throw SyntaxErrorException::unexpectedToken('selector', $stream->getPeek());
258
+		}
259
+
260
+		return [$result, $pseudoElement];
261
+	}
262
+
263
+	private function parseElementNode(TokenStream $stream): Node\ElementNode
264
+	{
265
+		$peek = $stream->getPeek();
266
+
267
+		if ($peek->isIdentifier() || $peek->isDelimiter(['*'])) {
268
+			if ($peek->isIdentifier()) {
269
+				$namespace = $stream->getNext()->getValue();
270
+			} else {
271
+				$stream->getNext();
272
+				$namespace = null;
273
+			}
274
+
275
+			if ($stream->getPeek()->isDelimiter(['|'])) {
276
+				$stream->getNext();
277
+				$element = $stream->getNextIdentifierOrStar();
278
+			} else {
279
+				$element = $namespace;
280
+				$namespace = null;
281
+			}
282
+		} else {
283
+			$element = $namespace = null;
284
+		}
285
+
286
+		return new Node\ElementNode($namespace, $element);
287
+	}
288
+
289
+	private function parseAttributeNode(Node\NodeInterface $selector, TokenStream $stream): Node\AttributeNode
290
+	{
291
+		$stream->skipWhitespace();
292
+		$attribute = $stream->getNextIdentifierOrStar();
293
+
294
+		if (null === $attribute && !$stream->getPeek()->isDelimiter(['|'])) {
295
+			throw SyntaxErrorException::unexpectedToken('"|"', $stream->getPeek());
296
+		}
297
+
298
+		if ($stream->getPeek()->isDelimiter(['|'])) {
299
+			$stream->getNext();
300
+
301
+			if ($stream->getPeek()->isDelimiter(['='])) {
302
+				$namespace = null;
303
+				$stream->getNext();
304
+				$operator = '|=';
305
+			} else {
306
+				$namespace = $attribute;
307
+				$attribute = $stream->getNextIdentifier();
308
+				$operator = null;
309
+			}
310
+		} else {
311
+			$namespace = $operator = null;
312
+		}
313
+
314
+		if (null === $operator) {
315
+			$stream->skipWhitespace();
316
+			$next = $stream->getNext();
317
+
318
+			if ($next->isDelimiter([']'])) {
319
+				return new Node\AttributeNode($selector, $namespace, $attribute, 'exists', null);
320
+			} elseif ($next->isDelimiter(['='])) {
321
+				$operator = '=';
322
+			} elseif ($next->isDelimiter(['^', '$', '*', '~', '|', '!'])
323
+				&& $stream->getPeek()->isDelimiter(['='])
324
+			) {
325
+				$operator = $next->getValue().'=';
326
+				$stream->getNext();
327
+			} else {
328
+				throw SyntaxErrorException::unexpectedToken('operator', $next);
329
+			}
330
+		}
331
+
332
+		$stream->skipWhitespace();
333
+		$value = $stream->getNext();
334
+
335
+		if ($value->isNumber()) {
336
+			// if the value is a number, it's casted into a string
337
+			$value = new Token(Token::TYPE_STRING, (string) $value->getValue(), $value->getPosition());
338
+		}
339
+
340
+		if (!($value->isIdentifier() || $value->isString())) {
341
+			throw SyntaxErrorException::unexpectedToken('string or identifier', $value);
342
+		}
343
+
344
+		$stream->skipWhitespace();
345
+		$next = $stream->getNext();
346
+
347
+		if (!$next->isDelimiter([']'])) {
348
+			throw SyntaxErrorException::unexpectedToken('"]"', $next);
349
+		}
350
+
351
+		return new Node\AttributeNode($selector, $namespace, $attribute, $operator, $value->getValue());
352
+	}
353 353
 }
Please login to merge, or discard this patch.
vendor/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php 1 patch
Indentation   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -23,43 +23,43 @@
 block discarded – undo
23 23
  */
24 24
 class TokenizerEscaping
25 25
 {
26
-    private TokenizerPatterns $patterns;
26
+	private TokenizerPatterns $patterns;
27 27
 
28
-    public function __construct(TokenizerPatterns $patterns)
29
-    {
30
-        $this->patterns = $patterns;
31
-    }
28
+	public function __construct(TokenizerPatterns $patterns)
29
+	{
30
+		$this->patterns = $patterns;
31
+	}
32 32
 
33
-    public function escapeUnicode(string $value): string
34
-    {
35
-        $value = $this->replaceUnicodeSequences($value);
33
+	public function escapeUnicode(string $value): string
34
+	{
35
+		$value = $this->replaceUnicodeSequences($value);
36 36
 
37
-        return preg_replace($this->patterns->getSimpleEscapePattern(), '$1', $value);
38
-    }
37
+		return preg_replace($this->patterns->getSimpleEscapePattern(), '$1', $value);
38
+	}
39 39
 
40
-    public function escapeUnicodeAndNewLine(string $value): string
41
-    {
42
-        $value = preg_replace($this->patterns->getNewLineEscapePattern(), '', $value);
40
+	public function escapeUnicodeAndNewLine(string $value): string
41
+	{
42
+		$value = preg_replace($this->patterns->getNewLineEscapePattern(), '', $value);
43 43
 
44
-        return $this->escapeUnicode($value);
45
-    }
44
+		return $this->escapeUnicode($value);
45
+	}
46 46
 
47
-    private function replaceUnicodeSequences(string $value): string
48
-    {
49
-        return preg_replace_callback($this->patterns->getUnicodeEscapePattern(), function ($match) {
50
-            $c = hexdec($match[1]);
47
+	private function replaceUnicodeSequences(string $value): string
48
+	{
49
+		return preg_replace_callback($this->patterns->getUnicodeEscapePattern(), function ($match) {
50
+			$c = hexdec($match[1]);
51 51
 
52
-            if (0x80 > $c %= 0x200000) {
53
-                return \chr($c);
54
-            }
55
-            if (0x800 > $c) {
56
-                return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F);
57
-            }
58
-            if (0x10000 > $c) {
59
-                return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
60
-            }
52
+			if (0x80 > $c %= 0x200000) {
53
+				return \chr($c);
54
+			}
55
+			if (0x800 > $c) {
56
+				return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F);
57
+			}
58
+			if (0x10000 > $c) {
59
+				return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
60
+			}
61 61
 
62
-            return '';
63
-        }, $value);
64
-    }
62
+			return '';
63
+		}, $value);
64
+	}
65 65
 }
Please login to merge, or discard this patch.
vendor/symfony/css-selector/XPath/Extension/CombinationExtension.php 1 patch
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -25,47 +25,47 @@
 block discarded – undo
25 25
  */
26 26
 class CombinationExtension extends AbstractExtension
27 27
 {
28
-    /**
29
-     * {@inheritdoc}
30
-     */
31
-    public function getCombinationTranslators(): array
32
-    {
33
-        return [
34
-            ' ' => $this->translateDescendant(...),
35
-            '>' => $this->translateChild(...),
36
-            '+' => $this->translateDirectAdjacent(...),
37
-            '~' => $this->translateIndirectAdjacent(...),
38
-        ];
39
-    }
28
+	/**
29
+	 * {@inheritdoc}
30
+	 */
31
+	public function getCombinationTranslators(): array
32
+	{
33
+		return [
34
+			' ' => $this->translateDescendant(...),
35
+			'>' => $this->translateChild(...),
36
+			'+' => $this->translateDirectAdjacent(...),
37
+			'~' => $this->translateIndirectAdjacent(...),
38
+		];
39
+	}
40 40
 
41
-    public function translateDescendant(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
42
-    {
43
-        return $xpath->join('/descendant-or-self::*/', $combinedXpath);
44
-    }
41
+	public function translateDescendant(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
42
+	{
43
+		return $xpath->join('/descendant-or-self::*/', $combinedXpath);
44
+	}
45 45
 
46
-    public function translateChild(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
47
-    {
48
-        return $xpath->join('/', $combinedXpath);
49
-    }
46
+	public function translateChild(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
47
+	{
48
+		return $xpath->join('/', $combinedXpath);
49
+	}
50 50
 
51
-    public function translateDirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
52
-    {
53
-        return $xpath
54
-            ->join('/following-sibling::', $combinedXpath)
55
-            ->addNameTest()
56
-            ->addCondition('position() = 1');
57
-    }
51
+	public function translateDirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
52
+	{
53
+		return $xpath
54
+			->join('/following-sibling::', $combinedXpath)
55
+			->addNameTest()
56
+			->addCondition('position() = 1');
57
+	}
58 58
 
59
-    public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
60
-    {
61
-        return $xpath->join('/following-sibling::', $combinedXpath);
62
-    }
59
+	public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath): XPathExpr
60
+	{
61
+		return $xpath->join('/following-sibling::', $combinedXpath);
62
+	}
63 63
 
64
-    /**
65
-     * {@inheritdoc}
66
-     */
67
-    public function getName(): string
68
-    {
69
-        return 'combination';
70
-    }
64
+	/**
65
+	 * {@inheritdoc}
66
+	 */
67
+	public function getName(): string
68
+	{
69
+		return 'combination';
70
+	}
71 71
 }
Please login to merge, or discard this patch.
vendor/symfony/css-selector/XPath/Extension/FunctionExtension.php 1 patch
Indentation   +138 added lines, -138 removed lines patch added patch discarded remove patch
@@ -30,142 +30,142 @@
 block discarded – undo
30 30
  */
31 31
 class FunctionExtension extends AbstractExtension
32 32
 {
33
-    /**
34
-     * {@inheritdoc}
35
-     */
36
-    public function getFunctionTranslators(): array
37
-    {
38
-        return [
39
-            'nth-child' => $this->translateNthChild(...),
40
-            'nth-last-child' => $this->translateNthLastChild(...),
41
-            'nth-of-type' => $this->translateNthOfType(...),
42
-            'nth-last-of-type' => $this->translateNthLastOfType(...),
43
-            'contains' => $this->translateContains(...),
44
-            'lang' => $this->translateLang(...),
45
-        ];
46
-    }
47
-
48
-    /**
49
-     * @throws ExpressionErrorException
50
-     */
51
-    public function translateNthChild(XPathExpr $xpath, FunctionNode $function, bool $last = false, bool $addNameTest = true): XPathExpr
52
-    {
53
-        try {
54
-            [$a, $b] = Parser::parseSeries($function->getArguments());
55
-        } catch (SyntaxErrorException $e) {
56
-            throw new ExpressionErrorException(sprintf('Invalid series: "%s".', implode('", "', $function->getArguments())), 0, $e);
57
-        }
58
-
59
-        $xpath->addStarPrefix();
60
-        if ($addNameTest) {
61
-            $xpath->addNameTest();
62
-        }
63
-
64
-        if (0 === $a) {
65
-            return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b));
66
-        }
67
-
68
-        if ($a < 0) {
69
-            if ($b < 1) {
70
-                return $xpath->addCondition('false()');
71
-            }
72
-
73
-            $sign = '<=';
74
-        } else {
75
-            $sign = '>=';
76
-        }
77
-
78
-        $expr = 'position()';
79
-
80
-        if ($last) {
81
-            $expr = 'last() - '.$expr;
82
-            --$b;
83
-        }
84
-
85
-        if (0 !== $b) {
86
-            $expr .= ' - '.$b;
87
-        }
88
-
89
-        $conditions = [sprintf('%s %s 0', $expr, $sign)];
90
-
91
-        if (1 !== $a && -1 !== $a) {
92
-            $conditions[] = sprintf('(%s) mod %d = 0', $expr, $a);
93
-        }
94
-
95
-        return $xpath->addCondition(implode(' and ', $conditions));
96
-
97
-        // todo: handle an+b, odd, even
98
-        // an+b means every-a, plus b, e.g., 2n+1 means odd
99
-        // 0n+b means b
100
-        // n+0 means a=1, i.e., all elements
101
-        // an means every a elements, i.e., 2n means even
102
-        // -n means -1n
103
-        // -1n+6 means elements 6 and previous
104
-    }
105
-
106
-    public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function): XPathExpr
107
-    {
108
-        return $this->translateNthChild($xpath, $function, true);
109
-    }
110
-
111
-    public function translateNthOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
112
-    {
113
-        return $this->translateNthChild($xpath, $function, false, false);
114
-    }
115
-
116
-    /**
117
-     * @throws ExpressionErrorException
118
-     */
119
-    public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
120
-    {
121
-        if ('*' === $xpath->getElement()) {
122
-            throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.');
123
-        }
124
-
125
-        return $this->translateNthChild($xpath, $function, true, false);
126
-    }
127
-
128
-    /**
129
-     * @throws ExpressionErrorException
130
-     */
131
-    public function translateContains(XPathExpr $xpath, FunctionNode $function): XPathExpr
132
-    {
133
-        $arguments = $function->getArguments();
134
-        foreach ($arguments as $token) {
135
-            if (!($token->isString() || $token->isIdentifier())) {
136
-                throw new ExpressionErrorException('Expected a single string or identifier for :contains(), got '.implode(', ', $arguments));
137
-            }
138
-        }
139
-
140
-        return $xpath->addCondition(sprintf(
141
-            'contains(string(.), %s)',
142
-            Translator::getXpathLiteral($arguments[0]->getValue())
143
-        ));
144
-    }
145
-
146
-    /**
147
-     * @throws ExpressionErrorException
148
-     */
149
-    public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr
150
-    {
151
-        $arguments = $function->getArguments();
152
-        foreach ($arguments as $token) {
153
-            if (!($token->isString() || $token->isIdentifier())) {
154
-                throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));
155
-            }
156
-        }
157
-
158
-        return $xpath->addCondition(sprintf(
159
-            'lang(%s)',
160
-            Translator::getXpathLiteral($arguments[0]->getValue())
161
-        ));
162
-    }
163
-
164
-    /**
165
-     * {@inheritdoc}
166
-     */
167
-    public function getName(): string
168
-    {
169
-        return 'function';
170
-    }
33
+	/**
34
+	 * {@inheritdoc}
35
+	 */
36
+	public function getFunctionTranslators(): array
37
+	{
38
+		return [
39
+			'nth-child' => $this->translateNthChild(...),
40
+			'nth-last-child' => $this->translateNthLastChild(...),
41
+			'nth-of-type' => $this->translateNthOfType(...),
42
+			'nth-last-of-type' => $this->translateNthLastOfType(...),
43
+			'contains' => $this->translateContains(...),
44
+			'lang' => $this->translateLang(...),
45
+		];
46
+	}
47
+
48
+	/**
49
+	 * @throws ExpressionErrorException
50
+	 */
51
+	public function translateNthChild(XPathExpr $xpath, FunctionNode $function, bool $last = false, bool $addNameTest = true): XPathExpr
52
+	{
53
+		try {
54
+			[$a, $b] = Parser::parseSeries($function->getArguments());
55
+		} catch (SyntaxErrorException $e) {
56
+			throw new ExpressionErrorException(sprintf('Invalid series: "%s".', implode('", "', $function->getArguments())), 0, $e);
57
+		}
58
+
59
+		$xpath->addStarPrefix();
60
+		if ($addNameTest) {
61
+			$xpath->addNameTest();
62
+		}
63
+
64
+		if (0 === $a) {
65
+			return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b));
66
+		}
67
+
68
+		if ($a < 0) {
69
+			if ($b < 1) {
70
+				return $xpath->addCondition('false()');
71
+			}
72
+
73
+			$sign = '<=';
74
+		} else {
75
+			$sign = '>=';
76
+		}
77
+
78
+		$expr = 'position()';
79
+
80
+		if ($last) {
81
+			$expr = 'last() - '.$expr;
82
+			--$b;
83
+		}
84
+
85
+		if (0 !== $b) {
86
+			$expr .= ' - '.$b;
87
+		}
88
+
89
+		$conditions = [sprintf('%s %s 0', $expr, $sign)];
90
+
91
+		if (1 !== $a && -1 !== $a) {
92
+			$conditions[] = sprintf('(%s) mod %d = 0', $expr, $a);
93
+		}
94
+
95
+		return $xpath->addCondition(implode(' and ', $conditions));
96
+
97
+		// todo: handle an+b, odd, even
98
+		// an+b means every-a, plus b, e.g., 2n+1 means odd
99
+		// 0n+b means b
100
+		// n+0 means a=1, i.e., all elements
101
+		// an means every a elements, i.e., 2n means even
102
+		// -n means -1n
103
+		// -1n+6 means elements 6 and previous
104
+	}
105
+
106
+	public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function): XPathExpr
107
+	{
108
+		return $this->translateNthChild($xpath, $function, true);
109
+	}
110
+
111
+	public function translateNthOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
112
+	{
113
+		return $this->translateNthChild($xpath, $function, false, false);
114
+	}
115
+
116
+	/**
117
+	 * @throws ExpressionErrorException
118
+	 */
119
+	public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function): XPathExpr
120
+	{
121
+		if ('*' === $xpath->getElement()) {
122
+			throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.');
123
+		}
124
+
125
+		return $this->translateNthChild($xpath, $function, true, false);
126
+	}
127
+
128
+	/**
129
+	 * @throws ExpressionErrorException
130
+	 */
131
+	public function translateContains(XPathExpr $xpath, FunctionNode $function): XPathExpr
132
+	{
133
+		$arguments = $function->getArguments();
134
+		foreach ($arguments as $token) {
135
+			if (!($token->isString() || $token->isIdentifier())) {
136
+				throw new ExpressionErrorException('Expected a single string or identifier for :contains(), got '.implode(', ', $arguments));
137
+			}
138
+		}
139
+
140
+		return $xpath->addCondition(sprintf(
141
+			'contains(string(.), %s)',
142
+			Translator::getXpathLiteral($arguments[0]->getValue())
143
+		));
144
+	}
145
+
146
+	/**
147
+	 * @throws ExpressionErrorException
148
+	 */
149
+	public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr
150
+	{
151
+		$arguments = $function->getArguments();
152
+		foreach ($arguments as $token) {
153
+			if (!($token->isString() || $token->isIdentifier())) {
154
+				throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));
155
+			}
156
+		}
157
+
158
+		return $xpath->addCondition(sprintf(
159
+			'lang(%s)',
160
+			Translator::getXpathLiteral($arguments[0]->getValue())
161
+		));
162
+	}
163
+
164
+	/**
165
+	 * {@inheritdoc}
166
+	 */
167
+	public function getName(): string
168
+	{
169
+		return 'function';
170
+	}
171 171
 }
Please login to merge, or discard this patch.
vendor/symfony/css-selector/XPath/Extension/HtmlExtension.php 1 patch
Indentation   +156 added lines, -156 removed lines patch added patch discarded remove patch
@@ -28,160 +28,160 @@
 block discarded – undo
28 28
  */
29 29
 class HtmlExtension extends AbstractExtension
30 30
 {
31
-    public function __construct(Translator $translator)
32
-    {
33
-        $translator
34
-            ->getExtension('node')
35
-            ->setFlag(NodeExtension::ELEMENT_NAME_IN_LOWER_CASE, true)
36
-            ->setFlag(NodeExtension::ATTRIBUTE_NAME_IN_LOWER_CASE, true);
37
-    }
38
-
39
-    /**
40
-     * {@inheritdoc}
41
-     */
42
-    public function getPseudoClassTranslators(): array
43
-    {
44
-        return [
45
-            'checked' => $this->translateChecked(...),
46
-            'link' => $this->translateLink(...),
47
-            'disabled' => $this->translateDisabled(...),
48
-            'enabled' => $this->translateEnabled(...),
49
-            'selected' => $this->translateSelected(...),
50
-            'invalid' => $this->translateInvalid(...),
51
-            'hover' => $this->translateHover(...),
52
-            'visited' => $this->translateVisited(...),
53
-        ];
54
-    }
55
-
56
-    /**
57
-     * {@inheritdoc}
58
-     */
59
-    public function getFunctionTranslators(): array
60
-    {
61
-        return [
62
-            'lang' => $this->translateLang(...),
63
-        ];
64
-    }
65
-
66
-    public function translateChecked(XPathExpr $xpath): XPathExpr
67
-    {
68
-        return $xpath->addCondition(
69
-            '(@checked '
70
-            ."and (name(.) = 'input' or name(.) = 'command')"
71
-            ."and (@type = 'checkbox' or @type = 'radio'))"
72
-        );
73
-    }
74
-
75
-    public function translateLink(XPathExpr $xpath): XPathExpr
76
-    {
77
-        return $xpath->addCondition("@href and (name(.) = 'a' or name(.) = 'link' or name(.) = 'area')");
78
-    }
79
-
80
-    public function translateDisabled(XPathExpr $xpath): XPathExpr
81
-    {
82
-        return $xpath->addCondition(
83
-            '('
84
-                .'@disabled and'
85
-                .'('
86
-                    ."(name(.) = 'input' and @type != 'hidden')"
87
-                    ." or name(.) = 'button'"
88
-                    ." or name(.) = 'select'"
89
-                    ." or name(.) = 'textarea'"
90
-                    ." or name(.) = 'command'"
91
-                    ." or name(.) = 'fieldset'"
92
-                    ." or name(.) = 'optgroup'"
93
-                    ." or name(.) = 'option'"
94
-                .')'
95
-            .') or ('
96
-                ."(name(.) = 'input' and @type != 'hidden')"
97
-                ." or name(.) = 'button'"
98
-                ." or name(.) = 'select'"
99
-                ." or name(.) = 'textarea'"
100
-            .')'
101
-            .' and ancestor::fieldset[@disabled]'
102
-        );
103
-        // todo: in the second half, add "and is not a descendant of that fieldset element's first legend element child, if any."
104
-    }
105
-
106
-    public function translateEnabled(XPathExpr $xpath): XPathExpr
107
-    {
108
-        return $xpath->addCondition(
109
-            '('
110
-                .'@href and ('
111
-                    ."name(.) = 'a'"
112
-                    ." or name(.) = 'link'"
113
-                    ." or name(.) = 'area'"
114
-                .')'
115
-            .') or ('
116
-                .'('
117
-                    ."name(.) = 'command'"
118
-                    ." or name(.) = 'fieldset'"
119
-                    ." or name(.) = 'optgroup'"
120
-                .')'
121
-                .' and not(@disabled)'
122
-            .') or ('
123
-                .'('
124
-                    ."(name(.) = 'input' and @type != 'hidden')"
125
-                    ." or name(.) = 'button'"
126
-                    ." or name(.) = 'select'"
127
-                    ." or name(.) = 'textarea'"
128
-                    ." or name(.) = 'keygen'"
129
-                .')'
130
-                .' and not (@disabled or ancestor::fieldset[@disabled])'
131
-            .') or ('
132
-                ."name(.) = 'option' and not("
133
-                    .'@disabled or ancestor::optgroup[@disabled]'
134
-                .')'
135
-            .')'
136
-        );
137
-    }
138
-
139
-    /**
140
-     * @throws ExpressionErrorException
141
-     */
142
-    public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr
143
-    {
144
-        $arguments = $function->getArguments();
145
-        foreach ($arguments as $token) {
146
-            if (!($token->isString() || $token->isIdentifier())) {
147
-                throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));
148
-            }
149
-        }
150
-
151
-        return $xpath->addCondition(sprintf(
152
-            'ancestor-or-self::*[@lang][1][starts-with(concat('
153
-            ."translate(@%s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '-')"
154
-            .', %s)]',
155
-            'lang',
156
-            Translator::getXpathLiteral(strtolower($arguments[0]->getValue()).'-')
157
-        ));
158
-    }
159
-
160
-    public function translateSelected(XPathExpr $xpath): XPathExpr
161
-    {
162
-        return $xpath->addCondition("(@selected and name(.) = 'option')");
163
-    }
164
-
165
-    public function translateInvalid(XPathExpr $xpath): XPathExpr
166
-    {
167
-        return $xpath->addCondition('0');
168
-    }
169
-
170
-    public function translateHover(XPathExpr $xpath): XPathExpr
171
-    {
172
-        return $xpath->addCondition('0');
173
-    }
174
-
175
-    public function translateVisited(XPathExpr $xpath): XPathExpr
176
-    {
177
-        return $xpath->addCondition('0');
178
-    }
179
-
180
-    /**
181
-     * {@inheritdoc}
182
-     */
183
-    public function getName(): string
184
-    {
185
-        return 'html';
186
-    }
31
+	public function __construct(Translator $translator)
32
+	{
33
+		$translator
34
+			->getExtension('node')
35
+			->setFlag(NodeExtension::ELEMENT_NAME_IN_LOWER_CASE, true)
36
+			->setFlag(NodeExtension::ATTRIBUTE_NAME_IN_LOWER_CASE, true);
37
+	}
38
+
39
+	/**
40
+	 * {@inheritdoc}
41
+	 */
42
+	public function getPseudoClassTranslators(): array
43
+	{
44
+		return [
45
+			'checked' => $this->translateChecked(...),
46
+			'link' => $this->translateLink(...),
47
+			'disabled' => $this->translateDisabled(...),
48
+			'enabled' => $this->translateEnabled(...),
49
+			'selected' => $this->translateSelected(...),
50
+			'invalid' => $this->translateInvalid(...),
51
+			'hover' => $this->translateHover(...),
52
+			'visited' => $this->translateVisited(...),
53
+		];
54
+	}
55
+
56
+	/**
57
+	 * {@inheritdoc}
58
+	 */
59
+	public function getFunctionTranslators(): array
60
+	{
61
+		return [
62
+			'lang' => $this->translateLang(...),
63
+		];
64
+	}
65
+
66
+	public function translateChecked(XPathExpr $xpath): XPathExpr
67
+	{
68
+		return $xpath->addCondition(
69
+			'(@checked '
70
+			."and (name(.) = 'input' or name(.) = 'command')"
71
+			."and (@type = 'checkbox' or @type = 'radio'))"
72
+		);
73
+	}
74
+
75
+	public function translateLink(XPathExpr $xpath): XPathExpr
76
+	{
77
+		return $xpath->addCondition("@href and (name(.) = 'a' or name(.) = 'link' or name(.) = 'area')");
78
+	}
79
+
80
+	public function translateDisabled(XPathExpr $xpath): XPathExpr
81
+	{
82
+		return $xpath->addCondition(
83
+			'('
84
+				.'@disabled and'
85
+				.'('
86
+					."(name(.) = 'input' and @type != 'hidden')"
87
+					." or name(.) = 'button'"
88
+					." or name(.) = 'select'"
89
+					." or name(.) = 'textarea'"
90
+					." or name(.) = 'command'"
91
+					." or name(.) = 'fieldset'"
92
+					." or name(.) = 'optgroup'"
93
+					." or name(.) = 'option'"
94
+				.')'
95
+			.') or ('
96
+				."(name(.) = 'input' and @type != 'hidden')"
97
+				." or name(.) = 'button'"
98
+				." or name(.) = 'select'"
99
+				." or name(.) = 'textarea'"
100
+			.')'
101
+			.' and ancestor::fieldset[@disabled]'
102
+		);
103
+		// todo: in the second half, add "and is not a descendant of that fieldset element's first legend element child, if any."
104
+	}
105
+
106
+	public function translateEnabled(XPathExpr $xpath): XPathExpr
107
+	{
108
+		return $xpath->addCondition(
109
+			'('
110
+				.'@href and ('
111
+					."name(.) = 'a'"
112
+					." or name(.) = 'link'"
113
+					." or name(.) = 'area'"
114
+				.')'
115
+			.') or ('
116
+				.'('
117
+					."name(.) = 'command'"
118
+					." or name(.) = 'fieldset'"
119
+					." or name(.) = 'optgroup'"
120
+				.')'
121
+				.' and not(@disabled)'
122
+			.') or ('
123
+				.'('
124
+					."(name(.) = 'input' and @type != 'hidden')"
125
+					." or name(.) = 'button'"
126
+					." or name(.) = 'select'"
127
+					." or name(.) = 'textarea'"
128
+					." or name(.) = 'keygen'"
129
+				.')'
130
+				.' and not (@disabled or ancestor::fieldset[@disabled])'
131
+			.') or ('
132
+				."name(.) = 'option' and not("
133
+					.'@disabled or ancestor::optgroup[@disabled]'
134
+				.')'
135
+			.')'
136
+		);
137
+	}
138
+
139
+	/**
140
+	 * @throws ExpressionErrorException
141
+	 */
142
+	public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr
143
+	{
144
+		$arguments = $function->getArguments();
145
+		foreach ($arguments as $token) {
146
+			if (!($token->isString() || $token->isIdentifier())) {
147
+				throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));
148
+			}
149
+		}
150
+
151
+		return $xpath->addCondition(sprintf(
152
+			'ancestor-or-self::*[@lang][1][starts-with(concat('
153
+			."translate(@%s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '-')"
154
+			.', %s)]',
155
+			'lang',
156
+			Translator::getXpathLiteral(strtolower($arguments[0]->getValue()).'-')
157
+		));
158
+	}
159
+
160
+	public function translateSelected(XPathExpr $xpath): XPathExpr
161
+	{
162
+		return $xpath->addCondition("(@selected and name(.) = 'option')");
163
+	}
164
+
165
+	public function translateInvalid(XPathExpr $xpath): XPathExpr
166
+	{
167
+		return $xpath->addCondition('0');
168
+	}
169
+
170
+	public function translateHover(XPathExpr $xpath): XPathExpr
171
+	{
172
+		return $xpath->addCondition('0');
173
+	}
174
+
175
+	public function translateVisited(XPathExpr $xpath): XPathExpr
176
+	{
177
+		return $xpath->addCondition('0');
178
+	}
179
+
180
+	/**
181
+	 * {@inheritdoc}
182
+	 */
183
+	public function getName(): string
184
+	{
185
+		return 'html';
186
+	}
187 187
 }
Please login to merge, or discard this patch.
vendor/symfony/css-selector/XPath/Extension/NodeExtension.php 1 patch
Indentation   +167 added lines, -167 removed lines patch added patch discarded remove patch
@@ -27,171 +27,171 @@
 block discarded – undo
27 27
  */
28 28
 class NodeExtension extends AbstractExtension
29 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
-    /**
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
-    }
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
+	/**
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 197
 }
Please login to merge, or discard this patch.
vendor/symfony/css-selector/XPath/Extension/PseudoClassExtension.php 1 patch
Indentation   +93 added lines, -93 removed lines patch added patch discarded remove patch
@@ -26,97 +26,97 @@
 block discarded – undo
26 26
  */
27 27
 class PseudoClassExtension extends AbstractExtension
28 28
 {
29
-    /**
30
-     * {@inheritdoc}
31
-     */
32
-    public function getPseudoClassTranslators(): array
33
-    {
34
-        return [
35
-            'root' => $this->translateRoot(...),
36
-            'first-child' => $this->translateFirstChild(...),
37
-            'last-child' => $this->translateLastChild(...),
38
-            'first-of-type' => $this->translateFirstOfType(...),
39
-            'last-of-type' => $this->translateLastOfType(...),
40
-            'only-child' => $this->translateOnlyChild(...),
41
-            'only-of-type' => $this->translateOnlyOfType(...),
42
-            'empty' => $this->translateEmpty(...),
43
-        ];
44
-    }
45
-
46
-    public function translateRoot(XPathExpr $xpath): XPathExpr
47
-    {
48
-        return $xpath->addCondition('not(parent::*)');
49
-    }
50
-
51
-    public function translateFirstChild(XPathExpr $xpath): XPathExpr
52
-    {
53
-        return $xpath
54
-            ->addStarPrefix()
55
-            ->addNameTest()
56
-            ->addCondition('position() = 1');
57
-    }
58
-
59
-    public function translateLastChild(XPathExpr $xpath): XPathExpr
60
-    {
61
-        return $xpath
62
-            ->addStarPrefix()
63
-            ->addNameTest()
64
-            ->addCondition('position() = last()');
65
-    }
66
-
67
-    /**
68
-     * @throws ExpressionErrorException
69
-     */
70
-    public function translateFirstOfType(XPathExpr $xpath): XPathExpr
71
-    {
72
-        if ('*' === $xpath->getElement()) {
73
-            throw new ExpressionErrorException('"*:first-of-type" is not implemented.');
74
-        }
75
-
76
-        return $xpath
77
-            ->addStarPrefix()
78
-            ->addCondition('position() = 1');
79
-    }
80
-
81
-    /**
82
-     * @throws ExpressionErrorException
83
-     */
84
-    public function translateLastOfType(XPathExpr $xpath): XPathExpr
85
-    {
86
-        if ('*' === $xpath->getElement()) {
87
-            throw new ExpressionErrorException('"*:last-of-type" is not implemented.');
88
-        }
89
-
90
-        return $xpath
91
-            ->addStarPrefix()
92
-            ->addCondition('position() = last()');
93
-    }
94
-
95
-    public function translateOnlyChild(XPathExpr $xpath): XPathExpr
96
-    {
97
-        return $xpath
98
-            ->addStarPrefix()
99
-            ->addNameTest()
100
-            ->addCondition('last() = 1');
101
-    }
102
-
103
-    public function translateOnlyOfType(XPathExpr $xpath): XPathExpr
104
-    {
105
-        $element = $xpath->getElement();
106
-
107
-        return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element));
108
-    }
109
-
110
-    public function translateEmpty(XPathExpr $xpath): XPathExpr
111
-    {
112
-        return $xpath->addCondition('not(*) and not(string-length())');
113
-    }
114
-
115
-    /**
116
-     * {@inheritdoc}
117
-     */
118
-    public function getName(): string
119
-    {
120
-        return 'pseudo-class';
121
-    }
29
+	/**
30
+	 * {@inheritdoc}
31
+	 */
32
+	public function getPseudoClassTranslators(): array
33
+	{
34
+		return [
35
+			'root' => $this->translateRoot(...),
36
+			'first-child' => $this->translateFirstChild(...),
37
+			'last-child' => $this->translateLastChild(...),
38
+			'first-of-type' => $this->translateFirstOfType(...),
39
+			'last-of-type' => $this->translateLastOfType(...),
40
+			'only-child' => $this->translateOnlyChild(...),
41
+			'only-of-type' => $this->translateOnlyOfType(...),
42
+			'empty' => $this->translateEmpty(...),
43
+		];
44
+	}
45
+
46
+	public function translateRoot(XPathExpr $xpath): XPathExpr
47
+	{
48
+		return $xpath->addCondition('not(parent::*)');
49
+	}
50
+
51
+	public function translateFirstChild(XPathExpr $xpath): XPathExpr
52
+	{
53
+		return $xpath
54
+			->addStarPrefix()
55
+			->addNameTest()
56
+			->addCondition('position() = 1');
57
+	}
58
+
59
+	public function translateLastChild(XPathExpr $xpath): XPathExpr
60
+	{
61
+		return $xpath
62
+			->addStarPrefix()
63
+			->addNameTest()
64
+			->addCondition('position() = last()');
65
+	}
66
+
67
+	/**
68
+	 * @throws ExpressionErrorException
69
+	 */
70
+	public function translateFirstOfType(XPathExpr $xpath): XPathExpr
71
+	{
72
+		if ('*' === $xpath->getElement()) {
73
+			throw new ExpressionErrorException('"*:first-of-type" is not implemented.');
74
+		}
75
+
76
+		return $xpath
77
+			->addStarPrefix()
78
+			->addCondition('position() = 1');
79
+	}
80
+
81
+	/**
82
+	 * @throws ExpressionErrorException
83
+	 */
84
+	public function translateLastOfType(XPathExpr $xpath): XPathExpr
85
+	{
86
+		if ('*' === $xpath->getElement()) {
87
+			throw new ExpressionErrorException('"*:last-of-type" is not implemented.');
88
+		}
89
+
90
+		return $xpath
91
+			->addStarPrefix()
92
+			->addCondition('position() = last()');
93
+	}
94
+
95
+	public function translateOnlyChild(XPathExpr $xpath): XPathExpr
96
+	{
97
+		return $xpath
98
+			->addStarPrefix()
99
+			->addNameTest()
100
+			->addCondition('last() = 1');
101
+	}
102
+
103
+	public function translateOnlyOfType(XPathExpr $xpath): XPathExpr
104
+	{
105
+		$element = $xpath->getElement();
106
+
107
+		return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element));
108
+	}
109
+
110
+	public function translateEmpty(XPathExpr $xpath): XPathExpr
111
+	{
112
+		return $xpath->addCondition('not(*) and not(string-length())');
113
+	}
114
+
115
+	/**
116
+	 * {@inheritdoc}
117
+	 */
118
+	public function getName(): string
119
+	{
120
+		return 'pseudo-class';
121
+	}
122 122
 }
Please login to merge, or discard this patch.
vendor/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php 1 patch
Indentation   +81 added lines, -81 removed lines patch added patch discarded remove patch
@@ -26,94 +26,94 @@
 block discarded – undo
26 26
  */
27 27
 class AttributeMatchingExtension extends AbstractExtension
28 28
 {
29
-    /**
30
-     * {@inheritdoc}
31
-     */
32
-    public function getAttributeMatchingTranslators(): array
33
-    {
34
-        return [
35
-            'exists' => $this->translateExists(...),
36
-            '=' => $this->translateEquals(...),
37
-            '~=' => $this->translateIncludes(...),
38
-            '|=' => $this->translateDashMatch(...),
39
-            '^=' => $this->translatePrefixMatch(...),
40
-            '$=' => $this->translateSuffixMatch(...),
41
-            '*=' => $this->translateSubstringMatch(...),
42
-            '!=' => $this->translateDifferent(...),
43
-        ];
44
-    }
29
+	/**
30
+	 * {@inheritdoc}
31
+	 */
32
+	public function getAttributeMatchingTranslators(): array
33
+	{
34
+		return [
35
+			'exists' => $this->translateExists(...),
36
+			'=' => $this->translateEquals(...),
37
+			'~=' => $this->translateIncludes(...),
38
+			'|=' => $this->translateDashMatch(...),
39
+			'^=' => $this->translatePrefixMatch(...),
40
+			'$=' => $this->translateSuffixMatch(...),
41
+			'*=' => $this->translateSubstringMatch(...),
42
+			'!=' => $this->translateDifferent(...),
43
+		];
44
+	}
45 45
 
46
-    public function translateExists(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
47
-    {
48
-        return $xpath->addCondition($attribute);
49
-    }
46
+	public function translateExists(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
47
+	{
48
+		return $xpath->addCondition($attribute);
49
+	}
50 50
 
51
-    public function translateEquals(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
52
-    {
53
-        return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value)));
54
-    }
51
+	public function translateEquals(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
52
+	{
53
+		return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value)));
54
+	}
55 55
 
56
-    public function translateIncludes(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
57
-    {
58
-        return $xpath->addCondition($value ? sprintf(
59
-            '%1$s and contains(concat(\' \', normalize-space(%1$s), \' \'), %2$s)',
60
-            $attribute,
61
-            Translator::getXpathLiteral(' '.$value.' ')
62
-        ) : '0');
63
-    }
56
+	public function translateIncludes(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
57
+	{
58
+		return $xpath->addCondition($value ? sprintf(
59
+			'%1$s and contains(concat(\' \', normalize-space(%1$s), \' \'), %2$s)',
60
+			$attribute,
61
+			Translator::getXpathLiteral(' '.$value.' ')
62
+		) : '0');
63
+	}
64 64
 
65
-    public function translateDashMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
66
-    {
67
-        return $xpath->addCondition(sprintf(
68
-            '%1$s and (%1$s = %2$s or starts-with(%1$s, %3$s))',
69
-            $attribute,
70
-            Translator::getXpathLiteral($value),
71
-            Translator::getXpathLiteral($value.'-')
72
-        ));
73
-    }
65
+	public function translateDashMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
66
+	{
67
+		return $xpath->addCondition(sprintf(
68
+			'%1$s and (%1$s = %2$s or starts-with(%1$s, %3$s))',
69
+			$attribute,
70
+			Translator::getXpathLiteral($value),
71
+			Translator::getXpathLiteral($value.'-')
72
+		));
73
+	}
74 74
 
75
-    public function translatePrefixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
76
-    {
77
-        return $xpath->addCondition($value ? sprintf(
78
-            '%1$s and starts-with(%1$s, %2$s)',
79
-            $attribute,
80
-            Translator::getXpathLiteral($value)
81
-        ) : '0');
82
-    }
75
+	public function translatePrefixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
76
+	{
77
+		return $xpath->addCondition($value ? sprintf(
78
+			'%1$s and starts-with(%1$s, %2$s)',
79
+			$attribute,
80
+			Translator::getXpathLiteral($value)
81
+		) : '0');
82
+	}
83 83
 
84
-    public function translateSuffixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
85
-    {
86
-        return $xpath->addCondition($value ? sprintf(
87
-            '%1$s and substring(%1$s, string-length(%1$s)-%2$s) = %3$s',
88
-            $attribute,
89
-            \strlen($value) - 1,
90
-            Translator::getXpathLiteral($value)
91
-        ) : '0');
92
-    }
84
+	public function translateSuffixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
85
+	{
86
+		return $xpath->addCondition($value ? sprintf(
87
+			'%1$s and substring(%1$s, string-length(%1$s)-%2$s) = %3$s',
88
+			$attribute,
89
+			\strlen($value) - 1,
90
+			Translator::getXpathLiteral($value)
91
+		) : '0');
92
+	}
93 93
 
94
-    public function translateSubstringMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
95
-    {
96
-        return $xpath->addCondition($value ? sprintf(
97
-            '%1$s and contains(%1$s, %2$s)',
98
-            $attribute,
99
-            Translator::getXpathLiteral($value)
100
-        ) : '0');
101
-    }
94
+	public function translateSubstringMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
95
+	{
96
+		return $xpath->addCondition($value ? sprintf(
97
+			'%1$s and contains(%1$s, %2$s)',
98
+			$attribute,
99
+			Translator::getXpathLiteral($value)
100
+		) : '0');
101
+	}
102 102
 
103
-    public function translateDifferent(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
104
-    {
105
-        return $xpath->addCondition(sprintf(
106
-            $value ? 'not(%1$s) or %1$s != %2$s' : '%s != %s',
107
-            $attribute,
108
-            Translator::getXpathLiteral($value)
109
-        ));
110
-    }
103
+	public function translateDifferent(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr
104
+	{
105
+		return $xpath->addCondition(sprintf(
106
+			$value ? 'not(%1$s) or %1$s != %2$s' : '%s != %s',
107
+			$attribute,
108
+			Translator::getXpathLiteral($value)
109
+		));
110
+	}
111 111
 
112
-    /**
113
-     * {@inheritdoc}
114
-     */
115
-    public function getName(): string
116
-    {
117
-        return 'attribute-matching';
118
-    }
112
+	/**
113
+	 * {@inheritdoc}
114
+	 */
115
+	public function getName(): string
116
+	{
117
+		return 'attribute-matching';
118
+	}
119 119
 }
Please login to merge, or discard this patch.