From 447944da2e7b5654b91f324e7aa7574f8ef39d32 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 19 May 2021 15:18:37 +0200 Subject: [PATCH 01/31] Bump Symfony 6 to PHP 8 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index eb63cf5..7c9551c 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "php": ">=7.2.5" + "php": ">=8.0.2" }, "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" }, From e3a55cd1ced2fa4e278eb20a880b1a6ccb8890c9 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 28 May 2021 17:52:26 +0200 Subject: [PATCH 02/31] Add return type to __toString() --- Node/AttributeNode.php | 3 --- Node/ClassNode.php | 3 --- Node/CombinedSelectorNode.php | 3 --- Node/ElementNode.php | 3 --- Node/FunctionNode.php | 3 --- Node/HashNode.php | 3 --- Node/NegationNode.php | 3 --- Node/PseudoNode.php | 3 --- Node/SelectorNode.php | 3 --- 9 files changed, 27 deletions(-) diff --git a/Node/AttributeNode.php b/Node/AttributeNode.php index bf702d9..0b6e0ee 100644 --- a/Node/AttributeNode.php +++ b/Node/AttributeNode.php @@ -71,9 +71,6 @@ public function getSpecificity(): Specificity return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } - /** - * {@inheritdoc} - */ public function __toString(): string { $attribute = $this->namespace ? $this->namespace.'|'.$this->attribute : $this->attribute; diff --git a/Node/ClassNode.php b/Node/ClassNode.php index 1998b4b..1efca80 100644 --- a/Node/ClassNode.php +++ b/Node/ClassNode.php @@ -50,9 +50,6 @@ public function getSpecificity(): Specificity return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } - /** - * {@inheritdoc} - */ public function __toString(): string { return sprintf('%s[%s.%s]', $this->getNodeName(), $this->selector, $this->name); diff --git a/Node/CombinedSelectorNode.php b/Node/CombinedSelectorNode.php index f97fd21..a217a45 100644 --- a/Node/CombinedSelectorNode.php +++ b/Node/CombinedSelectorNode.php @@ -57,9 +57,6 @@ public function getSpecificity(): Specificity return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); } - /** - * {@inheritdoc} - */ public function __toString(): string { $combinator = ' ' === $this->combinator ? '' : $this->combinator; diff --git a/Node/ElementNode.php b/Node/ElementNode.php index 7949ed9..fbf8ea0 100644 --- a/Node/ElementNode.php +++ b/Node/ElementNode.php @@ -50,9 +50,6 @@ public function getSpecificity(): Specificity return new Specificity(0, 0, $this->element ? 1 : 0); } - /** - * {@inheritdoc} - */ public function __toString(): string { $element = $this->element ?: '*'; diff --git a/Node/FunctionNode.php b/Node/FunctionNode.php index d3e9b4f..c464cf7 100644 --- a/Node/FunctionNode.php +++ b/Node/FunctionNode.php @@ -65,9 +65,6 @@ public function getSpecificity(): Specificity return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } - /** - * {@inheritdoc} - */ public function __toString(): string { $arguments = implode(', ', array_map(function (Token $token) { diff --git a/Node/HashNode.php b/Node/HashNode.php index f73fa2e..94114c0 100644 --- a/Node/HashNode.php +++ b/Node/HashNode.php @@ -50,9 +50,6 @@ public function getSpecificity(): Specificity return $this->selector->getSpecificity()->plus(new Specificity(1, 0, 0)); } - /** - * {@inheritdoc} - */ public function __toString(): string { return sprintf('%s[%s#%s]', $this->getNodeName(), $this->selector, $this->id); diff --git a/Node/NegationNode.php b/Node/NegationNode.php index afa47cf..f00522f 100644 --- a/Node/NegationNode.php +++ b/Node/NegationNode.php @@ -50,9 +50,6 @@ public function getSpecificity(): Specificity return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); } - /** - * {@inheritdoc} - */ public function __toString(): string { return sprintf('%s[%s:not(%s)]', $this->getNodeName(), $this->selector, $this->subSelector); diff --git a/Node/PseudoNode.php b/Node/PseudoNode.php index 7d4a011..12b7bd2 100644 --- a/Node/PseudoNode.php +++ b/Node/PseudoNode.php @@ -50,9 +50,6 @@ public function getSpecificity(): Specificity return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } - /** - * {@inheritdoc} - */ public function __toString(): string { return sprintf('%s[%s:%s]', $this->getNodeName(), $this->selector, $this->identifier); diff --git a/Node/SelectorNode.php b/Node/SelectorNode.php index a76aa5b..6e52b2f 100644 --- a/Node/SelectorNode.php +++ b/Node/SelectorNode.php @@ -50,9 +50,6 @@ public function getSpecificity(): Specificity return $this->tree->getSpecificity()->plus(new Specificity(0, 0, $this->pseudoElement ? 1 : 0)); } - /** - * {@inheritdoc} - */ public function __toString(): string { return sprintf('%s[%s%s]', $this->getNodeName(), $this->tree, $this->pseudoElement ? '::'.$this->pseudoElement : ''); From d289f5d19bc0e3618136a7927b55d414dafc2208 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 2 Jun 2021 18:09:43 +0200 Subject: [PATCH 03/31] Update phpunit.xml.dist files for phpunit >= 9.3 --- phpunit.xml.dist | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index a8e537e..7d8f839 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,7 +1,7 @@ - - + + ./ - - ./Resources - ./Tests - ./vendor - - - + + + ./Resources + ./Tests + ./vendor + + From d8206f5dfe9deaf10f41531d6fff9566443288a8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 27 May 2021 19:18:44 +0200 Subject: [PATCH 04/31] Add union types --- XPath/Translator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XPath/Translator.php b/XPath/Translator.php index 13e1ada..3fab76a 100644 --- a/XPath/Translator.php +++ b/XPath/Translator.php @@ -203,7 +203,7 @@ public function addPseudoClass(XPathExpr $xpath, string $pseudoClass): XPathExpr /** * @throws ExpressionErrorException */ - public function addAttributeMatching(XPathExpr $xpath, string $operator, string $attribute, $value): XPathExpr + public function addAttributeMatching(XPathExpr $xpath, string $operator, string $attribute, ?string $value): XPathExpr { if (!isset($this->attributeMatchingTranslators[$operator])) { throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator)); From 4742e79c85fda8b3db8979a62b56eb16ab0c7e50 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sun, 4 Jul 2021 23:06:02 +0200 Subject: [PATCH 05/31] [CssSelector] Add types to private properties Signed-off-by: Alexander M. Turek --- CssSelectorConverter.php | 8 ++++---- Node/AbstractNode.php | 11 ++--------- Node/AttributeNode.php | 10 +++++----- Node/ClassNode.php | 4 ++-- Node/CombinedSelectorNode.php | 6 +++--- Node/ElementNode.php | 4 ++-- Node/FunctionNode.php | 6 +++--- Node/HashNode.php | 4 ++-- Node/NegationNode.php | 4 ++-- Node/PseudoNode.php | 4 ++-- Node/SelectorNode.php | 4 ++-- Node/Specificity.php | 6 +++--- Parser/Handler/HashHandler.php | 4 ++-- Parser/Handler/IdentifierHandler.php | 4 ++-- Parser/Handler/NumberHandler.php | 2 +- Parser/Handler/StringHandler.php | 4 ++-- Parser/Parser.php | 2 +- Parser/Reader.php | 6 +++--- Parser/Token.php | 6 +++--- Parser/TokenStream.php | 21 +++++---------------- Parser/Tokenizer/Tokenizer.php | 2 +- Parser/Tokenizer/TokenizerEscaping.php | 2 +- Parser/Tokenizer/TokenizerPatterns.php | 24 ++++++++++++------------ XPath/Extension/NodeExtension.php | 2 +- XPath/Translator.php | 16 ++++++++-------- XPath/XPathExpr.php | 6 +++--- 26 files changed, 77 insertions(+), 95 deletions(-) diff --git a/CssSelectorConverter.php b/CssSelectorConverter.php index bbb6afe..faeee7a 100644 --- a/CssSelectorConverter.php +++ b/CssSelectorConverter.php @@ -26,11 +26,11 @@ */ class CssSelectorConverter { - private $translator; - private $cache; + private Translator $translator; + private array $cache; - private static $xmlCache = []; - private static $htmlCache = []; + private static array $xmlCache = []; + private static array $htmlCache = []; /** * @param bool $html Whether HTML support should be enabled. Disable it for XML documents diff --git a/Node/AbstractNode.php b/Node/AbstractNode.php index 1306aea..d99e80a 100644 --- a/Node/AbstractNode.php +++ b/Node/AbstractNode.php @@ -23,17 +23,10 @@ */ abstract class AbstractNode implements NodeInterface { - /** - * @var string - */ - private $nodeName; + private string $nodeName; public function getNodeName(): string { - if (null === $this->nodeName) { - $this->nodeName = preg_replace('~.*\\\\([^\\\\]+)Node$~', '$1', static::class); - } - - return $this->nodeName; + return $this->nodeName ??= preg_replace('~.*\\\\([^\\\\]+)Node$~', '$1', static::class); } } diff --git a/Node/AttributeNode.php b/Node/AttributeNode.php index 0b6e0ee..bca9db7 100644 --- a/Node/AttributeNode.php +++ b/Node/AttributeNode.php @@ -23,11 +23,11 @@ */ class AttributeNode extends AbstractNode { - private $selector; - private $namespace; - private $attribute; - private $operator; - private $value; + private NodeInterface $selector; + private ?string $namespace; + private string $attribute; + private string $operator; + private ?string $value; public function __construct(NodeInterface $selector, ?string $namespace, string $attribute, string $operator, ?string $value) { diff --git a/Node/ClassNode.php b/Node/ClassNode.php index 1efca80..b928407 100644 --- a/Node/ClassNode.php +++ b/Node/ClassNode.php @@ -23,8 +23,8 @@ */ class ClassNode extends AbstractNode { - private $selector; - private $name; + private NodeInterface $selector; + private string $name; public function __construct(NodeInterface $selector, string $name) { diff --git a/Node/CombinedSelectorNode.php b/Node/CombinedSelectorNode.php index a217a45..3388994 100644 --- a/Node/CombinedSelectorNode.php +++ b/Node/CombinedSelectorNode.php @@ -23,9 +23,9 @@ */ class CombinedSelectorNode extends AbstractNode { - private $selector; - private $combinator; - private $subSelector; + private NodeInterface $selector; + private string $combinator; + private NodeInterface $subSelector; public function __construct(NodeInterface $selector, string $combinator, NodeInterface $subSelector) { diff --git a/Node/ElementNode.php b/Node/ElementNode.php index fbf8ea0..b05164f 100644 --- a/Node/ElementNode.php +++ b/Node/ElementNode.php @@ -23,8 +23,8 @@ */ class ElementNode extends AbstractNode { - private $namespace; - private $element; + private ?string $namespace; + private ?string $element; public function __construct(string $namespace = null, string $element = null) { diff --git a/Node/FunctionNode.php b/Node/FunctionNode.php index c464cf7..5bfca18 100644 --- a/Node/FunctionNode.php +++ b/Node/FunctionNode.php @@ -25,9 +25,9 @@ */ class FunctionNode extends AbstractNode { - private $selector; - private $name; - private $arguments; + private NodeInterface $selector; + private string $name; + private array $arguments; /** * @param Token[] $arguments diff --git a/Node/HashNode.php b/Node/HashNode.php index 94114c0..5f5923f 100644 --- a/Node/HashNode.php +++ b/Node/HashNode.php @@ -23,8 +23,8 @@ */ class HashNode extends AbstractNode { - private $selector; - private $id; + private NodeInterface $selector; + private string $id; public function __construct(NodeInterface $selector, string $id) { diff --git a/Node/NegationNode.php b/Node/NegationNode.php index f00522f..c75d052 100644 --- a/Node/NegationNode.php +++ b/Node/NegationNode.php @@ -23,8 +23,8 @@ */ class NegationNode extends AbstractNode { - private $selector; - private $subSelector; + private NodeInterface $selector; + private NodeInterface $subSelector; public function __construct(NodeInterface $selector, NodeInterface $subSelector) { diff --git a/Node/PseudoNode.php b/Node/PseudoNode.php index 12b7bd2..6825f3a 100644 --- a/Node/PseudoNode.php +++ b/Node/PseudoNode.php @@ -23,8 +23,8 @@ */ class PseudoNode extends AbstractNode { - private $selector; - private $identifier; + private NodeInterface $selector; + private string $identifier; public function __construct(NodeInterface $selector, string $identifier) { diff --git a/Node/SelectorNode.php b/Node/SelectorNode.php index 6e52b2f..b1d6c58 100644 --- a/Node/SelectorNode.php +++ b/Node/SelectorNode.php @@ -23,8 +23,8 @@ */ class SelectorNode extends AbstractNode { - private $tree; - private $pseudoElement; + private NodeInterface $tree; + private ?string $pseudoElement; public function __construct(NodeInterface $tree, string $pseudoElement = null) { diff --git a/Node/Specificity.php b/Node/Specificity.php index b00f6d2..bb8e5e3 100644 --- a/Node/Specificity.php +++ b/Node/Specificity.php @@ -29,9 +29,9 @@ class Specificity public const B_FACTOR = 10; public const C_FACTOR = 1; - private $a; - private $b; - private $c; + private int $a; + private int $b; + private int $c; public function __construct(int $a, int $b, int $c) { diff --git a/Parser/Handler/HashHandler.php b/Parser/Handler/HashHandler.php index 7ae9b43..cde8cda 100644 --- a/Parser/Handler/HashHandler.php +++ b/Parser/Handler/HashHandler.php @@ -29,8 +29,8 @@ */ class HashHandler implements HandlerInterface { - private $patterns; - private $escaping; + private TokenizerPatterns $patterns; + private TokenizerEscaping $escaping; public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) { diff --git a/Parser/Handler/IdentifierHandler.php b/Parser/Handler/IdentifierHandler.php index 7b2a14e..2b9e02e 100644 --- a/Parser/Handler/IdentifierHandler.php +++ b/Parser/Handler/IdentifierHandler.php @@ -29,8 +29,8 @@ */ class IdentifierHandler implements HandlerInterface { - private $patterns; - private $escaping; + private TokenizerPatterns $patterns; + private TokenizerEscaping $escaping; public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) { diff --git a/Parser/Handler/NumberHandler.php b/Parser/Handler/NumberHandler.php index 8291a68..d3bbe5f 100644 --- a/Parser/Handler/NumberHandler.php +++ b/Parser/Handler/NumberHandler.php @@ -28,7 +28,7 @@ */ class NumberHandler implements HandlerInterface { - private $patterns; + private TokenizerPatterns $patterns; public function __construct(TokenizerPatterns $patterns) { diff --git a/Parser/Handler/StringHandler.php b/Parser/Handler/StringHandler.php index 6ce83cd..341c75f 100644 --- a/Parser/Handler/StringHandler.php +++ b/Parser/Handler/StringHandler.php @@ -31,8 +31,8 @@ */ class StringHandler implements HandlerInterface { - private $patterns; - private $escaping; + private TokenizerPatterns $patterns; + private TokenizerEscaping $escaping; public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) { diff --git a/Parser/Parser.php b/Parser/Parser.php index 963efb0..388a9cb 100644 --- a/Parser/Parser.php +++ b/Parser/Parser.php @@ -27,7 +27,7 @@ */ class Parser implements ParserInterface { - private $tokenizer; + private Tokenizer $tokenizer; public function __construct(Tokenizer $tokenizer = null) { diff --git a/Parser/Reader.php b/Parser/Reader.php index 4b43eff..34e5af2 100644 --- a/Parser/Reader.php +++ b/Parser/Reader.php @@ -23,9 +23,9 @@ */ class Reader { - private $source; - private $length; - private $position = 0; + private string $source; + private int $length; + private int $position = 0; public function __construct(string $source) { diff --git a/Parser/Token.php b/Parser/Token.php index a053203..a538d07 100644 --- a/Parser/Token.php +++ b/Parser/Token.php @@ -31,9 +31,9 @@ class Token public const TYPE_NUMBER = 'number'; public const TYPE_STRING = 'string'; - private $type; - private $value; - private $position; + private ?string $type; + private ?string $value; + private ?int $position; public function __construct(?string $type, ?string $value, ?int $position) { diff --git a/Parser/TokenStream.php b/Parser/TokenStream.php index f4c2585..e5ce48b 100644 --- a/Parser/TokenStream.php +++ b/Parser/TokenStream.php @@ -29,27 +29,16 @@ class TokenStream /** * @var Token[] */ - private $tokens = []; + private array $tokens = []; /** * @var Token[] */ - private $used = []; + private array $used = []; - /** - * @var int - */ - private $cursor = 0; - - /** - * @var Token|null - */ - private $peeked; - - /** - * @var bool - */ - private $peeking = false; + private int $cursor = 0; + private ?Token $peeked; + private bool $peeking = false; /** * Pushes a token. diff --git a/Parser/Tokenizer/Tokenizer.php b/Parser/Tokenizer/Tokenizer.php index e0dcc5b..35c96a4 100644 --- a/Parser/Tokenizer/Tokenizer.php +++ b/Parser/Tokenizer/Tokenizer.php @@ -31,7 +31,7 @@ class Tokenizer /** * @var Handler\HandlerInterface[] */ - private $handlers; + private array $handlers; public function __construct() { diff --git a/Parser/Tokenizer/TokenizerEscaping.php b/Parser/Tokenizer/TokenizerEscaping.php index 013e827..8c4b9f7 100644 --- a/Parser/Tokenizer/TokenizerEscaping.php +++ b/Parser/Tokenizer/TokenizerEscaping.php @@ -23,7 +23,7 @@ */ class TokenizerEscaping { - private $patterns; + private TokenizerPatterns $patterns; public function __construct(TokenizerPatterns $patterns) { diff --git a/Parser/Tokenizer/TokenizerPatterns.php b/Parser/Tokenizer/TokenizerPatterns.php index 5f16ac4..0b2767a 100644 --- a/Parser/Tokenizer/TokenizerPatterns.php +++ b/Parser/Tokenizer/TokenizerPatterns.php @@ -23,18 +23,18 @@ */ class TokenizerPatterns { - private $unicodeEscapePattern; - private $simpleEscapePattern; - private $newLineEscapePattern; - private $escapePattern; - private $stringEscapePattern; - private $nonAsciiPattern; - private $nmCharPattern; - private $nmStartPattern; - private $identifierPattern; - private $hashPattern; - private $numberPattern; - private $quotedStringPattern; + private string $unicodeEscapePattern; + private string $simpleEscapePattern; + private string $newLineEscapePattern; + private string $escapePattern; + private string $stringEscapePattern; + private string $nonAsciiPattern; + private string $nmCharPattern; + private string $nmStartPattern; + private string $identifierPattern; + private string $hashPattern; + private string $numberPattern; + private string $quotedStringPattern; public function __construct() { diff --git a/XPath/Extension/NodeExtension.php b/XPath/Extension/NodeExtension.php index aa6f3f7..824851f 100644 --- a/XPath/Extension/NodeExtension.php +++ b/XPath/Extension/NodeExtension.php @@ -31,7 +31,7 @@ class NodeExtension extends AbstractExtension public const ATTRIBUTE_NAME_IN_LOWER_CASE = 2; public const ATTRIBUTE_VALUE_IN_LOWER_CASE = 4; - private $flags; + private int $flags; public function __construct(int $flags = 0) { diff --git a/XPath/Translator.php b/XPath/Translator.php index 3fab76a..b0045bd 100644 --- a/XPath/Translator.php +++ b/XPath/Translator.php @@ -30,23 +30,23 @@ */ class Translator implements TranslatorInterface { - private $mainParser; + private ParserInterface $mainParser; /** * @var ParserInterface[] */ - private $shortcutParsers = []; + private array $shortcutParsers = []; /** * @var Extension\ExtensionInterface[] */ - private $extensions = []; + private array $extensions = []; - private $nodeTranslators = []; - private $combinationTranslators = []; - private $functionTranslators = []; - private $pseudoClassTranslators = []; - private $attributeMatchingTranslators = []; + private array $nodeTranslators = []; + private array $combinationTranslators = []; + private array $functionTranslators = []; + private array $pseudoClassTranslators = []; + private array $attributeMatchingTranslators = []; public function __construct(ParserInterface $parser = null) { diff --git a/XPath/XPathExpr.php b/XPath/XPathExpr.php index 638cbd0..f3304a6 100644 --- a/XPath/XPathExpr.php +++ b/XPath/XPathExpr.php @@ -23,9 +23,9 @@ */ class XPathExpr { - private $path; - private $element; - private $condition; + private string $path; + private string $element; + private string $condition; public function __construct(string $path = '', string $element = '*', string $condition = '', bool $starPrefix = false) { From 8a7b08e9e3b74dda3a002d2e9149224b54fb001a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 12 Aug 2021 17:01:43 +0200 Subject: [PATCH 06/31] Add return types - batch 2/n --- CssSelectorConverter.php | 4 +--- Exception/SyntaxErrorException.php | 25 +++++-------------------- Parser/Reader.php | 2 +- Parser/TokenStream.php | 4 ++-- XPath/Extension/NodeExtension.php | 2 +- XPath/Translator.php | 4 ++-- XPath/XPathExpr.php | 2 +- 7 files changed, 13 insertions(+), 30 deletions(-) diff --git a/CssSelectorConverter.php b/CssSelectorConverter.php index faeee7a..a322e93 100644 --- a/CssSelectorConverter.php +++ b/CssSelectorConverter.php @@ -59,10 +59,8 @@ public function __construct(bool $html = true) * * Optionally, a prefix can be added to the resulting XPath * expression with the $prefix parameter. - * - * @return string */ - public function toXPath(string $cssExpr, string $prefix = 'descendant-or-self::') + public function toXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string { return $this->cache[$prefix][$cssExpr] ?? $this->cache[$prefix][$cssExpr] = $this->translator->cssToXPath($cssExpr, $prefix); } diff --git a/Exception/SyntaxErrorException.php b/Exception/SyntaxErrorException.php index 7deacf9..f73860c 100644 --- a/Exception/SyntaxErrorException.php +++ b/Exception/SyntaxErrorException.php @@ -23,42 +23,27 @@ */ class SyntaxErrorException extends ParseException { - /** - * @return self - */ - public static function unexpectedToken(string $expectedValue, Token $foundToken) + public static function unexpectedToken(string $expectedValue, Token $foundToken): self { return new self(sprintf('Expected %s, but %s found.', $expectedValue, $foundToken)); } - /** - * @return self - */ - public static function pseudoElementFound(string $pseudoElement, string $unexpectedLocation) + public static function pseudoElementFound(string $pseudoElement, string $unexpectedLocation): self { return new self(sprintf('Unexpected pseudo-element "::%s" found %s.', $pseudoElement, $unexpectedLocation)); } - /** - * @return self - */ - public static function unclosedString(int $position) + public static function unclosedString(int $position): self { return new self(sprintf('Unclosed/invalid string at %s.', $position)); } - /** - * @return self - */ - public static function nestedNot() + public static function nestedNot(): self { return new self('Got nested ::not().'); } - /** - * @return self - */ - public static function stringAsFunctionArgument() + public static function stringAsFunctionArgument(): self { return new self('String not allowed as function argument.'); } diff --git a/Parser/Reader.php b/Parser/Reader.php index 34e5af2..f926e60 100644 --- a/Parser/Reader.php +++ b/Parser/Reader.php @@ -63,7 +63,7 @@ public function getOffset(string $string) /** * @return array|false */ - public function findPattern(string $pattern) + public function findPattern(string $pattern): array|false { $source = substr($this->source, $this->position); diff --git a/Parser/TokenStream.php b/Parser/TokenStream.php index e5ce48b..d1daa75 100644 --- a/Parser/TokenStream.php +++ b/Parser/TokenStream.php @@ -45,7 +45,7 @@ class TokenStream * * @return $this */ - public function push(Token $token): self + public function push(Token $token): static { $this->tokens[] = $token; @@ -57,7 +57,7 @@ public function push(Token $token): self * * @return $this */ - public function freeze(): self + public function freeze(): static { return $this; } diff --git a/XPath/Extension/NodeExtension.php b/XPath/Extension/NodeExtension.php index 824851f..642702b 100644 --- a/XPath/Extension/NodeExtension.php +++ b/XPath/Extension/NodeExtension.php @@ -41,7 +41,7 @@ public function __construct(int $flags = 0) /** * @return $this */ - public function setFlag(int $flag, bool $on): self + public function setFlag(int $flag, bool $on): static { if ($on && !$this->hasFlag($flag)) { $this->flags += $flag; diff --git a/XPath/Translator.php b/XPath/Translator.php index b671ad5..128a4a9 100644 --- a/XPath/Translator.php +++ b/XPath/Translator.php @@ -117,7 +117,7 @@ public function selectorToXPath(SelectorNode $selector, string $prefix = 'descen /** * @return $this */ - public function registerExtension(Extension\ExtensionInterface $extension): self + public function registerExtension(Extension\ExtensionInterface $extension): static { $this->extensions[$extension->getName()] = $extension; @@ -145,7 +145,7 @@ public function getExtension(string $name): Extension\ExtensionInterface /** * @return $this */ - public function registerParserShortcut(ParserInterface $shortcut): self + public function registerParserShortcut(ParserInterface $shortcut): static { $this->shortcutParsers[] = $shortcut; diff --git a/XPath/XPathExpr.php b/XPath/XPathExpr.php index f3304a6..61f8034 100644 --- a/XPath/XPathExpr.php +++ b/XPath/XPathExpr.php @@ -77,7 +77,7 @@ public function addStarPrefix(): self * * @return $this */ - public function join(string $combiner, self $expr): self + public function join(string $combiner, self $expr): static { $path = $this->__toString().$combiner; From 3a61e2e4fbda3fb7fb5d83620c30fef726139e1c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 9 Sep 2021 14:56:10 +0200 Subject: [PATCH 07/31] Fix return types --- XPath/XPathExpr.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/XPath/XPathExpr.php b/XPath/XPathExpr.php index b67ef93..a76e30b 100644 --- a/XPath/XPathExpr.php +++ b/XPath/XPathExpr.php @@ -46,7 +46,7 @@ public function getElement(): string /** * @return $this */ - public function addCondition(string $condition): self + public function addCondition(string $condition): static { $this->condition = $this->condition ? sprintf('(%s) and (%s)', $this->condition, $condition) : $condition; @@ -61,7 +61,7 @@ public function getCondition(): string /** * @return $this */ - public function addNameTest(): self + public function addNameTest(): static { if ('*' !== $this->element) { $this->addCondition('name() = '.Translator::getXpathLiteral($this->element)); @@ -74,7 +74,7 @@ public function addNameTest(): self /** * @return $this */ - public function addStarPrefix(): self + public function addStarPrefix(): static { $this->path .= '*/'; From ede53cafe1784b9131a48774b54f281d5d003f65 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 7 Dec 2021 12:27:08 +0100 Subject: [PATCH 08/31] Remove FQCN type hints on properties --- CssSelectorConverter.php | 2 +- Node/AttributeNode.php | 2 +- Node/ClassNode.php | 2 +- Node/CombinedSelectorNode.php | 4 ++-- Node/FunctionNode.php | 2 +- Node/HashNode.php | 2 +- Node/NegationNode.php | 4 ++-- Node/PseudoNode.php | 2 +- Node/SelectorNode.php | 2 +- Parser/Handler/HashHandler.php | 4 ++-- Parser/Handler/IdentifierHandler.php | 4 ++-- Parser/Handler/NumberHandler.php | 2 +- Parser/Handler/StringHandler.php | 4 ++-- Parser/Parser.php | 2 +- Parser/TokenStream.php | 2 +- Parser/Tokenizer/TokenizerEscaping.php | 2 +- XPath/Translator.php | 2 +- 17 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CssSelectorConverter.php b/CssSelectorConverter.php index a322e93..86ccb79 100644 --- a/CssSelectorConverter.php +++ b/CssSelectorConverter.php @@ -26,7 +26,7 @@ */ class CssSelectorConverter { - private Translator $translator; + private $translator; private array $cache; private static array $xmlCache = []; diff --git a/Node/AttributeNode.php b/Node/AttributeNode.php index bca9db7..bca91eb 100644 --- a/Node/AttributeNode.php +++ b/Node/AttributeNode.php @@ -23,7 +23,7 @@ */ class AttributeNode extends AbstractNode { - private NodeInterface $selector; + private $selector; private ?string $namespace; private string $attribute; private string $operator; diff --git a/Node/ClassNode.php b/Node/ClassNode.php index b928407..0e93768 100644 --- a/Node/ClassNode.php +++ b/Node/ClassNode.php @@ -23,7 +23,7 @@ */ class ClassNode extends AbstractNode { - private NodeInterface $selector; + private $selector; private string $name; public function __construct(NodeInterface $selector, string $name) diff --git a/Node/CombinedSelectorNode.php b/Node/CombinedSelectorNode.php index 3388994..f5f9969 100644 --- a/Node/CombinedSelectorNode.php +++ b/Node/CombinedSelectorNode.php @@ -23,9 +23,9 @@ */ class CombinedSelectorNode extends AbstractNode { - private NodeInterface $selector; + private $selector; private string $combinator; - private NodeInterface $subSelector; + private $subSelector; public function __construct(NodeInterface $selector, string $combinator, NodeInterface $subSelector) { diff --git a/Node/FunctionNode.php b/Node/FunctionNode.php index 5bfca18..e91a166 100644 --- a/Node/FunctionNode.php +++ b/Node/FunctionNode.php @@ -25,7 +25,7 @@ */ class FunctionNode extends AbstractNode { - private NodeInterface $selector; + private $selector; private string $name; private array $arguments; diff --git a/Node/HashNode.php b/Node/HashNode.php index 5f5923f..119b0d5 100644 --- a/Node/HashNode.php +++ b/Node/HashNode.php @@ -23,7 +23,7 @@ */ class HashNode extends AbstractNode { - private NodeInterface $selector; + private $selector; private string $id; public function __construct(NodeInterface $selector, string $id) diff --git a/Node/NegationNode.php b/Node/NegationNode.php index c75d052..f00522f 100644 --- a/Node/NegationNode.php +++ b/Node/NegationNode.php @@ -23,8 +23,8 @@ */ class NegationNode extends AbstractNode { - private NodeInterface $selector; - private NodeInterface $subSelector; + private $selector; + private $subSelector; public function __construct(NodeInterface $selector, NodeInterface $subSelector) { diff --git a/Node/PseudoNode.php b/Node/PseudoNode.php index 6825f3a..5d6325a 100644 --- a/Node/PseudoNode.php +++ b/Node/PseudoNode.php @@ -23,7 +23,7 @@ */ class PseudoNode extends AbstractNode { - private NodeInterface $selector; + private $selector; private string $identifier; public function __construct(NodeInterface $selector, string $identifier) diff --git a/Node/SelectorNode.php b/Node/SelectorNode.php index b1d6c58..55fae1e 100644 --- a/Node/SelectorNode.php +++ b/Node/SelectorNode.php @@ -23,7 +23,7 @@ */ class SelectorNode extends AbstractNode { - private NodeInterface $tree; + private $tree; private ?string $pseudoElement; public function __construct(NodeInterface $tree, string $pseudoElement = null) diff --git a/Parser/Handler/HashHandler.php b/Parser/Handler/HashHandler.php index cde8cda..7ae9b43 100644 --- a/Parser/Handler/HashHandler.php +++ b/Parser/Handler/HashHandler.php @@ -29,8 +29,8 @@ */ class HashHandler implements HandlerInterface { - private TokenizerPatterns $patterns; - private TokenizerEscaping $escaping; + private $patterns; + private $escaping; public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) { diff --git a/Parser/Handler/IdentifierHandler.php b/Parser/Handler/IdentifierHandler.php index 2b9e02e..7b2a14e 100644 --- a/Parser/Handler/IdentifierHandler.php +++ b/Parser/Handler/IdentifierHandler.php @@ -29,8 +29,8 @@ */ class IdentifierHandler implements HandlerInterface { - private TokenizerPatterns $patterns; - private TokenizerEscaping $escaping; + private $patterns; + private $escaping; public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) { diff --git a/Parser/Handler/NumberHandler.php b/Parser/Handler/NumberHandler.php index d3bbe5f..8291a68 100644 --- a/Parser/Handler/NumberHandler.php +++ b/Parser/Handler/NumberHandler.php @@ -28,7 +28,7 @@ */ class NumberHandler implements HandlerInterface { - private TokenizerPatterns $patterns; + private $patterns; public function __construct(TokenizerPatterns $patterns) { diff --git a/Parser/Handler/StringHandler.php b/Parser/Handler/StringHandler.php index 341c75f..6ce83cd 100644 --- a/Parser/Handler/StringHandler.php +++ b/Parser/Handler/StringHandler.php @@ -31,8 +31,8 @@ */ class StringHandler implements HandlerInterface { - private TokenizerPatterns $patterns; - private TokenizerEscaping $escaping; + private $patterns; + private $escaping; public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) { diff --git a/Parser/Parser.php b/Parser/Parser.php index df77e2c..d73489e 100644 --- a/Parser/Parser.php +++ b/Parser/Parser.php @@ -27,7 +27,7 @@ */ class Parser implements ParserInterface { - private Tokenizer $tokenizer; + private $tokenizer; public function __construct(Tokenizer $tokenizer = null) { diff --git a/Parser/TokenStream.php b/Parser/TokenStream.php index 69f7778..e2c15a6 100644 --- a/Parser/TokenStream.php +++ b/Parser/TokenStream.php @@ -37,7 +37,7 @@ class TokenStream private array $used = []; private int $cursor = 0; - private ?Token $peeked; + private $peeked; private bool $peeking = false; /** diff --git a/Parser/Tokenizer/TokenizerEscaping.php b/Parser/Tokenizer/TokenizerEscaping.php index 8c4b9f7..013e827 100644 --- a/Parser/Tokenizer/TokenizerEscaping.php +++ b/Parser/Tokenizer/TokenizerEscaping.php @@ -23,7 +23,7 @@ */ class TokenizerEscaping { - private TokenizerPatterns $patterns; + private $patterns; public function __construct(TokenizerPatterns $patterns) { diff --git a/XPath/Translator.php b/XPath/Translator.php index 128a4a9..c0bb29d 100644 --- a/XPath/Translator.php +++ b/XPath/Translator.php @@ -30,7 +30,7 @@ */ class Translator implements TranslatorInterface { - private ParserInterface $mainParser; + private $mainParser; /** * @var ParserInterface[] From 380f86c1a9830226f42a08b5926f18aed4195f25 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 16 Dec 2021 23:13:01 +0100 Subject: [PATCH 09/31] [6.0] cs fixes --- Parser/Reader.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/Parser/Reader.php b/Parser/Reader.php index f926e60..c0b6923 100644 --- a/Parser/Reader.php +++ b/Parser/Reader.php @@ -60,9 +60,6 @@ public function getOffset(string $string) return false === $position ? false : $position - $this->position; } - /** - * @return array|false - */ public function findPattern(string $pattern): array|false { $source = substr($this->source, $this->position); From 7be7e43ed04460e4e3f1d720df7f50060148cb88 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 16 Dec 2021 11:11:51 +0100 Subject: [PATCH 10/31] Add more nullsafe operators --- CssSelectorConverter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CssSelectorConverter.php b/CssSelectorConverter.php index a322e93..7120a29 100644 --- a/CssSelectorConverter.php +++ b/CssSelectorConverter.php @@ -62,6 +62,6 @@ public function __construct(bool $html = true) */ public function toXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string { - return $this->cache[$prefix][$cssExpr] ?? $this->cache[$prefix][$cssExpr] = $this->translator->cssToXPath($cssExpr, $prefix); + return $this->cache[$prefix][$cssExpr] ??= $this->translator->cssToXPath($cssExpr, $prefix); } } From 05c40f02f621609404b8820ff8bc39acb46e19cf Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 9 Feb 2022 15:00:38 +0100 Subject: [PATCH 11/31] Bump minimum version of PHP to 8.1 --- Tests/Parser/Handler/AbstractHandlerTest.php | 1 - Tests/Parser/ReaderTest.php | 1 - XPath/Extension/AttributeMatchingExtension.php | 16 ++++++++-------- XPath/Extension/CombinationExtension.php | 8 ++++---- XPath/Extension/FunctionExtension.php | 12 ++++++------ XPath/Extension/HtmlExtension.php | 18 +++++++++--------- XPath/Extension/NodeExtension.php | 18 +++++++++--------- XPath/Extension/PseudoClassExtension.php | 16 ++++++++-------- composer.json | 2 +- 9 files changed, 45 insertions(+), 47 deletions(-) diff --git a/Tests/Parser/Handler/AbstractHandlerTest.php b/Tests/Parser/Handler/AbstractHandlerTest.php index aaed55c..8afc7ae 100644 --- a/Tests/Parser/Handler/AbstractHandlerTest.php +++ b/Tests/Parser/Handler/AbstractHandlerTest.php @@ -52,7 +52,6 @@ abstract protected function generateHandler(); protected function assertStreamEmpty(TokenStream $stream) { $property = new \ReflectionProperty($stream, 'tokens'); - $property->setAccessible(true); $this->assertEquals([], $property->getValue($stream)); } diff --git a/Tests/Parser/ReaderTest.php b/Tests/Parser/ReaderTest.php index 8f57979..fa056dd 100644 --- a/Tests/Parser/ReaderTest.php +++ b/Tests/Parser/ReaderTest.php @@ -96,7 +96,6 @@ public function testToEnd() private function assignPosition(Reader $reader, int $value) { $position = new \ReflectionProperty($reader, 'position'); - $position->setAccessible(true); $position->setValue($reader, $value); } } diff --git a/XPath/Extension/AttributeMatchingExtension.php b/XPath/Extension/AttributeMatchingExtension.php index a9879f1..b40a86b 100644 --- a/XPath/Extension/AttributeMatchingExtension.php +++ b/XPath/Extension/AttributeMatchingExtension.php @@ -32,14 +32,14 @@ class AttributeMatchingExtension extends AbstractExtension public function getAttributeMatchingTranslators(): array { return [ - 'exists' => [$this, 'translateExists'], - '=' => [$this, 'translateEquals'], - '~=' => [$this, 'translateIncludes'], - '|=' => [$this, 'translateDashMatch'], - '^=' => [$this, 'translatePrefixMatch'], - '$=' => [$this, 'translateSuffixMatch'], - '*=' => [$this, 'translateSubstringMatch'], - '!=' => [$this, 'translateDifferent'], + 'exists' => $this->translateExists(...), + '=' => $this->translateEquals(...), + '~=' => $this->translateIncludes(...), + '|=' => $this->translateDashMatch(...), + '^=' => $this->translatePrefixMatch(...), + '$=' => $this->translateSuffixMatch(...), + '*=' => $this->translateSubstringMatch(...), + '!=' => $this->translateDifferent(...), ]; } diff --git a/XPath/Extension/CombinationExtension.php b/XPath/Extension/CombinationExtension.php index aee976e..e75da8b 100644 --- a/XPath/Extension/CombinationExtension.php +++ b/XPath/Extension/CombinationExtension.php @@ -31,10 +31,10 @@ class CombinationExtension extends AbstractExtension public function getCombinationTranslators(): array { return [ - ' ' => [$this, 'translateDescendant'], - '>' => [$this, 'translateChild'], - '+' => [$this, 'translateDirectAdjacent'], - '~' => [$this, 'translateIndirectAdjacent'], + ' ' => $this->translateDescendant(...), + '>' => $this->translateChild(...), + '+' => $this->translateDirectAdjacent(...), + '~' => $this->translateIndirectAdjacent(...), ]; } diff --git a/XPath/Extension/FunctionExtension.php b/XPath/Extension/FunctionExtension.php index d3f7222..4ed9c23 100644 --- a/XPath/Extension/FunctionExtension.php +++ b/XPath/Extension/FunctionExtension.php @@ -36,12 +36,12 @@ class FunctionExtension extends AbstractExtension public function getFunctionTranslators(): array { return [ - 'nth-child' => [$this, 'translateNthChild'], - 'nth-last-child' => [$this, 'translateNthLastChild'], - 'nth-of-type' => [$this, 'translateNthOfType'], - 'nth-last-of-type' => [$this, 'translateNthLastOfType'], - 'contains' => [$this, 'translateContains'], - 'lang' => [$this, 'translateLang'], + 'nth-child' => $this->translateNthChild(...), + 'nth-last-child' => $this->translateNthLastChild(...), + 'nth-of-type' => $this->translateNthOfType(...), + 'nth-last-of-type' => $this->translateNthLastOfType(...), + 'contains' => $this->translateContains(...), + 'lang' => $this->translateLang(...), ]; } diff --git a/XPath/Extension/HtmlExtension.php b/XPath/Extension/HtmlExtension.php index 6edc085..9528981 100644 --- a/XPath/Extension/HtmlExtension.php +++ b/XPath/Extension/HtmlExtension.php @@ -42,14 +42,14 @@ public function __construct(Translator $translator) public function getPseudoClassTranslators(): array { return [ - 'checked' => [$this, 'translateChecked'], - 'link' => [$this, 'translateLink'], - 'disabled' => [$this, 'translateDisabled'], - 'enabled' => [$this, 'translateEnabled'], - 'selected' => [$this, 'translateSelected'], - 'invalid' => [$this, 'translateInvalid'], - 'hover' => [$this, 'translateHover'], - 'visited' => [$this, 'translateVisited'], + 'checked' => $this->translateChecked(...), + 'link' => $this->translateLink(...), + 'disabled' => $this->translateDisabled(...), + 'enabled' => $this->translateEnabled(...), + 'selected' => $this->translateSelected(...), + 'invalid' => $this->translateInvalid(...), + 'hover' => $this->translateHover(...), + 'visited' => $this->translateVisited(...), ]; } @@ -59,7 +59,7 @@ public function getPseudoClassTranslators(): array public function getFunctionTranslators(): array { return [ - 'lang' => [$this, 'translateLang'], + 'lang' => $this->translateLang(...), ]; } diff --git a/XPath/Extension/NodeExtension.php b/XPath/Extension/NodeExtension.php index 642702b..70f287b 100644 --- a/XPath/Extension/NodeExtension.php +++ b/XPath/Extension/NodeExtension.php @@ -65,15 +65,15 @@ public function hasFlag(int $flag): bool public function getNodeTranslators(): array { return [ - 'Selector' => [$this, 'translateSelector'], - 'CombinedSelector' => [$this, 'translateCombinedSelector'], - 'Negation' => [$this, 'translateNegation'], - 'Function' => [$this, 'translateFunction'], - 'Pseudo' => [$this, 'translatePseudo'], - 'Attribute' => [$this, 'translateAttribute'], - 'Class' => [$this, 'translateClass'], - 'Hash' => [$this, 'translateHash'], - 'Element' => [$this, 'translateElement'], + 'Selector' => $this->translateSelector(...), + 'CombinedSelector' => $this->translateCombinedSelector(...), + 'Negation' => $this->translateNegation(...), + 'Function' => $this->translateFunction(...), + 'Pseudo' => $this->translatePseudo(...), + 'Attribute' => $this->translateAttribute(...), + 'Class' => $this->translateClass(...), + 'Hash' => $this->translateHash(...), + 'Element' => $this->translateElement(...), ]; } diff --git a/XPath/Extension/PseudoClassExtension.php b/XPath/Extension/PseudoClassExtension.php index a50b048..a9b6abc 100644 --- a/XPath/Extension/PseudoClassExtension.php +++ b/XPath/Extension/PseudoClassExtension.php @@ -32,14 +32,14 @@ class PseudoClassExtension extends AbstractExtension public function getPseudoClassTranslators(): array { return [ - 'root' => [$this, 'translateRoot'], - 'first-child' => [$this, 'translateFirstChild'], - 'last-child' => [$this, 'translateLastChild'], - 'first-of-type' => [$this, 'translateFirstOfType'], - 'last-of-type' => [$this, 'translateLastOfType'], - 'only-child' => [$this, 'translateOnlyChild'], - 'only-of-type' => [$this, 'translateOnlyOfType'], - 'empty' => [$this, 'translateEmpty'], + 'root' => $this->translateRoot(...), + 'first-child' => $this->translateFirstChild(...), + 'last-child' => $this->translateLastChild(...), + 'first-of-type' => $this->translateFirstOfType(...), + 'last-of-type' => $this->translateLastOfType(...), + 'only-child' => $this->translateOnlyChild(...), + 'only-of-type' => $this->translateOnlyOfType(...), + 'empty' => $this->translateEmpty(...), ]; } diff --git a/composer.json b/composer.json index 7c9551c..c08fdc2 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" }, From 021fd05e0be9eebca0356606f26b62fc6e3902e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 25 Aug 2022 16:59:21 +0200 Subject: [PATCH 12/31] [CS] Remove @inheritdoc PHPDoc --- Node/AttributeNode.php | 3 --- Node/ClassNode.php | 3 --- Node/CombinedSelectorNode.php | 3 --- Node/ElementNode.php | 3 --- Node/FunctionNode.php | 3 --- Node/HashNode.php | 3 --- Node/NegationNode.php | 3 --- Node/PseudoNode.php | 3 --- Node/SelectorNode.php | 3 --- Parser/Handler/CommentHandler.php | 3 --- Parser/Handler/HashHandler.php | 3 --- Parser/Handler/IdentifierHandler.php | 3 --- Parser/Handler/NumberHandler.php | 3 --- Parser/Handler/StringHandler.php | 3 --- Parser/Handler/WhitespaceHandler.php | 3 --- Parser/Parser.php | 3 --- Parser/Shortcut/ClassParser.php | 3 --- Parser/Shortcut/ElementParser.php | 3 --- Parser/Shortcut/EmptyStringParser.php | 3 --- Parser/Shortcut/HashParser.php | 3 --- XPath/Extension/AbstractExtension.php | 15 --------------- XPath/Extension/AttributeMatchingExtension.php | 6 ------ XPath/Extension/CombinationExtension.php | 6 ------ XPath/Extension/FunctionExtension.php | 6 ------ XPath/Extension/HtmlExtension.php | 9 --------- XPath/Extension/NodeExtension.php | 6 ------ XPath/Extension/PseudoClassExtension.php | 6 ------ XPath/Translator.php | 6 ------ 28 files changed, 120 deletions(-) diff --git a/Node/AttributeNode.php b/Node/AttributeNode.php index bca9db7..ba4df31 100644 --- a/Node/AttributeNode.php +++ b/Node/AttributeNode.php @@ -63,9 +63,6 @@ public function getValue(): ?string return $this->value; } - /** - * {@inheritdoc} - */ public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); diff --git a/Node/ClassNode.php b/Node/ClassNode.php index b928407..7174458 100644 --- a/Node/ClassNode.php +++ b/Node/ClassNode.php @@ -42,9 +42,6 @@ public function getName(): string return $this->name; } - /** - * {@inheritdoc} - */ public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); diff --git a/Node/CombinedSelectorNode.php b/Node/CombinedSelectorNode.php index 3388994..19fecb7 100644 --- a/Node/CombinedSelectorNode.php +++ b/Node/CombinedSelectorNode.php @@ -49,9 +49,6 @@ public function getSubSelector(): NodeInterface return $this->subSelector; } - /** - * {@inheritdoc} - */ public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); diff --git a/Node/ElementNode.php b/Node/ElementNode.php index b05164f..39f4d9c 100644 --- a/Node/ElementNode.php +++ b/Node/ElementNode.php @@ -42,9 +42,6 @@ public function getElement(): ?string return $this->element; } - /** - * {@inheritdoc} - */ public function getSpecificity(): Specificity { return new Specificity(0, 0, $this->element ? 1 : 0); diff --git a/Node/FunctionNode.php b/Node/FunctionNode.php index 5bfca18..8428bac 100644 --- a/Node/FunctionNode.php +++ b/Node/FunctionNode.php @@ -57,9 +57,6 @@ public function getArguments(): array return $this->arguments; } - /** - * {@inheritdoc} - */ public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); diff --git a/Node/HashNode.php b/Node/HashNode.php index 5f5923f..3af8e84 100644 --- a/Node/HashNode.php +++ b/Node/HashNode.php @@ -42,9 +42,6 @@ public function getId(): string return $this->id; } - /** - * {@inheritdoc} - */ public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus(new Specificity(1, 0, 0)); diff --git a/Node/NegationNode.php b/Node/NegationNode.php index c75d052..f806758 100644 --- a/Node/NegationNode.php +++ b/Node/NegationNode.php @@ -42,9 +42,6 @@ public function getSubSelector(): NodeInterface return $this->subSelector; } - /** - * {@inheritdoc} - */ public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); diff --git a/Node/PseudoNode.php b/Node/PseudoNode.php index 6825f3a..c21cd6e 100644 --- a/Node/PseudoNode.php +++ b/Node/PseudoNode.php @@ -42,9 +42,6 @@ public function getIdentifier(): string return $this->identifier; } - /** - * {@inheritdoc} - */ public function getSpecificity(): Specificity { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); diff --git a/Node/SelectorNode.php b/Node/SelectorNode.php index b1d6c58..0b09ab5 100644 --- a/Node/SelectorNode.php +++ b/Node/SelectorNode.php @@ -42,9 +42,6 @@ public function getPseudoElement(): ?string return $this->pseudoElement; } - /** - * {@inheritdoc} - */ public function getSpecificity(): Specificity { return $this->tree->getSpecificity()->plus(new Specificity(0, 0, $this->pseudoElement ? 1 : 0)); diff --git a/Parser/Handler/CommentHandler.php b/Parser/Handler/CommentHandler.php index 93f3188..cc01d1e 100644 --- a/Parser/Handler/CommentHandler.php +++ b/Parser/Handler/CommentHandler.php @@ -26,9 +26,6 @@ */ class CommentHandler implements HandlerInterface { - /** - * {@inheritdoc} - */ public function handle(Reader $reader, TokenStream $stream): bool { if ('/*' !== $reader->getSubstring(2)) { diff --git a/Parser/Handler/HashHandler.php b/Parser/Handler/HashHandler.php index cde8cda..b29042f 100644 --- a/Parser/Handler/HashHandler.php +++ b/Parser/Handler/HashHandler.php @@ -38,9 +38,6 @@ public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $esca $this->escaping = $escaping; } - /** - * {@inheritdoc} - */ public function handle(Reader $reader, TokenStream $stream): bool { $match = $reader->findPattern($this->patterns->getHashPattern()); diff --git a/Parser/Handler/IdentifierHandler.php b/Parser/Handler/IdentifierHandler.php index 2b9e02e..25c0761 100644 --- a/Parser/Handler/IdentifierHandler.php +++ b/Parser/Handler/IdentifierHandler.php @@ -38,9 +38,6 @@ public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $esca $this->escaping = $escaping; } - /** - * {@inheritdoc} - */ public function handle(Reader $reader, TokenStream $stream): bool { $match = $reader->findPattern($this->patterns->getIdentifierPattern()); diff --git a/Parser/Handler/NumberHandler.php b/Parser/Handler/NumberHandler.php index d3bbe5f..e3eb7af 100644 --- a/Parser/Handler/NumberHandler.php +++ b/Parser/Handler/NumberHandler.php @@ -35,9 +35,6 @@ public function __construct(TokenizerPatterns $patterns) $this->patterns = $patterns; } - /** - * {@inheritdoc} - */ public function handle(Reader $reader, TokenStream $stream): bool { $match = $reader->findPattern($this->patterns->getNumberPattern()); diff --git a/Parser/Handler/StringHandler.php b/Parser/Handler/StringHandler.php index 341c75f..5fd44df 100644 --- a/Parser/Handler/StringHandler.php +++ b/Parser/Handler/StringHandler.php @@ -40,9 +40,6 @@ public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $esca $this->escaping = $escaping; } - /** - * {@inheritdoc} - */ public function handle(Reader $reader, TokenStream $stream): bool { $quote = $reader->getSubstring(1); diff --git a/Parser/Handler/WhitespaceHandler.php b/Parser/Handler/WhitespaceHandler.php index 21345e3..eb41c3f 100644 --- a/Parser/Handler/WhitespaceHandler.php +++ b/Parser/Handler/WhitespaceHandler.php @@ -27,9 +27,6 @@ */ class WhitespaceHandler implements HandlerInterface { - /** - * {@inheritdoc} - */ public function handle(Reader $reader, TokenStream $stream): bool { $match = $reader->findPattern('~^[ \t\r\n\f]+~'); diff --git a/Parser/Parser.php b/Parser/Parser.php index df77e2c..2263399 100644 --- a/Parser/Parser.php +++ b/Parser/Parser.php @@ -34,9 +34,6 @@ public function __construct(Tokenizer $tokenizer = null) $this->tokenizer = $tokenizer ?? new Tokenizer(); } - /** - * {@inheritdoc} - */ public function parse(string $source): array { $reader = new Reader($source); diff --git a/Parser/Shortcut/ClassParser.php b/Parser/Shortcut/ClassParser.php index 17fa8c2..f0ce611 100644 --- a/Parser/Shortcut/ClassParser.php +++ b/Parser/Shortcut/ClassParser.php @@ -28,9 +28,6 @@ */ class ClassParser implements ParserInterface { - /** - * {@inheritdoc} - */ public function parse(string $source): array { // Matches an optional namespace, optional element, and required class diff --git a/Parser/Shortcut/ElementParser.php b/Parser/Shortcut/ElementParser.php index 8b9a863..a448e4a 100644 --- a/Parser/Shortcut/ElementParser.php +++ b/Parser/Shortcut/ElementParser.php @@ -27,9 +27,6 @@ */ class ElementParser implements ParserInterface { - /** - * {@inheritdoc} - */ public function parse(string $source): array { // Matches an optional namespace, required element or `*` diff --git a/Parser/Shortcut/EmptyStringParser.php b/Parser/Shortcut/EmptyStringParser.php index 222df5c..a639191 100644 --- a/Parser/Shortcut/EmptyStringParser.php +++ b/Parser/Shortcut/EmptyStringParser.php @@ -31,9 +31,6 @@ */ class EmptyStringParser implements ParserInterface { - /** - * {@inheritdoc} - */ public function parse(string $source): array { // Matches an empty string diff --git a/Parser/Shortcut/HashParser.php b/Parser/Shortcut/HashParser.php index fb07ee6..6683126 100644 --- a/Parser/Shortcut/HashParser.php +++ b/Parser/Shortcut/HashParser.php @@ -28,9 +28,6 @@ */ class HashParser implements ParserInterface { - /** - * {@inheritdoc} - */ public function parse(string $source): array { // Matches an optional namespace, optional element, and required id diff --git a/XPath/Extension/AbstractExtension.php b/XPath/Extension/AbstractExtension.php index 44e0035..495f882 100644 --- a/XPath/Extension/AbstractExtension.php +++ b/XPath/Extension/AbstractExtension.php @@ -23,41 +23,26 @@ */ abstract class AbstractExtension implements ExtensionInterface { - /** - * {@inheritdoc} - */ public function getNodeTranslators(): array { return []; } - /** - * {@inheritdoc} - */ public function getCombinationTranslators(): array { return []; } - /** - * {@inheritdoc} - */ public function getFunctionTranslators(): array { return []; } - /** - * {@inheritdoc} - */ public function getPseudoClassTranslators(): array { return []; } - /** - * {@inheritdoc} - */ public function getAttributeMatchingTranslators(): array { return []; diff --git a/XPath/Extension/AttributeMatchingExtension.php b/XPath/Extension/AttributeMatchingExtension.php index b40a86b..3c785e9 100644 --- a/XPath/Extension/AttributeMatchingExtension.php +++ b/XPath/Extension/AttributeMatchingExtension.php @@ -26,9 +26,6 @@ */ class AttributeMatchingExtension extends AbstractExtension { - /** - * {@inheritdoc} - */ public function getAttributeMatchingTranslators(): array { return [ @@ -109,9 +106,6 @@ public function translateDifferent(XPathExpr $xpath, string $attribute, ?string )); } - /** - * {@inheritdoc} - */ public function getName(): string { return 'attribute-matching'; diff --git a/XPath/Extension/CombinationExtension.php b/XPath/Extension/CombinationExtension.php index e75da8b..f78d488 100644 --- a/XPath/Extension/CombinationExtension.php +++ b/XPath/Extension/CombinationExtension.php @@ -25,9 +25,6 @@ */ class CombinationExtension extends AbstractExtension { - /** - * {@inheritdoc} - */ public function getCombinationTranslators(): array { return [ @@ -61,9 +58,6 @@ public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedX return $xpath->join('/following-sibling::', $combinedXpath); } - /** - * {@inheritdoc} - */ public function getName(): string { return 'combination'; diff --git a/XPath/Extension/FunctionExtension.php b/XPath/Extension/FunctionExtension.php index 4ed9c23..4b9d7bc 100644 --- a/XPath/Extension/FunctionExtension.php +++ b/XPath/Extension/FunctionExtension.php @@ -30,9 +30,6 @@ */ class FunctionExtension extends AbstractExtension { - /** - * {@inheritdoc} - */ public function getFunctionTranslators(): array { return [ @@ -161,9 +158,6 @@ public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathEx )); } - /** - * {@inheritdoc} - */ public function getName(): string { return 'function'; diff --git a/XPath/Extension/HtmlExtension.php b/XPath/Extension/HtmlExtension.php index 9528981..4653add 100644 --- a/XPath/Extension/HtmlExtension.php +++ b/XPath/Extension/HtmlExtension.php @@ -36,9 +36,6 @@ public function __construct(Translator $translator) ->setFlag(NodeExtension::ATTRIBUTE_NAME_IN_LOWER_CASE, true); } - /** - * {@inheritdoc} - */ public function getPseudoClassTranslators(): array { return [ @@ -53,9 +50,6 @@ public function getPseudoClassTranslators(): array ]; } - /** - * {@inheritdoc} - */ public function getFunctionTranslators(): array { return [ @@ -177,9 +171,6 @@ public function translateVisited(XPathExpr $xpath): XPathExpr return $xpath->addCondition('0'); } - /** - * {@inheritdoc} - */ public function getName(): string { return 'html'; diff --git a/XPath/Extension/NodeExtension.php b/XPath/Extension/NodeExtension.php index 70f287b..49e894a 100644 --- a/XPath/Extension/NodeExtension.php +++ b/XPath/Extension/NodeExtension.php @@ -59,9 +59,6 @@ public function hasFlag(int $flag): bool return (bool) ($this->flags & $flag); } - /** - * {@inheritdoc} - */ public function getNodeTranslators(): array { return [ @@ -182,9 +179,6 @@ public function translateElement(Node\ElementNode $node): XPathExpr return $xpath; } - /** - * {@inheritdoc} - */ public function getName(): string { return 'node'; diff --git a/XPath/Extension/PseudoClassExtension.php b/XPath/Extension/PseudoClassExtension.php index a9b6abc..36ab582 100644 --- a/XPath/Extension/PseudoClassExtension.php +++ b/XPath/Extension/PseudoClassExtension.php @@ -26,9 +26,6 @@ */ class PseudoClassExtension extends AbstractExtension { - /** - * {@inheritdoc} - */ public function getPseudoClassTranslators(): array { return [ @@ -112,9 +109,6 @@ public function translateEmpty(XPathExpr $xpath): XPathExpr return $xpath->addCondition('not(*) and not(string-length())'); } - /** - * {@inheritdoc} - */ public function getName(): string { return 'pseudo-class'; diff --git a/XPath/Translator.php b/XPath/Translator.php index 128a4a9..4c5d5a0 100644 --- a/XPath/Translator.php +++ b/XPath/Translator.php @@ -87,9 +87,6 @@ public static function getXpathLiteral(string $element): string return sprintf('concat(%s)', implode(', ', $parts)); } - /** - * {@inheritdoc} - */ public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string { $selectors = $this->parseSelectors($cssExpr); @@ -106,9 +103,6 @@ public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self return implode(' | ', $selectors); } - /** - * {@inheritdoc} - */ public function selectorToXPath(SelectorNode $selector, string $prefix = 'descendant-or-self::'): string { return ($prefix ?: '').$this->nodeToXPath($selector); From b4491ac9e026f9851f2691a020c05b070a45e159 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Tue, 23 Aug 2022 21:46:41 +0200 Subject: [PATCH 13/31] Remove usage of empty function when possible --- Parser/Parser.php | 2 +- Parser/Token.php | 2 +- XPath/Translator.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Parser/Parser.php b/Parser/Parser.php index df77e2c..aef60e7 100644 --- a/Parser/Parser.php +++ b/Parser/Parser.php @@ -242,7 +242,7 @@ private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = } } - if (empty($arguments)) { + if (!$arguments) { throw SyntaxErrorException::unexpectedToken('at least one argument', $next); } diff --git a/Parser/Token.php b/Parser/Token.php index a538d07..b50441a 100644 --- a/Parser/Token.php +++ b/Parser/Token.php @@ -68,7 +68,7 @@ public function isDelimiter(array $values = []): bool return false; } - if (empty($values)) { + if (!$values) { return true; } diff --git a/XPath/Translator.php b/XPath/Translator.php index 128a4a9..2d6d466 100644 --- a/XPath/Translator.php +++ b/XPath/Translator.php @@ -220,7 +220,7 @@ private function parseSelectors(string $css): array foreach ($this->shortcutParsers as $shortcut) { $tokens = $shortcut->parse($css); - if (!empty($tokens)) { + if ($tokens) { return $tokens; } } From 952d70db7645f69a8970541d91877b37bb8b3c12 Mon Sep 17 00:00:00 2001 From: tigitz Date: Sun, 1 Jan 2023 19:45:34 +0100 Subject: [PATCH 14/31] Leverage arrow function syntax for closure --- Node/FunctionNode.php | 4 +--- Parser/Parser.php | 4 +--- Tests/Parser/ParserTest.php | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Node/FunctionNode.php b/Node/FunctionNode.php index 8428bac..938a82b 100644 --- a/Node/FunctionNode.php +++ b/Node/FunctionNode.php @@ -64,9 +64,7 @@ public function getSpecificity(): Specificity public function __toString(): string { - $arguments = implode(', ', array_map(function (Token $token) { - return "'".$token->getValue()."'"; - }, $this->arguments)); + $arguments = implode(', ', array_map(fn (Token $token) => "'".$token->getValue()."'", $this->arguments)); return sprintf('%s[%s:%s(%s)]', $this->getNodeName(), $this->selector, $this->name, $arguments ? '['.$arguments.']' : ''); } diff --git a/Parser/Parser.php b/Parser/Parser.php index d611efe..101df57 100644 --- a/Parser/Parser.php +++ b/Parser/Parser.php @@ -57,9 +57,7 @@ public static function parseSeries(array $tokens): array } } - $joined = trim(implode('', array_map(function (Token $token) { - return $token->getValue(); - }, $tokens))); + $joined = trim(implode('', array_map(fn (Token $token) => $token->getValue(), $tokens))); $int = function ($string) { if (!is_numeric($string)) { diff --git a/Tests/Parser/ParserTest.php b/Tests/Parser/ParserTest.php index 48a67f5..919357b 100644 --- a/Tests/Parser/ParserTest.php +++ b/Tests/Parser/ParserTest.php @@ -25,9 +25,7 @@ public function testParser($source, $representation) { $parser = new Parser(); - $this->assertEquals($representation, array_map(function (SelectorNode $node) { - return (string) $node->getTree(); - }, $parser->parse($source))); + $this->assertEquals($representation, array_map(fn (SelectorNode $node) => (string) $node->getTree(), $parser->parse($source))); } /** @dataProvider getParserExceptionTestData */ From 36cd5587c38f474dfdb238fa2bfae86db80efbc9 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 6 Feb 2023 06:06:48 +0100 Subject: [PATCH 15/31] [PHPUnit 10] Use `TestCase` suffix for abstract tests in `/Tests/` --- Tests/Node/{AbstractNodeTest.php => AbstractNodeTestCase.php} | 2 +- Tests/Node/AttributeNodeTest.php | 2 +- Tests/Node/ClassNodeTest.php | 2 +- Tests/Node/CombinedSelectorNodeTest.php | 2 +- Tests/Node/ElementNodeTest.php | 2 +- Tests/Node/FunctionNodeTest.php | 2 +- Tests/Node/HashNodeTest.php | 2 +- Tests/Node/NegationNodeTest.php | 2 +- Tests/Node/PseudoNodeTest.php | 2 +- Tests/Node/SelectorNodeTest.php | 2 +- .../{AbstractHandlerTest.php => AbstractHandlerTestCase.php} | 2 +- Tests/Parser/Handler/CommentHandlerTest.php | 2 +- Tests/Parser/Handler/HashHandlerTest.php | 2 +- Tests/Parser/Handler/IdentifierHandlerTest.php | 2 +- Tests/Parser/Handler/NumberHandlerTest.php | 2 +- Tests/Parser/Handler/StringHandlerTest.php | 2 +- Tests/Parser/Handler/WhitespaceHandlerTest.php | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) rename Tests/Node/{AbstractNodeTest.php => AbstractNodeTestCase.php} (94%) rename Tests/Parser/Handler/{AbstractHandlerTest.php => AbstractHandlerTestCase.php} (97%) diff --git a/Tests/Node/AbstractNodeTest.php b/Tests/Node/AbstractNodeTestCase.php similarity index 94% rename from Tests/Node/AbstractNodeTest.php rename to Tests/Node/AbstractNodeTestCase.php index 5955513..5519dce 100644 --- a/Tests/Node/AbstractNodeTest.php +++ b/Tests/Node/AbstractNodeTestCase.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\CssSelector\Node\NodeInterface; -abstract class AbstractNodeTest extends TestCase +abstract class AbstractNodeTestCase extends TestCase { /** @dataProvider getToStringConversionTestData */ public function testToStringConversion(NodeInterface $node, $representation) diff --git a/Tests/Node/AttributeNodeTest.php b/Tests/Node/AttributeNodeTest.php index 4d60074..c424491 100644 --- a/Tests/Node/AttributeNodeTest.php +++ b/Tests/Node/AttributeNodeTest.php @@ -14,7 +14,7 @@ use Symfony\Component\CssSelector\Node\AttributeNode; use Symfony\Component\CssSelector\Node\ElementNode; -class AttributeNodeTest extends AbstractNodeTest +class AttributeNodeTest extends AbstractNodeTestCase { public function getToStringConversionTestData() { diff --git a/Tests/Node/ClassNodeTest.php b/Tests/Node/ClassNodeTest.php index aa80c92..f6af741 100644 --- a/Tests/Node/ClassNodeTest.php +++ b/Tests/Node/ClassNodeTest.php @@ -14,7 +14,7 @@ use Symfony\Component\CssSelector\Node\ClassNode; use Symfony\Component\CssSelector\Node\ElementNode; -class ClassNodeTest extends AbstractNodeTest +class ClassNodeTest extends AbstractNodeTestCase { public function getToStringConversionTestData() { diff --git a/Tests/Node/CombinedSelectorNodeTest.php b/Tests/Node/CombinedSelectorNodeTest.php index 435eaa6..b47fc65 100644 --- a/Tests/Node/CombinedSelectorNodeTest.php +++ b/Tests/Node/CombinedSelectorNodeTest.php @@ -14,7 +14,7 @@ use Symfony\Component\CssSelector\Node\CombinedSelectorNode; use Symfony\Component\CssSelector\Node\ElementNode; -class CombinedSelectorNodeTest extends AbstractNodeTest +class CombinedSelectorNodeTest extends AbstractNodeTestCase { public function getToStringConversionTestData() { diff --git a/Tests/Node/ElementNodeTest.php b/Tests/Node/ElementNodeTest.php index e0797ff..170645e 100644 --- a/Tests/Node/ElementNodeTest.php +++ b/Tests/Node/ElementNodeTest.php @@ -13,7 +13,7 @@ use Symfony\Component\CssSelector\Node\ElementNode; -class ElementNodeTest extends AbstractNodeTest +class ElementNodeTest extends AbstractNodeTestCase { public function getToStringConversionTestData() { diff --git a/Tests/Node/FunctionNodeTest.php b/Tests/Node/FunctionNodeTest.php index 0932393..8579f8c 100644 --- a/Tests/Node/FunctionNodeTest.php +++ b/Tests/Node/FunctionNodeTest.php @@ -15,7 +15,7 @@ use Symfony\Component\CssSelector\Node\FunctionNode; use Symfony\Component\CssSelector\Parser\Token; -class FunctionNodeTest extends AbstractNodeTest +class FunctionNodeTest extends AbstractNodeTestCase { public function getToStringConversionTestData() { diff --git a/Tests/Node/HashNodeTest.php b/Tests/Node/HashNodeTest.php index ad6765b..eb6de45 100644 --- a/Tests/Node/HashNodeTest.php +++ b/Tests/Node/HashNodeTest.php @@ -14,7 +14,7 @@ use Symfony\Component\CssSelector\Node\ElementNode; use Symfony\Component\CssSelector\Node\HashNode; -class HashNodeTest extends AbstractNodeTest +class HashNodeTest extends AbstractNodeTestCase { public function getToStringConversionTestData() { diff --git a/Tests/Node/NegationNodeTest.php b/Tests/Node/NegationNodeTest.php index 90b684c..6bca18b 100644 --- a/Tests/Node/NegationNodeTest.php +++ b/Tests/Node/NegationNodeTest.php @@ -15,7 +15,7 @@ use Symfony\Component\CssSelector\Node\ElementNode; use Symfony\Component\CssSelector\Node\NegationNode; -class NegationNodeTest extends AbstractNodeTest +class NegationNodeTest extends AbstractNodeTestCase { public function getToStringConversionTestData() { diff --git a/Tests/Node/PseudoNodeTest.php b/Tests/Node/PseudoNodeTest.php index 4e78776..366836d 100644 --- a/Tests/Node/PseudoNodeTest.php +++ b/Tests/Node/PseudoNodeTest.php @@ -14,7 +14,7 @@ use Symfony\Component\CssSelector\Node\ElementNode; use Symfony\Component\CssSelector\Node\PseudoNode; -class PseudoNodeTest extends AbstractNodeTest +class PseudoNodeTest extends AbstractNodeTestCase { public function getToStringConversionTestData() { diff --git a/Tests/Node/SelectorNodeTest.php b/Tests/Node/SelectorNodeTest.php index 85e3bda..4d1518a 100644 --- a/Tests/Node/SelectorNodeTest.php +++ b/Tests/Node/SelectorNodeTest.php @@ -14,7 +14,7 @@ use Symfony\Component\CssSelector\Node\ElementNode; use Symfony\Component\CssSelector\Node\SelectorNode; -class SelectorNodeTest extends AbstractNodeTest +class SelectorNodeTest extends AbstractNodeTestCase { public function getToStringConversionTestData() { diff --git a/Tests/Parser/Handler/AbstractHandlerTest.php b/Tests/Parser/Handler/AbstractHandlerTestCase.php similarity index 97% rename from Tests/Parser/Handler/AbstractHandlerTest.php rename to Tests/Parser/Handler/AbstractHandlerTestCase.php index 8afc7ae..1f642ef 100644 --- a/Tests/Parser/Handler/AbstractHandlerTest.php +++ b/Tests/Parser/Handler/AbstractHandlerTestCase.php @@ -19,7 +19,7 @@ /** * @author Jean-François Simon */ -abstract class AbstractHandlerTest extends TestCase +abstract class AbstractHandlerTestCase extends TestCase { /** @dataProvider getHandleValueTestData */ public function testHandleValue($value, Token $expectedToken, $remainingContent) diff --git a/Tests/Parser/Handler/CommentHandlerTest.php b/Tests/Parser/Handler/CommentHandlerTest.php index de40047..59eaeba 100644 --- a/Tests/Parser/Handler/CommentHandlerTest.php +++ b/Tests/Parser/Handler/CommentHandlerTest.php @@ -16,7 +16,7 @@ use Symfony\Component\CssSelector\Parser\Token; use Symfony\Component\CssSelector\Parser\TokenStream; -class CommentHandlerTest extends AbstractHandlerTest +class CommentHandlerTest extends AbstractHandlerTestCase { /** @dataProvider getHandleValueTestData */ public function testHandleValue($value, Token $unusedArgument, $remainingContent) diff --git a/Tests/Parser/Handler/HashHandlerTest.php b/Tests/Parser/Handler/HashHandlerTest.php index 9444f51..a156305 100644 --- a/Tests/Parser/Handler/HashHandlerTest.php +++ b/Tests/Parser/Handler/HashHandlerTest.php @@ -16,7 +16,7 @@ use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; -class HashHandlerTest extends AbstractHandlerTest +class HashHandlerTest extends AbstractHandlerTestCase { public function getHandleValueTestData() { diff --git a/Tests/Parser/Handler/IdentifierHandlerTest.php b/Tests/Parser/Handler/IdentifierHandlerTest.php index c54ba53..3bc35a9 100644 --- a/Tests/Parser/Handler/IdentifierHandlerTest.php +++ b/Tests/Parser/Handler/IdentifierHandlerTest.php @@ -16,7 +16,7 @@ use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; -class IdentifierHandlerTest extends AbstractHandlerTest +class IdentifierHandlerTest extends AbstractHandlerTestCase { public function getHandleValueTestData() { diff --git a/Tests/Parser/Handler/NumberHandlerTest.php b/Tests/Parser/Handler/NumberHandlerTest.php index b43d369..07a6018 100644 --- a/Tests/Parser/Handler/NumberHandlerTest.php +++ b/Tests/Parser/Handler/NumberHandlerTest.php @@ -15,7 +15,7 @@ use Symfony\Component\CssSelector\Parser\Token; use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; -class NumberHandlerTest extends AbstractHandlerTest +class NumberHandlerTest extends AbstractHandlerTestCase { public function getHandleValueTestData() { diff --git a/Tests/Parser/Handler/StringHandlerTest.php b/Tests/Parser/Handler/StringHandlerTest.php index baa7273..133ae38 100644 --- a/Tests/Parser/Handler/StringHandlerTest.php +++ b/Tests/Parser/Handler/StringHandlerTest.php @@ -16,7 +16,7 @@ use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerEscaping; use Symfony\Component\CssSelector\Parser\Tokenizer\TokenizerPatterns; -class StringHandlerTest extends AbstractHandlerTest +class StringHandlerTest extends AbstractHandlerTestCase { public function getHandleValueTestData() { diff --git a/Tests/Parser/Handler/WhitespaceHandlerTest.php b/Tests/Parser/Handler/WhitespaceHandlerTest.php index 67509ef..7a1b893 100644 --- a/Tests/Parser/Handler/WhitespaceHandlerTest.php +++ b/Tests/Parser/Handler/WhitespaceHandlerTest.php @@ -14,7 +14,7 @@ use Symfony\Component\CssSelector\Parser\Handler\WhitespaceHandler; use Symfony\Component\CssSelector\Parser\Token; -class WhitespaceHandlerTest extends AbstractHandlerTest +class WhitespaceHandlerTest extends AbstractHandlerTestCase { public function getHandleValueTestData() { From 5436c3ddc6486fae818a54ede1cd33f988b19000 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Mon, 13 Feb 2023 00:00:27 +0100 Subject: [PATCH 16/31] Add PHP types to private methods and functions --- Parser/Reader.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Parser/Reader.php b/Parser/Reader.php index c0b6923..7f6ae7a 100644 --- a/Parser/Reader.php +++ b/Parser/Reader.php @@ -53,7 +53,10 @@ public function getSubstring(int $length, int $offset = 0): string return substr($this->source, $this->position + $offset, $length); } - public function getOffset(string $string) + /** + * @return int|false + */ + public function getOffset(string $string): int|bool { $position = strpos($this->source, $string, $this->position); @@ -71,12 +74,12 @@ public function findPattern(string $pattern): array|false return false; } - public function moveForward(int $length) + public function moveForward(int $length): void { $this->position += $length; } - public function moveToEnd() + public function moveToEnd(): void { $this->position = $this->length; } From 275d858049480a2b637aa615f35e4564b3aa03df Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 12 Feb 2023 23:57:18 +0100 Subject: [PATCH 17/31] Add void return types --- Parser/TokenStream.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Parser/TokenStream.php b/Parser/TokenStream.php index 69f7778..8b72d5d 100644 --- a/Parser/TokenStream.php +++ b/Parser/TokenStream.php @@ -145,7 +145,7 @@ public function getNextIdentifierOrStar(): ?string /** * Skips next whitespace if any. */ - public function skipWhitespace() + public function skipWhitespace(): void { $peek = $this->getPeek(); From 88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf Mon Sep 17 00:00:00 2001 From: Franck Ranaivo-Harisoa Date: Mon, 6 Mar 2023 17:06:03 +0100 Subject: [PATCH 18/31] [CssSelector] Add suport for :scope --- CHANGELOG.md | 5 +++++ Exception/SyntaxErrorException.php | 5 +++++ Parser/Parser.php | 15 +++++++++++++-- Tests/Parser/ParserTest.php | 7 +++++++ Tests/XPath/TranslatorTest.php | 5 +++++ XPath/Extension/PseudoClassExtension.php | 6 ++++++ 6 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de81fa2..c035d6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.3 +----- + + * Add support for `:scope` + 4.4.0 ----- diff --git a/Exception/SyntaxErrorException.php b/Exception/SyntaxErrorException.php index f73860c..5a9d807 100644 --- a/Exception/SyntaxErrorException.php +++ b/Exception/SyntaxErrorException.php @@ -43,6 +43,11 @@ public static function nestedNot(): self return new self('Got nested ::not().'); } + public static function notAtTheStartOfASelector(string $pseudoElement): self + { + return new self(sprintf('Got immediate child pseudo-element ":%s" not at the start of a selector', $pseudoElement)); + } + public static function stringAsFunctionArgument(): self { return new self('String not allowed as function argument.'); diff --git a/Parser/Parser.php b/Parser/Parser.php index 101df57..5313d34 100644 --- a/Parser/Parser.php +++ b/Parser/Parser.php @@ -19,7 +19,7 @@ * CSS selector parser. * * This component is a port of the Python cssselect library, - * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * which is copyright Ian Bicking, @see https://github.com/scrapy/cssselect. * * @author Jean-François Simon * @@ -192,7 +192,18 @@ private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = if (!$stream->getPeek()->isDelimiter(['('])) { $result = new Node\PseudoNode($result, $identifier); - + if ('Pseudo[Element[*]:scope]' === $result->__toString()) { + $used = \count($stream->getUsed()); + if (!(2 === $used + || 3 === $used && $stream->getUsed()[0]->isWhiteSpace() + || $used >= 3 && $stream->getUsed()[$used - 3]->isDelimiter([',']) + || $used >= 4 + && $stream->getUsed()[$used - 3]->isWhiteSpace() + && $stream->getUsed()[$used - 4]->isDelimiter([',']) + )) { + throw SyntaxErrorException::notAtTheStartOfASelector('scope'); + } + } continue; } diff --git a/Tests/Parser/ParserTest.php b/Tests/Parser/ParserTest.php index af8cf58..a8708ce 100644 --- a/Tests/Parser/ParserTest.php +++ b/Tests/Parser/ParserTest.php @@ -146,6 +146,12 @@ public static function getParserTestData() // unicode escape: \20 == (space) ['*[aval="\'\20 \'"]', ['Attribute[Element[*][aval = \'\' \'\']]']], ["*[aval=\"'\\20\r\n '\"]", ['Attribute[Element[*][aval = \'\' \'\']]']], + [':scope > foo', ['CombinedSelector[Pseudo[Element[*]:scope] > Element[foo]]']], + [':scope > foo bar > div', ['CombinedSelector[CombinedSelector[CombinedSelector[Pseudo[Element[*]:scope] > Element[foo]] Element[bar]] > Element[div]]']], + [':scope > #foo #bar', ['CombinedSelector[CombinedSelector[Pseudo[Element[*]:scope] > Hash[Element[*]#foo]] Hash[Element[*]#bar]]']], + [':scope', ['Pseudo[Element[*]:scope]']], + ['foo bar, :scope > div', ['CombinedSelector[Element[foo] Element[bar]]', 'CombinedSelector[Pseudo[Element[*]:scope] > Element[div]]']], + ['foo bar,:scope > div', ['CombinedSelector[Element[foo] Element[bar]]', 'CombinedSelector[Pseudo[Element[*]:scope] > Element[div]]']], ]; } @@ -176,6 +182,7 @@ public static function getParserExceptionTestData() [':lang(fr', SyntaxErrorException::unexpectedToken('an argument', new Token(Token::TYPE_FILE_END, '', 8))->getMessage()], [':contains("foo', SyntaxErrorException::unclosedString(10)->getMessage()], ['foo!', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '!', 3))->getMessage()], + [':scope > div :scope header', SyntaxErrorException::notAtTheStartOfASelector('scope')->getMessage()], ]; } diff --git a/Tests/XPath/TranslatorTest.php b/Tests/XPath/TranslatorTest.php index d330600..894b8a0 100644 --- a/Tests/XPath/TranslatorTest.php +++ b/Tests/XPath/TranslatorTest.php @@ -219,6 +219,8 @@ public static function getCssToXPathTestData() ['e + f', "e/following-sibling::*[(name() = 'f') and (position() = 1)]"], ['e ~ f', 'e/following-sibling::f'], ['div#container p', "div[@id = 'container']/descendant-or-self::*/p"], + [':scope > div[dataimg=""]', "*[1]/div[@dataimg = '']"], + [':scope', '*[1]'], ]; } @@ -411,6 +413,9 @@ public static function getHtmlShakespearTestData() ['div[class|=dialog]', 50], // ? Seems right ['div[class!=madeup]', 243], // ? Seems right ['div[class~=dialog]', 51], // ? Seems right + [':scope > div', 1], + [':scope > div > div[class=dialog]', 1], + [':scope > div div', 242], ]; } } diff --git a/XPath/Extension/PseudoClassExtension.php b/XPath/Extension/PseudoClassExtension.php index 36ab582..aada832 100644 --- a/XPath/Extension/PseudoClassExtension.php +++ b/XPath/Extension/PseudoClassExtension.php @@ -30,6 +30,7 @@ public function getPseudoClassTranslators(): array { return [ 'root' => $this->translateRoot(...), + 'scope' => $this->translateScopePseudo(...), 'first-child' => $this->translateFirstChild(...), 'last-child' => $this->translateLastChild(...), 'first-of-type' => $this->translateFirstOfType(...), @@ -45,6 +46,11 @@ public function translateRoot(XPathExpr $xpath): XPathExpr return $xpath->addCondition('not(parent::*)'); } + public function translateScopePseudo(XPathExpr $xpath): XPathExpr + { + return $xpath->addCondition('1'); + } + public function translateFirstChild(XPathExpr $xpath): XPathExpr { return $xpath From 6ba609e09b51517816da205fbc18b21656fded84 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 May 2023 17:24:39 +0200 Subject: [PATCH 19/31] [7.0] Bump to PHP 8.2 minimum --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c08fdc2..a753a1a 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ } ], "require": { - "php": ">=8.1" + "php": ">=8.2" }, "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" }, From c9ffa750679e233b084a2a0f3d02b7f9c1d53d08 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 2 Jul 2023 23:52:21 +0200 Subject: [PATCH 20/31] [Components] Convert to native return types --- Parser/Reader.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Parser/Reader.php b/Parser/Reader.php index 7f6ae7a..a280a48 100644 --- a/Parser/Reader.php +++ b/Parser/Reader.php @@ -53,10 +53,7 @@ public function getSubstring(int $length, int $offset = 0): string return substr($this->source, $this->position + $offset, $length); } - /** - * @return int|false - */ - public function getOffset(string $string): int|bool + public function getOffset(string $string): int|false { $position = strpos($this->source, $string, $this->position); From 40eff9db7468587bbf18416ec5cbc2ee6867a356 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Fri, 28 Jul 2023 16:03:16 +0200 Subject: [PATCH 21/31] Use Stringable interface as much as possible --- Node/NodeInterface.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Node/NodeInterface.php b/Node/NodeInterface.php index b078d26..7d541f9 100644 --- a/Node/NodeInterface.php +++ b/Node/NodeInterface.php @@ -21,11 +21,9 @@ * * @internal */ -interface NodeInterface +interface NodeInterface extends \Stringable { public function getNodeName(): string; public function getSpecificity(): Specificity; - - public function __toString(): string; } From d036c6c0d0b09e24a14a35f8292146a658f986e4 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Fri, 27 Oct 2023 13:50:26 +0200 Subject: [PATCH 22/31] [Tests] Move expectException closer to the place of the expectation to avoid false positives --- Tests/CssSelectorConverterTest.php | 5 ++--- Tests/Parser/TokenStreamTest.php | 10 ++++++---- Tests/XPath/TranslatorTest.php | 24 ++++++++++++++++++------ 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Tests/CssSelectorConverterTest.php b/Tests/CssSelectorConverterTest.php index 36d39f3..c197032 100644 --- a/Tests/CssSelectorConverterTest.php +++ b/Tests/CssSelectorConverterTest.php @@ -48,8 +48,7 @@ public function testParseExceptions() { $this->expectException(ParseException::class); $this->expectExceptionMessage('Expected identifier, but found.'); - $converter = new CssSelectorConverter(); - $converter->toXPath('h1:'); + (new CssSelectorConverter())->toXPath('h1:'); } /** @dataProvider getCssToXPathWithoutPrefixTestData */ @@ -60,7 +59,7 @@ public function testCssToXPathWithoutPrefix($css, $xpath) $this->assertEquals($xpath, $converter->toXPath($css, ''), '->parse() parses an input string and returns a node'); } - public static function getCssToXPathWithoutPrefixTestData() + public static function getCssToXPathWithoutPrefixTestData(): array { return [ ['h1', 'h1'], diff --git a/Tests/Parser/TokenStreamTest.php b/Tests/Parser/TokenStreamTest.php index f50c8de..3692854 100644 --- a/Tests/Parser/TokenStreamTest.php +++ b/Tests/Parser/TokenStreamTest.php @@ -54,10 +54,11 @@ public function testGetNextIdentifier() public function testFailToGetNextIdentifier() { - $this->expectException(SyntaxErrorException::class); - $stream = new TokenStream(); $stream->push(new Token(Token::TYPE_DELIMITER, '.', 2)); + + $this->expectException(SyntaxErrorException::class); + $stream->getNextIdentifier(); } @@ -74,10 +75,11 @@ public function testGetNextIdentifierOrStar() public function testFailToGetNextIdentifierOrStar() { - $this->expectException(SyntaxErrorException::class); - $stream = new TokenStream(); $stream->push(new Token(Token::TYPE_DELIMITER, '.', 2)); + + $this->expectException(SyntaxErrorException::class); + $stream->getNextIdentifierOrStar(); } diff --git a/Tests/XPath/TranslatorTest.php b/Tests/XPath/TranslatorTest.php index bfb9072..c161b80 100644 --- a/Tests/XPath/TranslatorTest.php +++ b/Tests/XPath/TranslatorTest.php @@ -38,56 +38,68 @@ public function testCssToXPath($css, $xpath) public function testCssToXPathPseudoElement() { - $this->expectException(ExpressionErrorException::class); $translator = new Translator(); $translator->registerExtension(new HtmlExtension($translator)); + + $this->expectException(ExpressionErrorException::class); + $translator->cssToXPath('e::first-line'); } public function testGetExtensionNotExistsExtension() { - $this->expectException(ExpressionErrorException::class); $translator = new Translator(); $translator->registerExtension(new HtmlExtension($translator)); + + $this->expectException(ExpressionErrorException::class); + $translator->getExtension('fake'); } public function testAddCombinationNotExistsExtension() { - $this->expectException(ExpressionErrorException::class); $translator = new Translator(); $translator->registerExtension(new HtmlExtension($translator)); $parser = new Parser(); $xpath = $parser->parse('*')[0]; $combinedXpath = $parser->parse('*')[0]; + + $this->expectException(ExpressionErrorException::class); + $translator->addCombination('fake', $xpath, $combinedXpath); } public function testAddFunctionNotExistsFunction() { - $this->expectException(ExpressionErrorException::class); $translator = new Translator(); $translator->registerExtension(new HtmlExtension($translator)); $xpath = new XPathExpr(); $function = new FunctionNode(new ElementNode(), 'fake'); + + $this->expectException(ExpressionErrorException::class); + $translator->addFunction($xpath, $function); } public function testAddPseudoClassNotExistsClass() { - $this->expectException(ExpressionErrorException::class); $translator = new Translator(); $translator->registerExtension(new HtmlExtension($translator)); $xpath = new XPathExpr(); + + $this->expectException(ExpressionErrorException::class); + $translator->addPseudoClass($xpath, 'fake'); } public function testAddAttributeMatchingClassNotExistsClass() { - $this->expectException(ExpressionErrorException::class); $translator = new Translator(); $translator->registerExtension(new HtmlExtension($translator)); $xpath = new XPathExpr(); + + $this->expectException(ExpressionErrorException::class); + $translator->addAttributeMatching($xpath, '', '', ''); } From 5cd8af561c3703d6ec71120ef3a3839e59416781 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Mon, 20 Nov 2023 19:32:45 +0100 Subject: [PATCH 23/31] [CssSelector][Serializer][Translation] [Command] Clean unused code --- XPath/XPathExpr.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XPath/XPathExpr.php b/XPath/XPathExpr.php index a76e30b..29f0c8a 100644 --- a/XPath/XPathExpr.php +++ b/XPath/XPathExpr.php @@ -104,7 +104,7 @@ public function join(string $combiner, self $expr): static public function __toString(): string { $path = $this->path.$this->element; - $condition = null === $this->condition || '' === $this->condition ? '' : '['.$this->condition.']'; + $condition = '' === $this->condition ? '' : '['.$this->condition.']'; return $path.$condition; } From 74ca262b973368e25e6178dc263784ddf2e018d6 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 14 Dec 2023 11:03:37 +0100 Subject: [PATCH 24/31] Set `strict` parameter of `in_array` to true where possible --- Parser/Token.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Parser/Token.php b/Parser/Token.php index b50441a..3e926c7 100644 --- a/Parser/Token.php +++ b/Parser/Token.php @@ -72,7 +72,7 @@ public function isDelimiter(array $values = []): bool return true; } - return \in_array($this->value, $values); + return \in_array($this->value, $values, true); } public function isWhitespace(): bool From 3874347e40b1f00c483dc6f02819838b1399049e Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 27 Dec 2023 22:18:42 +0100 Subject: [PATCH 25/31] Fix typo --- Tests/XPath/TranslatorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/XPath/TranslatorTest.php b/Tests/XPath/TranslatorTest.php index c161b80..de15384 100644 --- a/Tests/XPath/TranslatorTest.php +++ b/Tests/XPath/TranslatorTest.php @@ -277,7 +277,7 @@ public static function getHtmlIdsTestData() ['div[foobar~="cd"]', []], ['*[lang|="En"]', ['second-li']], ['[lang|="En-us"]', ['second-li']], - // Attribute values are case sensitive + // Attribute values are case-sensitive ['*[lang|="en"]', []], ['[lang|="en-US"]', []], ['*[lang|="e"]', []], @@ -334,7 +334,7 @@ public static function getHtmlIdsTestData() ['* :root', []], ['*:contains("link")', ['html', 'nil', 'outer-div', 'tag-anchor', 'nofollow-anchor']], [':CONtains("link")', ['html', 'nil', 'outer-div', 'tag-anchor', 'nofollow-anchor']], - ['*:contains("LInk")', []], // case sensitive + ['*:contains("LInk")', []], // case-sensitive ['*:contains("e")', ['html', 'nil', 'outer-div', 'first-ol', 'first-li', 'paragraph', 'p-em']], ['*:contains("E")', []], // case-sensitive ['.a', ['first-ol']], From 5ec03ebb4337064dffc8ca0805b82028c1b4a777 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 2 Jan 2024 14:14:18 +0100 Subject: [PATCH 26/31] [Asset][BrowserKit][Cache][Console][CssSelector] Use CPP --- Node/AttributeNode.php | 20 +++++++------------- Node/ClassNode.php | 11 ++++------- Node/CombinedSelectorNode.php | 14 +++++--------- Node/ElementNode.php | 11 ++++------- Node/FunctionNode.php | 11 +++++------ Node/HashNode.php | 11 ++++------- Node/NegationNode.php | 11 ++++------- Node/PseudoNode.php | 8 ++++---- Node/SelectorNode.php | 8 ++++---- Node/Specificity.php | 14 +++++--------- Parser/Handler/HashHandler.php | 11 ++++------- Parser/Handler/IdentifierHandler.php | 11 ++++------- Parser/Handler/NumberHandler.php | 8 +++----- Parser/Handler/StringHandler.php | 11 ++++------- Parser/Reader.php | 7 +++---- Parser/Token.php | 14 +++++--------- Parser/Tokenizer/TokenizerEscaping.php | 8 +++----- XPath/Extension/NodeExtension.php | 8 +++----- XPath/XPathExpr.php | 16 ++++++---------- 19 files changed, 81 insertions(+), 132 deletions(-) diff --git a/Node/AttributeNode.php b/Node/AttributeNode.php index ba4df31..41b1787 100644 --- a/Node/AttributeNode.php +++ b/Node/AttributeNode.php @@ -23,19 +23,13 @@ */ class AttributeNode extends AbstractNode { - private NodeInterface $selector; - private ?string $namespace; - private string $attribute; - private string $operator; - private ?string $value; - - public function __construct(NodeInterface $selector, ?string $namespace, string $attribute, string $operator, ?string $value) - { - $this->selector = $selector; - $this->namespace = $namespace; - $this->attribute = $attribute; - $this->operator = $operator; - $this->value = $value; + public function __construct( + private NodeInterface $selector, + private ?string $namespace, + private string $attribute, + private string $operator, + private ?string $value, + ) { } public function getSelector(): NodeInterface diff --git a/Node/ClassNode.php b/Node/ClassNode.php index 7174458..2b488c7 100644 --- a/Node/ClassNode.php +++ b/Node/ClassNode.php @@ -23,13 +23,10 @@ */ class ClassNode extends AbstractNode { - private NodeInterface $selector; - private string $name; - - public function __construct(NodeInterface $selector, string $name) - { - $this->selector = $selector; - $this->name = $name; + public function __construct( + private NodeInterface $selector, + private string $name, + ) { } public function getSelector(): NodeInterface diff --git a/Node/CombinedSelectorNode.php b/Node/CombinedSelectorNode.php index 19fecb7..fead5e5 100644 --- a/Node/CombinedSelectorNode.php +++ b/Node/CombinedSelectorNode.php @@ -23,15 +23,11 @@ */ class CombinedSelectorNode extends AbstractNode { - private NodeInterface $selector; - private string $combinator; - private NodeInterface $subSelector; - - public function __construct(NodeInterface $selector, string $combinator, NodeInterface $subSelector) - { - $this->selector = $selector; - $this->combinator = $combinator; - $this->subSelector = $subSelector; + public function __construct( + private NodeInterface $selector, + private string $combinator, + private NodeInterface $subSelector, + ) { } public function getSelector(): NodeInterface diff --git a/Node/ElementNode.php b/Node/ElementNode.php index 39f4d9c..366681f 100644 --- a/Node/ElementNode.php +++ b/Node/ElementNode.php @@ -23,13 +23,10 @@ */ class ElementNode extends AbstractNode { - private ?string $namespace; - private ?string $element; - - public function __construct(string $namespace = null, string $element = null) - { - $this->namespace = $namespace; - $this->element = $element; + public function __construct( + private ?string $namespace = null, + private ?string $element = null, + ) { } public function getNamespace(): ?string diff --git a/Node/FunctionNode.php b/Node/FunctionNode.php index 938a82b..5a04d63 100644 --- a/Node/FunctionNode.php +++ b/Node/FunctionNode.php @@ -25,18 +25,17 @@ */ class FunctionNode extends AbstractNode { - private NodeInterface $selector; private string $name; - private array $arguments; /** * @param Token[] $arguments */ - public function __construct(NodeInterface $selector, string $name, array $arguments = []) - { - $this->selector = $selector; + public function __construct( + private NodeInterface $selector, + string $name, + private array $arguments = [], + ) { $this->name = strtolower($name); - $this->arguments = $arguments; } public function getSelector(): NodeInterface diff --git a/Node/HashNode.php b/Node/HashNode.php index 3af8e84..97e51d6 100644 --- a/Node/HashNode.php +++ b/Node/HashNode.php @@ -23,13 +23,10 @@ */ class HashNode extends AbstractNode { - private NodeInterface $selector; - private string $id; - - public function __construct(NodeInterface $selector, string $id) - { - $this->selector = $selector; - $this->id = $id; + public function __construct( + private NodeInterface $selector, + private string $id, + ) { } public function getSelector(): NodeInterface diff --git a/Node/NegationNode.php b/Node/NegationNode.php index f806758..c4c251d 100644 --- a/Node/NegationNode.php +++ b/Node/NegationNode.php @@ -23,13 +23,10 @@ */ class NegationNode extends AbstractNode { - private NodeInterface $selector; - private NodeInterface $subSelector; - - public function __construct(NodeInterface $selector, NodeInterface $subSelector) - { - $this->selector = $selector; - $this->subSelector = $subSelector; + public function __construct( + private NodeInterface $selector, + private NodeInterface $subSelector, + ) { } public function getSelector(): NodeInterface diff --git a/Node/PseudoNode.php b/Node/PseudoNode.php index c21cd6e..dbf2f85 100644 --- a/Node/PseudoNode.php +++ b/Node/PseudoNode.php @@ -23,12 +23,12 @@ */ class PseudoNode extends AbstractNode { - private NodeInterface $selector; private string $identifier; - public function __construct(NodeInterface $selector, string $identifier) - { - $this->selector = $selector; + public function __construct( + private NodeInterface $selector, + string $identifier, + ) { $this->identifier = strtolower($identifier); } diff --git a/Node/SelectorNode.php b/Node/SelectorNode.php index 0b09ab5..72c9023 100644 --- a/Node/SelectorNode.php +++ b/Node/SelectorNode.php @@ -23,12 +23,12 @@ */ class SelectorNode extends AbstractNode { - private NodeInterface $tree; private ?string $pseudoElement; - public function __construct(NodeInterface $tree, string $pseudoElement = null) - { - $this->tree = $tree; + public function __construct( + private NodeInterface $tree, + string $pseudoElement = null, + ) { $this->pseudoElement = $pseudoElement ? strtolower($pseudoElement) : null; } diff --git a/Node/Specificity.php b/Node/Specificity.php index bb8e5e3..c669a39 100644 --- a/Node/Specificity.php +++ b/Node/Specificity.php @@ -29,15 +29,11 @@ class Specificity public const B_FACTOR = 10; public const C_FACTOR = 1; - private int $a; - private int $b; - private int $c; - - public function __construct(int $a, int $b, int $c) - { - $this->a = $a; - $this->b = $b; - $this->c = $c; + public function __construct( + private int $a, + private int $b, + private int $c, + ) { } public function plus(self $specificity): self diff --git a/Parser/Handler/HashHandler.php b/Parser/Handler/HashHandler.php index b29042f..0be4652 100644 --- a/Parser/Handler/HashHandler.php +++ b/Parser/Handler/HashHandler.php @@ -29,13 +29,10 @@ */ class HashHandler implements HandlerInterface { - private TokenizerPatterns $patterns; - private TokenizerEscaping $escaping; - - public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) - { - $this->patterns = $patterns; - $this->escaping = $escaping; + public function __construct( + private TokenizerPatterns $patterns, + private TokenizerEscaping $escaping, + ) { } public function handle(Reader $reader, TokenStream $stream): bool diff --git a/Parser/Handler/IdentifierHandler.php b/Parser/Handler/IdentifierHandler.php index 25c0761..7e4356b 100644 --- a/Parser/Handler/IdentifierHandler.php +++ b/Parser/Handler/IdentifierHandler.php @@ -29,13 +29,10 @@ */ class IdentifierHandler implements HandlerInterface { - private TokenizerPatterns $patterns; - private TokenizerEscaping $escaping; - - public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) - { - $this->patterns = $patterns; - $this->escaping = $escaping; + public function __construct( + private TokenizerPatterns $patterns, + private TokenizerEscaping $escaping, + ) { } public function handle(Reader $reader, TokenStream $stream): bool diff --git a/Parser/Handler/NumberHandler.php b/Parser/Handler/NumberHandler.php index e3eb7af..38cc9b1 100644 --- a/Parser/Handler/NumberHandler.php +++ b/Parser/Handler/NumberHandler.php @@ -28,11 +28,9 @@ */ class NumberHandler implements HandlerInterface { - private TokenizerPatterns $patterns; - - public function __construct(TokenizerPatterns $patterns) - { - $this->patterns = $patterns; + public function __construct( + private TokenizerPatterns $patterns, + ) { } public function handle(Reader $reader, TokenStream $stream): bool diff --git a/Parser/Handler/StringHandler.php b/Parser/Handler/StringHandler.php index 5fd44df..28bc457 100644 --- a/Parser/Handler/StringHandler.php +++ b/Parser/Handler/StringHandler.php @@ -31,13 +31,10 @@ */ class StringHandler implements HandlerInterface { - private TokenizerPatterns $patterns; - private TokenizerEscaping $escaping; - - public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping) - { - $this->patterns = $patterns; - $this->escaping = $escaping; + public function __construct( + private TokenizerPatterns $patterns, + private TokenizerEscaping $escaping, + ) { } public function handle(Reader $reader, TokenStream $stream): bool diff --git a/Parser/Reader.php b/Parser/Reader.php index a280a48..b68d02f 100644 --- a/Parser/Reader.php +++ b/Parser/Reader.php @@ -23,13 +23,12 @@ */ class Reader { - private string $source; private int $length; private int $position = 0; - public function __construct(string $source) - { - $this->source = $source; + public function __construct( + private string $source, + ) { $this->length = \strlen($source); } diff --git a/Parser/Token.php b/Parser/Token.php index 3e926c7..73f7234 100644 --- a/Parser/Token.php +++ b/Parser/Token.php @@ -31,15 +31,11 @@ class Token public const TYPE_NUMBER = 'number'; public const TYPE_STRING = 'string'; - private ?string $type; - private ?string $value; - private ?int $position; - - public function __construct(?string $type, ?string $value, ?int $position) - { - $this->type = $type; - $this->value = $value; - $this->position = $position; + public function __construct( + private ?string $type, + private ?string $value, + private ?int $position, + ) { } public function getType(): ?int diff --git a/Parser/Tokenizer/TokenizerEscaping.php b/Parser/Tokenizer/TokenizerEscaping.php index 8c4b9f7..bb504e4 100644 --- a/Parser/Tokenizer/TokenizerEscaping.php +++ b/Parser/Tokenizer/TokenizerEscaping.php @@ -23,11 +23,9 @@ */ class TokenizerEscaping { - private TokenizerPatterns $patterns; - - public function __construct(TokenizerPatterns $patterns) - { - $this->patterns = $patterns; + public function __construct( + private TokenizerPatterns $patterns, + ) { } public function escapeUnicode(string $value): string diff --git a/XPath/Extension/NodeExtension.php b/XPath/Extension/NodeExtension.php index 49e894a..f2cd617 100644 --- a/XPath/Extension/NodeExtension.php +++ b/XPath/Extension/NodeExtension.php @@ -31,11 +31,9 @@ class NodeExtension extends AbstractExtension public const ATTRIBUTE_NAME_IN_LOWER_CASE = 2; public const ATTRIBUTE_VALUE_IN_LOWER_CASE = 4; - private int $flags; - - public function __construct(int $flags = 0) - { - $this->flags = $flags; + public function __construct( + private int $flags = 0, + ) { } /** diff --git a/XPath/XPathExpr.php b/XPath/XPathExpr.php index 29f0c8a..14f63ef 100644 --- a/XPath/XPathExpr.php +++ b/XPath/XPathExpr.php @@ -23,16 +23,12 @@ */ class XPathExpr { - private string $path; - private string $element; - private string $condition; - - public function __construct(string $path = '', string $element = '*', string $condition = '', bool $starPrefix = false) - { - $this->path = $path; - $this->element = $element; - $this->condition = $condition; - + public function __construct( + private string $path = '', + private string $element = '*', + private string $condition = '', + bool $starPrefix = false, + ) { if ($starPrefix) { $this->addStarPrefix(); } From ce566cad89416dedb8667dbcea57b8c8551658e6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 3 Feb 2024 18:41:27 +0100 Subject: [PATCH 27/31] Fxi markdown --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c035d6b..d452500 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ CHANGELOG ========= 6.3 ------ +--- * Add support for `:scope` From 8e8616b51c35678d3bd439812f556277e9fa9460 Mon Sep 17 00:00:00 2001 From: "hubert.lenoir" Date: Wed, 28 Dec 2022 09:59:18 +0100 Subject: [PATCH 28/31] [CssSelector] add support for :is() and :where() --- CHANGELOG.md | 8 ++- Node/MatchingNode.php | 55 ++++++++++++++++++++ Node/SpecificityAdjustmentNode.php | 49 +++++++++++++++++ Parser/Parser.php | 44 ++++++++++++---- Tests/Node/MatchingNodeTest.php | 48 +++++++++++++++++ Tests/Node/SpecificityAdjustmentNodeTest.php | 44 ++++++++++++++++ Tests/Parser/ParserTest.php | 17 ++++++ Tests/XPath/TranslatorTest.php | 14 +++++ XPath/Extension/NodeExtension.php | 32 ++++++++++++ XPath/XPathExpr.php | 4 +- 10 files changed, 303 insertions(+), 12 deletions(-) create mode 100644 Node/MatchingNode.php create mode 100644 Node/SpecificityAdjustmentNode.php create mode 100644 Tests/Node/MatchingNodeTest.php create mode 100644 Tests/Node/SpecificityAdjustmentNodeTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index c035d6b..1c8c6cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,14 @@ CHANGELOG ========= +7.1 +--- + +* Add support for `:is()` +* Add support for `:where()` + 6.3 ------ +--- * Add support for `:scope` diff --git a/Node/MatchingNode.php b/Node/MatchingNode.php new file mode 100644 index 0000000..381ac45 --- /dev/null +++ b/Node/MatchingNode.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a ":is()" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Hubert Lenoir + * + * @internal + */ +class MatchingNode extends AbstractNode +{ + /** + * @param array $arguments + */ + public function __construct( + public readonly NodeInterface $selector, + public readonly array $arguments = [], + ) { + } + + public function getSpecificity(): Specificity + { + $argumentsSpecificity = array_reduce( + $this->arguments, + fn ($c, $n) => 1 === $n->getSpecificity()->compareTo($c) ? $n->getSpecificity() : $c, + new Specificity(0, 0, 0), + ); + + return $this->selector->getSpecificity()->plus($argumentsSpecificity); + } + + public function __toString(): string + { + $selectorArguments = array_map( + fn ($n): string => ltrim((string) $n, '*'), + $this->arguments, + ); + + return sprintf('%s[%s:is(%s)]', $this->getNodeName(), $this->selector, implode(', ', $selectorArguments)); + } +} diff --git a/Node/SpecificityAdjustmentNode.php b/Node/SpecificityAdjustmentNode.php new file mode 100644 index 0000000..d49ed4c --- /dev/null +++ b/Node/SpecificityAdjustmentNode.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Node; + +/** + * Represents a ":where()" node. + * + * This component is a port of the Python cssselect library, + * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. + * + * @author Hubert Lenoir + * + * @internal + */ +class SpecificityAdjustmentNode extends AbstractNode +{ + /** + * @param array $arguments + */ + public function __construct( + public readonly NodeInterface $selector, + public readonly array $arguments = [], + ) { + } + + public function getSpecificity(): Specificity + { + return $this->selector->getSpecificity(); + } + + public function __toString(): string + { + $selectorArguments = array_map( + fn ($n) => ltrim((string) $n, '*'), + $this->arguments, + ); + + return sprintf('%s[%s:where(%s)]', $this->getNodeName(), $this->selector, implode(', ', $selectorArguments)); + } +} diff --git a/Parser/Parser.php b/Parser/Parser.php index 5313d34..462714c 100644 --- a/Parser/Parser.php +++ b/Parser/Parser.php @@ -87,13 +87,17 @@ public static function parseSeries(array $tokens): array ]; } - private function parseSelectorList(TokenStream $stream): array + private function parseSelectorList(TokenStream $stream, bool $isArgument = false): array { $stream->skipWhitespace(); $selectors = []; while (true) { - $selectors[] = $this->parserSelectorNode($stream); + if ($isArgument && $stream->getPeek()->isDelimiter([')'])) { + break; + } + + $selectors[] = $this->parserSelectorNode($stream, $isArgument); if ($stream->getPeek()->isDelimiter([','])) { $stream->getNext(); @@ -106,15 +110,19 @@ private function parseSelectorList(TokenStream $stream): array return $selectors; } - private function parserSelectorNode(TokenStream $stream): Node\SelectorNode + private function parserSelectorNode(TokenStream $stream, bool $isArgument = false): Node\SelectorNode { - [$result, $pseudoElement] = $this->parseSimpleSelector($stream); + [$result, $pseudoElement] = $this->parseSimpleSelector($stream, false, $isArgument); while (true) { $stream->skipWhitespace(); $peek = $stream->getPeek(); - if ($peek->isFileEnd() || $peek->isDelimiter([','])) { + if ( + $peek->isFileEnd() + || $peek->isDelimiter([',']) + || ($isArgument && $peek->isDelimiter([')'])) + ) { break; } @@ -129,7 +137,7 @@ private function parserSelectorNode(TokenStream $stream): Node\SelectorNode $combinator = ' '; } - [$nextSelector, $pseudoElement] = $this->parseSimpleSelector($stream); + [$nextSelector, $pseudoElement] = $this->parseSimpleSelector($stream, false, $isArgument); $result = new Node\CombinedSelectorNode($result, $combinator, $nextSelector); } @@ -141,7 +149,7 @@ private function parserSelectorNode(TokenStream $stream): Node\SelectorNode * * @throws SyntaxErrorException */ - private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = false): array + private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = false, bool $isArgument = false): array { $stream->skipWhitespace(); @@ -154,7 +162,7 @@ private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = if ($peek->isWhitespace() || $peek->isFileEnd() || $peek->isDelimiter([',', '+', '>', '~']) - || ($insideNegation && $peek->isDelimiter([')'])) + || ($isArgument && $peek->isDelimiter([')'])) ) { break; } @@ -215,7 +223,7 @@ private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = throw SyntaxErrorException::nestedNot(); } - [$argument, $argumentPseudoElement] = $this->parseSimpleSelector($stream, true); + [$argument, $argumentPseudoElement] = $this->parseSimpleSelector($stream, true, true); $next = $stream->getNext(); if (null !== $argumentPseudoElement) { @@ -227,6 +235,24 @@ private function parseSimpleSelector(TokenStream $stream, bool $insideNegation = } $result = new Node\NegationNode($result, $argument); + } elseif ('is' === strtolower($identifier)) { + $selectors = $this->parseSelectorList($stream, true); + + $next = $stream->getNext(); + if (!$next->isDelimiter([')'])) { + throw SyntaxErrorException::unexpectedToken('")"', $next); + } + + $result = new Node\MatchingNode($result, $selectors); + } elseif ('where' === strtolower($identifier)) { + $selectors = $this->parseSelectorList($stream, true); + + $next = $stream->getNext(); + if (!$next->isDelimiter([')'])) { + throw SyntaxErrorException::unexpectedToken('")"', $next); + } + + $result = new Node\SpecificityAdjustmentNode($result, $selectors); } else { $arguments = []; $next = null; diff --git a/Tests/Node/MatchingNodeTest.php b/Tests/Node/MatchingNodeTest.php new file mode 100644 index 0000000..0bc718c --- /dev/null +++ b/Tests/Node/MatchingNodeTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ClassNode; +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\HashNode; +use Symfony\Component\CssSelector\Node\MatchingNode; + +class MatchingNodeTest extends AbstractNodeTestCase +{ + public static function getToStringConversionTestData() + { + return [ + [new MatchingNode(new ElementNode(), [ + new ClassNode(new ElementNode(), 'class'), + new HashNode(new ElementNode(), 'id'), + ]), 'Matching[Element[*]:is(Class[Element[*].class], Hash[Element[*]#id])]'], + ]; + } + + public static function getSpecificityValueTestData() + { + return [ + [new MatchingNode(new ElementNode(), [ + new ClassNode(new ElementNode(), 'class'), + new HashNode(new ElementNode(), 'id'), + ]), 100], + [new MatchingNode(new ClassNode(new ElementNode(), 'class'), [ + new ClassNode(new ElementNode(), 'class'), + new HashNode(new ElementNode(), 'id'), + ]), 110], + [new MatchingNode(new HashNode(new ElementNode(), 'id'), [ + new ClassNode(new ElementNode(), 'class'), + new HashNode(new ElementNode(), 'id'), + ]), 200], + ]; + } +} diff --git a/Tests/Node/SpecificityAdjustmentNodeTest.php b/Tests/Node/SpecificityAdjustmentNodeTest.php new file mode 100644 index 0000000..5c83057 --- /dev/null +++ b/Tests/Node/SpecificityAdjustmentNodeTest.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\CssSelector\Tests\Node; + +use Symfony\Component\CssSelector\Node\ClassNode; +use Symfony\Component\CssSelector\Node\ElementNode; +use Symfony\Component\CssSelector\Node\HashNode; +use Symfony\Component\CssSelector\Node\SpecificityAdjustmentNode; + +class SpecificityAdjustmentNodeTest extends AbstractNodeTestCase +{ + public static function getToStringConversionTestData() + { + return [ + [new SpecificityAdjustmentNode(new ElementNode(), [ + new ClassNode(new ElementNode(), 'class'), + new HashNode(new ElementNode(), 'id'), + ]), 'SpecificityAdjustment[Element[*]:where(Class[Element[*].class], Hash[Element[*]#id])]'], + ]; + } + + public static function getSpecificityValueTestData() + { + return [ + [new SpecificityAdjustmentNode(new ElementNode(), [ + new ClassNode(new ElementNode(), 'class'), + new HashNode(new ElementNode(), 'id'), + ]), 0], + [new SpecificityAdjustmentNode(new ClassNode(new ElementNode(), 'class'), [ + new ClassNode(new ElementNode(), 'class'), + new HashNode(new ElementNode(), 'id'), + ]), 10], + ]; + } +} diff --git a/Tests/Parser/ParserTest.php b/Tests/Parser/ParserTest.php index a8708ce..509f6e3 100644 --- a/Tests/Parser/ParserTest.php +++ b/Tests/Parser/ParserTest.php @@ -152,6 +152,10 @@ public static function getParserTestData() [':scope', ['Pseudo[Element[*]:scope]']], ['foo bar, :scope > div', ['CombinedSelector[Element[foo] Element[bar]]', 'CombinedSelector[Pseudo[Element[*]:scope] > Element[div]]']], ['foo bar,:scope > div', ['CombinedSelector[Element[foo] Element[bar]]', 'CombinedSelector[Pseudo[Element[*]:scope] > Element[div]]']], + ['div:is(.foo, #bar)', ['Matching[Element[div]:is(Selector[Class[Element[*].foo]], Selector[Hash[Element[*]#bar]])]']], + [':is(:hover, :visited)', ['Matching[Element[*]:is(Selector[Pseudo[Element[*]:hover]], Selector[Pseudo[Element[*]:visited]])]']], + ['div:where(.foo, #bar)', ['SpecificityAdjustment[Element[div]:where(Selector[Class[Element[*].foo]], Selector[Hash[Element[*]#bar]])]']], + [':where(:hover, :visited)', ['SpecificityAdjustment[Element[*]:where(Selector[Pseudo[Element[*]:hover]], Selector[Pseudo[Element[*]:visited]])]']], ]; } @@ -183,6 +187,7 @@ public static function getParserExceptionTestData() [':contains("foo', SyntaxErrorException::unclosedString(10)->getMessage()], ['foo!', SyntaxErrorException::unexpectedToken('selector', new Token(Token::TYPE_DELIMITER, '!', 3))->getMessage()], [':scope > div :scope header', SyntaxErrorException::notAtTheStartOfASelector('scope')->getMessage()], + [':not(:not(a))', SyntaxErrorException::nestedNot()->getMessage()], ]; } @@ -233,6 +238,18 @@ public static function getSpecificityTestData() ['foo::before', 2], ['foo:empty::before', 12], ['#lorem + foo#ipsum:first-child > bar:first-line', 213], + [':is(*)', 0], + [':is(foo)', 1], + [':is(.foo)', 10], + [':is(#foo)', 100], + [':is(#foo, :empty, foo)', 100], + ['#foo:is(#bar:empty)', 210], + [':where(*)', 0], + [':where(foo)', 0], + [':where(.foo)', 0], + [':where(#foo)', 0], + [':where(#foo, :empty, foo)', 0], + ['#foo:where(#bar:empty)', 100], ]; } diff --git a/Tests/XPath/TranslatorTest.php b/Tests/XPath/TranslatorTest.php index bfb9072..55a2b10 100644 --- a/Tests/XPath/TranslatorTest.php +++ b/Tests/XPath/TranslatorTest.php @@ -221,6 +221,8 @@ public static function getCssToXPathTestData() ['div#container p', "div[@id = 'container']/descendant-or-self::*/p"], [':scope > div[dataimg=""]', "*[1]/div[@dataimg = '']"], [':scope', '*[1]'], + ['e:is(section, article) h1', "e[(name() = 'section') or (name() = 'article')]/descendant-or-self::*/h1"], + ['e:where(section, article) h1', "e[(name() = 'section') or (name() = 'article')]/descendant-or-self::*/h1"], ]; } @@ -355,6 +357,17 @@ public static function getHtmlIdsTestData() [':not(*)', []], ['a:not([href])', ['name-anchor']], ['ol :Not(li[class])', ['first-li', 'second-li', 'li-div', 'fifth-li', 'sixth-li', 'seventh-li']], + [':is(#first-li, #second-li)', ['first-li', 'second-li']], + ['a:is(#name-anchor, #tag-anchor)', ['name-anchor', 'tag-anchor']], + [':is(.c)', ['first-ol', 'third-li', 'fourth-li']], + ['a:is(:not(#name-anchor))', ['tag-anchor', 'nofollow-anchor']], + ['a:not(:is(#name-anchor))', ['tag-anchor', 'nofollow-anchor']], + [':where(#first-li, #second-li)', ['first-li', 'second-li']], + ['a:where(#name-anchor, #tag-anchor)', ['name-anchor', 'tag-anchor']], + [':where(.c)', ['first-ol', 'third-li', 'fourth-li']], + ['a:where(:not(#name-anchor))', ['tag-anchor', 'nofollow-anchor']], + ['a:not(:where(#name-anchor))', ['tag-anchor', 'nofollow-anchor']], + ['a:where(:is(#name-anchor), :where(#tag-anchor))', ['name-anchor', 'tag-anchor']], // HTML-specific [':link', ['link-href', 'tag-anchor', 'nofollow-anchor', 'area-href']], [':visited', []], @@ -416,6 +429,7 @@ public static function getHtmlShakespearTestData() [':scope > div', 1], [':scope > div > div[class=dialog]', 1], [':scope > div div', 242], + ['div:is(div#test .dialog) .direction', 4], ]; } } diff --git a/XPath/Extension/NodeExtension.php b/XPath/Extension/NodeExtension.php index 49e894a..174d009 100644 --- a/XPath/Extension/NodeExtension.php +++ b/XPath/Extension/NodeExtension.php @@ -65,6 +65,8 @@ public function getNodeTranslators(): array 'Selector' => $this->translateSelector(...), 'CombinedSelector' => $this->translateCombinedSelector(...), 'Negation' => $this->translateNegation(...), + 'Matching' => $this->translateMatching(...), + 'SpecificityAdjustment' => $this->translateSpecificityAdjustment(...), 'Function' => $this->translateFunction(...), 'Pseudo' => $this->translatePseudo(...), 'Attribute' => $this->translateAttribute(...), @@ -97,6 +99,36 @@ public function translateNegation(Node\NegationNode $node, Translator $translato return $xpath->addCondition('0'); } + public function translateMatching(Node\MatchingNode $node, Translator $translator): XPathExpr + { + $xpath = $translator->nodeToXPath($node->selector); + + foreach ($node->arguments as $argument) { + $expr = $translator->nodeToXPath($argument); + $expr->addNameTest(); + if ($condition = $expr->getCondition()) { + $xpath->addCondition($condition, 'or'); + } + } + + return $xpath; + } + + public function translateSpecificityAdjustment(Node\SpecificityAdjustmentNode $node, Translator $translator): XPathExpr + { + $xpath = $translator->nodeToXPath($node->selector); + + foreach ($node->arguments as $argument) { + $expr = $translator->nodeToXPath($argument); + $expr->addNameTest(); + if ($condition = $expr->getCondition()) { + $xpath->addCondition($condition, 'or'); + } + } + + return $xpath; + } + public function translateFunction(Node\FunctionNode $node, Translator $translator): XPathExpr { $xpath = $translator->nodeToXPath($node->getSelector()); diff --git a/XPath/XPathExpr.php b/XPath/XPathExpr.php index a76e30b..8cde461 100644 --- a/XPath/XPathExpr.php +++ b/XPath/XPathExpr.php @@ -46,9 +46,9 @@ public function getElement(): string /** * @return $this */ - public function addCondition(string $condition): static + public function addCondition(string $condition, string $operator = 'and'): static { - $this->condition = $this->condition ? sprintf('(%s) and (%s)', $this->condition, $condition) : $condition; + $this->condition = $this->condition ? sprintf('(%s) %s (%s)', $this->condition, $operator, $condition) : $condition; return $this; } From f5e2522186b8d2c094a94b3af1ce33afac23f13a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 3 Feb 2024 21:05:29 +0100 Subject: [PATCH 29/31] fix markdown --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c8c6cf..d2b7fb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,8 @@ CHANGELOG 7.1 --- -* Add support for `:is()` -* Add support for `:where()` + * Add support for `:is()` + * Add support for `:where()` 6.3 --- From 84c06b5eaf5c2cefd2dd612aba4df1af05813815 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 20 Jun 2024 17:52:34 +0200 Subject: [PATCH 30/31] Prefix all sprintf() calls --- Exception/SyntaxErrorException.php | 8 ++++---- Node/AttributeNode.php | 4 ++-- Node/ClassNode.php | 2 +- Node/CombinedSelectorNode.php | 2 +- Node/ElementNode.php | 2 +- Node/FunctionNode.php | 2 +- Node/HashNode.php | 2 +- Node/MatchingNode.php | 2 +- Node/NegationNode.php | 2 +- Node/PseudoNode.php | 2 +- Node/SelectorNode.php | 2 +- Node/SpecificityAdjustmentNode.php | 2 +- Parser/Handler/StringHandler.php | 2 +- Parser/Token.php | 4 ++-- Parser/Tokenizer/TokenizerPatterns.php | 2 +- Tests/Parser/ParserTest.php | 4 ++-- XPath/Extension/AttributeMatchingExtension.php | 14 +++++++------- XPath/Extension/FunctionExtension.php | 10 +++++----- XPath/Extension/HtmlExtension.php | 2 +- XPath/Extension/NodeExtension.php | 8 ++++---- XPath/Extension/PseudoClassExtension.php | 2 +- XPath/Translator.php | 16 ++++++++-------- XPath/XPathExpr.php | 2 +- 23 files changed, 49 insertions(+), 49 deletions(-) diff --git a/Exception/SyntaxErrorException.php b/Exception/SyntaxErrorException.php index 5a9d807..52d8259 100644 --- a/Exception/SyntaxErrorException.php +++ b/Exception/SyntaxErrorException.php @@ -25,17 +25,17 @@ class SyntaxErrorException extends ParseException { public static function unexpectedToken(string $expectedValue, Token $foundToken): self { - return new self(sprintf('Expected %s, but %s found.', $expectedValue, $foundToken)); + return new self(\sprintf('Expected %s, but %s found.', $expectedValue, $foundToken)); } public static function pseudoElementFound(string $pseudoElement, string $unexpectedLocation): self { - return new self(sprintf('Unexpected pseudo-element "::%s" found %s.', $pseudoElement, $unexpectedLocation)); + return new self(\sprintf('Unexpected pseudo-element "::%s" found %s.', $pseudoElement, $unexpectedLocation)); } public static function unclosedString(int $position): self { - return new self(sprintf('Unclosed/invalid string at %s.', $position)); + return new self(\sprintf('Unclosed/invalid string at %s.', $position)); } public static function nestedNot(): self @@ -45,7 +45,7 @@ public static function nestedNot(): self public static function notAtTheStartOfASelector(string $pseudoElement): self { - return new self(sprintf('Got immediate child pseudo-element ":%s" not at the start of a selector', $pseudoElement)); + return new self(\sprintf('Got immediate child pseudo-element ":%s" not at the start of a selector', $pseudoElement)); } public static function stringAsFunctionArgument(): self diff --git a/Node/AttributeNode.php b/Node/AttributeNode.php index 41b1787..9bcb3a4 100644 --- a/Node/AttributeNode.php +++ b/Node/AttributeNode.php @@ -67,7 +67,7 @@ public function __toString(): string $attribute = $this->namespace ? $this->namespace.'|'.$this->attribute : $this->attribute; return 'exists' === $this->operator - ? sprintf('%s[%s[%s]]', $this->getNodeName(), $this->selector, $attribute) - : sprintf("%s[%s[%s %s '%s']]", $this->getNodeName(), $this->selector, $attribute, $this->operator, $this->value); + ? \sprintf('%s[%s[%s]]', $this->getNodeName(), $this->selector, $attribute) + : \sprintf("%s[%s[%s %s '%s']]", $this->getNodeName(), $this->selector, $attribute, $this->operator, $this->value); } } diff --git a/Node/ClassNode.php b/Node/ClassNode.php index 2b488c7..e9862c3 100644 --- a/Node/ClassNode.php +++ b/Node/ClassNode.php @@ -46,6 +46,6 @@ public function getSpecificity(): Specificity public function __toString(): string { - return sprintf('%s[%s.%s]', $this->getNodeName(), $this->selector, $this->name); + return \sprintf('%s[%s.%s]', $this->getNodeName(), $this->selector, $this->name); } } diff --git a/Node/CombinedSelectorNode.php b/Node/CombinedSelectorNode.php index fead5e5..78a2fd3 100644 --- a/Node/CombinedSelectorNode.php +++ b/Node/CombinedSelectorNode.php @@ -54,6 +54,6 @@ public function __toString(): string { $combinator = ' ' === $this->combinator ? '' : $this->combinator; - return sprintf('%s[%s %s %s]', $this->getNodeName(), $this->selector, $combinator, $this->subSelector); + return \sprintf('%s[%s %s %s]', $this->getNodeName(), $this->selector, $combinator, $this->subSelector); } } diff --git a/Node/ElementNode.php b/Node/ElementNode.php index 366681f..9bfbd08 100644 --- a/Node/ElementNode.php +++ b/Node/ElementNode.php @@ -48,6 +48,6 @@ public function __toString(): string { $element = $this->element ?: '*'; - return sprintf('%s[%s]', $this->getNodeName(), $this->namespace ? $this->namespace.'|'.$element : $element); + return \sprintf('%s[%s]', $this->getNodeName(), $this->namespace ? $this->namespace.'|'.$element : $element); } } diff --git a/Node/FunctionNode.php b/Node/FunctionNode.php index 5a04d63..de44600 100644 --- a/Node/FunctionNode.php +++ b/Node/FunctionNode.php @@ -65,6 +65,6 @@ public function __toString(): string { $arguments = implode(', ', array_map(fn (Token $token) => "'".$token->getValue()."'", $this->arguments)); - return sprintf('%s[%s:%s(%s)]', $this->getNodeName(), $this->selector, $this->name, $arguments ? '['.$arguments.']' : ''); + return \sprintf('%s[%s:%s(%s)]', $this->getNodeName(), $this->selector, $this->name, $arguments ? '['.$arguments.']' : ''); } } diff --git a/Node/HashNode.php b/Node/HashNode.php index 97e51d6..b3fb3c9 100644 --- a/Node/HashNode.php +++ b/Node/HashNode.php @@ -46,6 +46,6 @@ public function getSpecificity(): Specificity public function __toString(): string { - return sprintf('%s[%s#%s]', $this->getNodeName(), $this->selector, $this->id); + return \sprintf('%s[%s#%s]', $this->getNodeName(), $this->selector, $this->id); } } diff --git a/Node/MatchingNode.php b/Node/MatchingNode.php index 381ac45..9b59503 100644 --- a/Node/MatchingNode.php +++ b/Node/MatchingNode.php @@ -50,6 +50,6 @@ public function __toString(): string $this->arguments, ); - return sprintf('%s[%s:is(%s)]', $this->getNodeName(), $this->selector, implode(', ', $selectorArguments)); + return \sprintf('%s[%s:is(%s)]', $this->getNodeName(), $this->selector, implode(', ', $selectorArguments)); } } diff --git a/Node/NegationNode.php b/Node/NegationNode.php index c4c251d..c14c33d 100644 --- a/Node/NegationNode.php +++ b/Node/NegationNode.php @@ -46,6 +46,6 @@ public function getSpecificity(): Specificity public function __toString(): string { - return sprintf('%s[%s:not(%s)]', $this->getNodeName(), $this->selector, $this->subSelector); + return \sprintf('%s[%s:not(%s)]', $this->getNodeName(), $this->selector, $this->subSelector); } } diff --git a/Node/PseudoNode.php b/Node/PseudoNode.php index dbf2f85..d1082e8 100644 --- a/Node/PseudoNode.php +++ b/Node/PseudoNode.php @@ -49,6 +49,6 @@ public function getSpecificity(): Specificity public function __toString(): string { - return sprintf('%s[%s:%s]', $this->getNodeName(), $this->selector, $this->identifier); + return \sprintf('%s[%s:%s]', $this->getNodeName(), $this->selector, $this->identifier); } } diff --git a/Node/SelectorNode.php b/Node/SelectorNode.php index aebe502..f36e54c 100644 --- a/Node/SelectorNode.php +++ b/Node/SelectorNode.php @@ -49,6 +49,6 @@ public function getSpecificity(): Specificity public function __toString(): string { - return sprintf('%s[%s%s]', $this->getNodeName(), $this->tree, $this->pseudoElement ? '::'.$this->pseudoElement : ''); + return \sprintf('%s[%s%s]', $this->getNodeName(), $this->tree, $this->pseudoElement ? '::'.$this->pseudoElement : ''); } } diff --git a/Node/SpecificityAdjustmentNode.php b/Node/SpecificityAdjustmentNode.php index d49ed4c..58659cc 100644 --- a/Node/SpecificityAdjustmentNode.php +++ b/Node/SpecificityAdjustmentNode.php @@ -44,6 +44,6 @@ public function __toString(): string $this->arguments, ); - return sprintf('%s[%s:where(%s)]', $this->getNodeName(), $this->selector, implode(', ', $selectorArguments)); + return \sprintf('%s[%s:where(%s)]', $this->getNodeName(), $this->selector, implode(', ', $selectorArguments)); } } diff --git a/Parser/Handler/StringHandler.php b/Parser/Handler/StringHandler.php index 28bc457..5e00eda 100644 --- a/Parser/Handler/StringHandler.php +++ b/Parser/Handler/StringHandler.php @@ -49,7 +49,7 @@ public function handle(Reader $reader, TokenStream $stream): bool $match = $reader->findPattern($this->patterns->getQuotedStringPattern($quote)); if (!$match) { - throw new InternalErrorException(sprintf('Should have found at least an empty match at %d.', $reader->getPosition())); + throw new InternalErrorException(\sprintf('Should have found at least an empty match at %d.', $reader->getPosition())); } // check unclosed strings diff --git a/Parser/Token.php b/Parser/Token.php index 73f7234..5bfb8d4 100644 --- a/Parser/Token.php +++ b/Parser/Token.php @@ -99,9 +99,9 @@ public function isString(): bool public function __toString(): string { if ($this->value) { - return sprintf('<%s "%s" at %s>', $this->type, $this->value, $this->position); + return \sprintf('<%s "%s" at %s>', $this->type, $this->value, $this->position); } - return sprintf('<%s at %s>', $this->type, $this->position); + return \sprintf('<%s at %s>', $this->type, $this->position); } } diff --git a/Parser/Tokenizer/TokenizerPatterns.php b/Parser/Tokenizer/TokenizerPatterns.php index 3c77cf0..1825bbf 100644 --- a/Parser/Tokenizer/TokenizerPatterns.php +++ b/Parser/Tokenizer/TokenizerPatterns.php @@ -84,6 +84,6 @@ public function getNumberPattern(): string public function getQuotedStringPattern(string $quote): string { - return '~^'.sprintf($this->quotedStringPattern, $quote).'~i'; + return '~^'.\sprintf($this->quotedStringPattern, $quote).'~i'; } } diff --git a/Tests/Parser/ParserTest.php b/Tests/Parser/ParserTest.php index 509f6e3..82de5ab 100644 --- a/Tests/Parser/ParserTest.php +++ b/Tests/Parser/ParserTest.php @@ -70,7 +70,7 @@ public function testSpecificity($source, $value) public function testParseSeries($series, $a, $b) { $parser = new Parser(); - $selectors = $parser->parse(sprintf(':nth-child(%s)', $series)); + $selectors = $parser->parse(\sprintf(':nth-child(%s)', $series)); $this->assertCount(1, $selectors); /** @var FunctionNode $function */ @@ -82,7 +82,7 @@ public function testParseSeries($series, $a, $b) public function testParseSeriesException($series) { $parser = new Parser(); - $selectors = $parser->parse(sprintf(':nth-child(%s)', $series)); + $selectors = $parser->parse(\sprintf(':nth-child(%s)', $series)); $this->assertCount(1, $selectors); /** @var FunctionNode $function */ diff --git a/XPath/Extension/AttributeMatchingExtension.php b/XPath/Extension/AttributeMatchingExtension.php index 3c785e9..28a16c1 100644 --- a/XPath/Extension/AttributeMatchingExtension.php +++ b/XPath/Extension/AttributeMatchingExtension.php @@ -47,12 +47,12 @@ public function translateExists(XPathExpr $xpath, string $attribute, ?string $va public function translateEquals(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { - return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value))); + return $xpath->addCondition(\sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value))); } public function translateIncludes(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { - return $xpath->addCondition($value ? sprintf( + return $xpath->addCondition($value ? \sprintf( '%1$s and contains(concat(\' \', normalize-space(%1$s), \' \'), %2$s)', $attribute, Translator::getXpathLiteral(' '.$value.' ') @@ -61,7 +61,7 @@ public function translateIncludes(XPathExpr $xpath, string $attribute, ?string $ public function translateDashMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { - return $xpath->addCondition(sprintf( + return $xpath->addCondition(\sprintf( '%1$s and (%1$s = %2$s or starts-with(%1$s, %3$s))', $attribute, Translator::getXpathLiteral($value), @@ -71,7 +71,7 @@ public function translateDashMatch(XPathExpr $xpath, string $attribute, ?string public function translatePrefixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { - return $xpath->addCondition($value ? sprintf( + return $xpath->addCondition($value ? \sprintf( '%1$s and starts-with(%1$s, %2$s)', $attribute, Translator::getXpathLiteral($value) @@ -80,7 +80,7 @@ public function translatePrefixMatch(XPathExpr $xpath, string $attribute, ?strin public function translateSuffixMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { - return $xpath->addCondition($value ? sprintf( + return $xpath->addCondition($value ? \sprintf( '%1$s and substring(%1$s, string-length(%1$s)-%2$s) = %3$s', $attribute, \strlen($value) - 1, @@ -90,7 +90,7 @@ public function translateSuffixMatch(XPathExpr $xpath, string $attribute, ?strin public function translateSubstringMatch(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { - return $xpath->addCondition($value ? sprintf( + return $xpath->addCondition($value ? \sprintf( '%1$s and contains(%1$s, %2$s)', $attribute, Translator::getXpathLiteral($value) @@ -99,7 +99,7 @@ public function translateSubstringMatch(XPathExpr $xpath, string $attribute, ?st public function translateDifferent(XPathExpr $xpath, string $attribute, ?string $value): XPathExpr { - return $xpath->addCondition(sprintf( + return $xpath->addCondition(\sprintf( $value ? 'not(%1$s) or %1$s != %2$s' : '%s != %s', $attribute, Translator::getXpathLiteral($value) diff --git a/XPath/Extension/FunctionExtension.php b/XPath/Extension/FunctionExtension.php index 4b9d7bc..557e305 100644 --- a/XPath/Extension/FunctionExtension.php +++ b/XPath/Extension/FunctionExtension.php @@ -50,7 +50,7 @@ public function translateNthChild(XPathExpr $xpath, FunctionNode $function, bool try { [$a, $b] = Parser::parseSeries($function->getArguments()); } catch (SyntaxErrorException $e) { - throw new ExpressionErrorException(sprintf('Invalid series: "%s".', implode('", "', $function->getArguments())), 0, $e); + throw new ExpressionErrorException(\sprintf('Invalid series: "%s".', implode('", "', $function->getArguments())), 0, $e); } $xpath->addStarPrefix(); @@ -83,10 +83,10 @@ public function translateNthChild(XPathExpr $xpath, FunctionNode $function, bool $expr .= ' - '.$b; } - $conditions = [sprintf('%s %s 0', $expr, $sign)]; + $conditions = [\sprintf('%s %s 0', $expr, $sign)]; if (1 !== $a && -1 !== $a) { - $conditions[] = sprintf('(%s) mod %d = 0', $expr, $a); + $conditions[] = \sprintf('(%s) mod %d = 0', $expr, $a); } return $xpath->addCondition(implode(' and ', $conditions)); @@ -134,7 +134,7 @@ public function translateContains(XPathExpr $xpath, FunctionNode $function): XPa } } - return $xpath->addCondition(sprintf( + return $xpath->addCondition(\sprintf( 'contains(string(.), %s)', Translator::getXpathLiteral($arguments[0]->getValue()) )); @@ -152,7 +152,7 @@ public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathEx } } - return $xpath->addCondition(sprintf( + return $xpath->addCondition(\sprintf( 'lang(%s)', Translator::getXpathLiteral($arguments[0]->getValue()) )); diff --git a/XPath/Extension/HtmlExtension.php b/XPath/Extension/HtmlExtension.php index 4653add..b3bf132 100644 --- a/XPath/Extension/HtmlExtension.php +++ b/XPath/Extension/HtmlExtension.php @@ -142,7 +142,7 @@ public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathEx } } - return $xpath->addCondition(sprintf( + return $xpath->addCondition(\sprintf( 'ancestor-or-self::*[@lang][1][starts-with(concat(' ."translate(@%s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '-')" .', %s)]', diff --git a/XPath/Extension/NodeExtension.php b/XPath/Extension/NodeExtension.php index 87b70df..4cd46fa 100644 --- a/XPath/Extension/NodeExtension.php +++ b/XPath/Extension/NodeExtension.php @@ -91,7 +91,7 @@ public function translateNegation(Node\NegationNode $node, Translator $translato $subXpath->addNameTest(); if ($subXpath->getCondition()) { - return $xpath->addCondition(sprintf('not(%s)', $subXpath->getCondition())); + return $xpath->addCondition(\sprintf('not(%s)', $subXpath->getCondition())); } return $xpath->addCondition('0'); @@ -151,11 +151,11 @@ public function translateAttribute(Node\AttributeNode $node, Translator $transla } if ($node->getNamespace()) { - $name = sprintf('%s:%s', $node->getNamespace(), $name); + $name = \sprintf('%s:%s', $node->getNamespace(), $name); $safe = $safe && $this->isSafeName($node->getNamespace()); } - $attribute = $safe ? '@'.$name : sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name)); + $attribute = $safe ? '@'.$name : \sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name)); $value = $node->getValue(); $xpath = $translator->nodeToXPath($node->getSelector()); @@ -196,7 +196,7 @@ public function translateElement(Node\ElementNode $node): XPathExpr } if ($node->getNamespace()) { - $element = sprintf('%s:%s', $node->getNamespace(), $element); + $element = \sprintf('%s:%s', $node->getNamespace(), $element); $safe = $safe && $this->isSafeName($node->getNamespace()); } diff --git a/XPath/Extension/PseudoClassExtension.php b/XPath/Extension/PseudoClassExtension.php index aada832..397f06f 100644 --- a/XPath/Extension/PseudoClassExtension.php +++ b/XPath/Extension/PseudoClassExtension.php @@ -107,7 +107,7 @@ public function translateOnlyOfType(XPathExpr $xpath): XPathExpr { $element = $xpath->getElement(); - return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element)); + return $xpath->addCondition(\sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element)); } public function translateEmpty(XPathExpr $xpath): XPathExpr diff --git a/XPath/Translator.php b/XPath/Translator.php index 9e66ce7..b2623e5 100644 --- a/XPath/Translator.php +++ b/XPath/Translator.php @@ -75,7 +75,7 @@ public static function getXpathLiteral(string $element): string $parts = []; while (true) { if (false !== $pos = strpos($string, "'")) { - $parts[] = sprintf("'%s'", substr($string, 0, $pos)); + $parts[] = \sprintf("'%s'", substr($string, 0, $pos)); $parts[] = "\"'\""; $string = substr($string, $pos + 1); } else { @@ -84,7 +84,7 @@ public static function getXpathLiteral(string $element): string } } - return sprintf('concat(%s)', implode(', ', $parts)); + return \sprintf('concat(%s)', implode(', ', $parts)); } public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string @@ -130,7 +130,7 @@ public function registerExtension(Extension\ExtensionInterface $extension): stat public function getExtension(string $name): Extension\ExtensionInterface { if (!isset($this->extensions[$name])) { - throw new ExpressionErrorException(sprintf('Extension "%s" not registered.', $name)); + throw new ExpressionErrorException(\sprintf('Extension "%s" not registered.', $name)); } return $this->extensions[$name]; @@ -152,7 +152,7 @@ public function registerParserShortcut(ParserInterface $shortcut): static public function nodeToXPath(NodeInterface $node): XPathExpr { if (!isset($this->nodeTranslators[$node->getNodeName()])) { - throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName())); + throw new ExpressionErrorException(\sprintf('Node "%s" not supported.', $node->getNodeName())); } return $this->nodeTranslators[$node->getNodeName()]($node, $this); @@ -164,7 +164,7 @@ public function nodeToXPath(NodeInterface $node): XPathExpr public function addCombination(string $combiner, NodeInterface $xpath, NodeInterface $combinedXpath): XPathExpr { if (!isset($this->combinationTranslators[$combiner])) { - throw new ExpressionErrorException(sprintf('Combiner "%s" not supported.', $combiner)); + throw new ExpressionErrorException(\sprintf('Combiner "%s" not supported.', $combiner)); } return $this->combinationTranslators[$combiner]($this->nodeToXPath($xpath), $this->nodeToXPath($combinedXpath)); @@ -176,7 +176,7 @@ public function addCombination(string $combiner, NodeInterface $xpath, NodeInter public function addFunction(XPathExpr $xpath, FunctionNode $function): XPathExpr { if (!isset($this->functionTranslators[$function->getName()])) { - throw new ExpressionErrorException(sprintf('Function "%s" not supported.', $function->getName())); + throw new ExpressionErrorException(\sprintf('Function "%s" not supported.', $function->getName())); } return $this->functionTranslators[$function->getName()]($xpath, $function); @@ -188,7 +188,7 @@ public function addFunction(XPathExpr $xpath, FunctionNode $function): XPathExpr public function addPseudoClass(XPathExpr $xpath, string $pseudoClass): XPathExpr { if (!isset($this->pseudoClassTranslators[$pseudoClass])) { - throw new ExpressionErrorException(sprintf('Pseudo-class "%s" not supported.', $pseudoClass)); + throw new ExpressionErrorException(\sprintf('Pseudo-class "%s" not supported.', $pseudoClass)); } return $this->pseudoClassTranslators[$pseudoClass]($xpath); @@ -200,7 +200,7 @@ public function addPseudoClass(XPathExpr $xpath, string $pseudoClass): XPathExpr public function addAttributeMatching(XPathExpr $xpath, string $operator, string $attribute, ?string $value): XPathExpr { if (!isset($this->attributeMatchingTranslators[$operator])) { - throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator)); + throw new ExpressionErrorException(\sprintf('Attribute matcher operator "%s" not supported.', $operator)); } return $this->attributeMatchingTranslators[$operator]($xpath, $attribute, $value); diff --git a/XPath/XPathExpr.php b/XPath/XPathExpr.php index ceccab6..a148feb 100644 --- a/XPath/XPathExpr.php +++ b/XPath/XPathExpr.php @@ -44,7 +44,7 @@ public function getElement(): string */ public function addCondition(string $condition, string $operator = 'and'): static { - $this->condition = $this->condition ? sprintf('(%s) %s (%s)', $this->condition, $operator, $condition) : $condition; + $this->condition = $this->condition ? \sprintf('(%s) %s (%s)', $this->condition, $operator, $condition) : $condition; return $this; } From 8198536854262560fce7695308daf7fb1217a535 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 6 Jul 2024 09:57:16 +0200 Subject: [PATCH 31/31] Update .gitattributes --- .gitattributes | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 84c7add..14c3c35 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,3 @@ /Tests export-ignore /phpunit.xml.dist export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore +/.git* export-ignore