From 9f4f6e9cac6c20257ff496c28a02dd9463c6cdcb Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 6 Sep 2024 09:11:37 +0200 Subject: Bump phpgt/cssxpath (#6618) Includes https://github.com/PhpGt/CssXPath/pull/227 Follow-up of https://github.com/PhpGt/CssXPath/pull/227 for PHP 8.4 Requires PHP 8.0+ Full diff https://github.com/PhpGt/CssXPath/compare/d99d35f7194bac19fb3f8726b70c1bc83de3e931...45f3ac151fc21d459e2515c3aff97cd4bf877bf8 --- lib/composer.json | 2 +- lib/phpgt/cssxpath/README.md | 14 +++++- lib/phpgt/cssxpath/src/Translator.php | 91 +++++++++++++++++++++++++++++------ 3 files changed, 89 insertions(+), 18 deletions(-) diff --git a/lib/composer.json b/lib/composer.json index a0ff7bdb4..c1b3da704 100644 --- a/lib/composer.json +++ b/lib/composer.json @@ -12,7 +12,7 @@ ], "require": { "marienfressinaud/lib_opml": "0.5.1", - "phpgt/cssxpath": "dev-master#d99d35f7194bac19fb3f8726b70c1bc83de3e931", + "phpgt/cssxpath": "dev-master#45f3ac151fc21d459e2515c3aff97cd4bf877bf8", "phpmailer/phpmailer": "6.9.1" }, "config": { diff --git a/lib/phpgt/cssxpath/README.md b/lib/phpgt/cssxpath/README.md index 8dfeabc42..048547442 100644 --- a/lib/phpgt/cssxpath/README.md +++ b/lib/phpgt/cssxpath/README.md @@ -8,10 +8,10 @@ A lightweight and dependency free CSS to XPath translator. This repository is us Build status - + Code quality - + Code coverage @@ -49,5 +49,15 @@ $xpath = new DOMXPath($document); $inputElementList = $xpath->query(new Translator("form>label>input"); ``` +## Using this library with XML Documents + +To correctly work with XML documents, where the attributes are case-sensitive, pass `false` to the `htmlMode` property of the constructor. + +```php +$translator = new Translator("[data-FOO='bar']", htmlMode: false); +``` + +It's perhaps worth noting that for XML-style matching to work, you must load the document content with DOMDocument->load/DOMDocument->loadXML instead of DOMDocument->loadHTMLFile/DOMDocument->loadHTML, as the HTML loading methods automatically convert the tags and attribute names to lowercase. This is handled automatically when using [PHP.Gt/Dom][gt-dom]. + [qsa]: https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll [gt-dom]: https://www.php.gt/dom diff --git a/lib/phpgt/cssxpath/src/Translator.php b/lib/phpgt/cssxpath/src/Translator.php index 01ed98bc6..7bb11265c 100644 --- a/lib/phpgt/cssxpath/src/Translator.php +++ b/lib/phpgt/cssxpath/src/Translator.php @@ -12,7 +12,7 @@ class Translator { . '|(#(?P[\w-]*))' . '|(\.(?P[\w-]*))' . '|(?P\s*\+\s*)' - . "|(\[(?P[\w-]*)((?P[=~$*]+)(?P(.+\[\]'?)|[^\]]+))*\])+" + . "|(\[(?P[\w-]*)((?P[=~$|^*]+)(?P(.+\[\]'?)|[^\]]+))*\])+" . '|(?P\s+)' . '/'; @@ -20,17 +20,14 @@ class Translator { const EQUALS_CONTAINS_WORD = "~="; const EQUALS_ENDS_WITH = "$="; const EQUALS_CONTAINS = "*="; - const EQUALS_STARTS_WITH_OR_STARTS_WITH_HYPHENATED = "|="; + const EQUALS_OR_STARTS_WITH_HYPHENATED = "|="; const EQUALS_STARTS_WITH = "^="; - /** @var string */ - protected $cssSelector; - /** @var string */ - protected $prefix; - - public function __construct(string $cssSelector, string $prefix = ".//") { - $this->cssSelector = $cssSelector; - $this->prefix = $prefix; + public function __construct( + protected string $cssSelector, + protected string $prefix = ".//", + protected bool $htmlMode = true + ) { } public function __toString():string { @@ -70,7 +67,11 @@ class Translator { switch ($currentThreadItem["type"]) { case "star": case "element": - $xpath []= $currentThreadItem['content']; + if($this->htmlMode) { + $xpath []= strtolower($currentThreadItem['content']); + } else { + $xpath []= $currentThreadItem['content']; + } $hasElement = true; break; @@ -135,6 +136,30 @@ class Translator { ); } break; + + case "last-child": + $prev = count($xpath) - 1; + $xpath[$prev] = '*[last()]/self::' . $xpath[$prev]; + break; + + case 'first-of-type': + $prev = count($xpath) - 1; + $previous = $xpath[$prev]; + + if(substr($previous, -1, 1) === "]") { + array_push( + $xpath, + "[1]" + ); + } + else { + array_push( + $xpath, + "[1]" + ); + } + break; + case "nth-of-type": if (empty($specifier)) { continue 3; @@ -156,6 +181,25 @@ class Translator { ); } break; + + case "last-of-type": + $prev = count($xpath) - 1; + $previous = $xpath[$prev]; + + if(substr($previous, -1, 1) === "]") { + array_push( + $xpath, + "[last()]" + ); + } + else { + array_push( + $xpath, + "[last()]" + ); + } + break; + } break; @@ -197,6 +241,10 @@ class Translator { $hasElement = true; } + if($this->htmlMode) { + $currentThreadItem['content'] = strtolower($currentThreadItem['content']); + } + /** @var null|array> $detail */ $detail = $currentThreadItem["detail"] ?? null; $detailType = $detail[0] ?? null; @@ -244,11 +292,24 @@ class Translator { ); break; - case self::EQUALS_STARTS_WITH_OR_STARTS_WITH_HYPHENATED: - throw new NotYetImplementedException(); + case self::EQUALS_OR_STARTS_WITH_HYPHENATED: + array_push( + $xpath, + "[" + . "@{$currentThreadItem['content']}=\"{$valueString}\" or " + . "starts-with(@{$currentThreadItem['content']}, \"{$valueString}-\")" + . "]" + ); + break; case self::EQUALS_STARTS_WITH: - throw new NotYetImplementedException(); + array_push( + $xpath, + "[starts-with(" + . "@{$currentThreadItem['content']}, \"{$valueString}\"" + . ")]" + ); + break; case self::EQUALS_ENDS_WITH: array_push( @@ -279,7 +340,7 @@ class Translator { protected function preg_match_collated( string $regex, string $string, - callable $transform = null + ?callable $transform = null ):array { preg_match_all( $regex, -- cgit v1.2.3