Skip to content

Commit

Permalink
CssXPath-StarSelector
Browse files Browse the repository at this point in the history
Add support for selectors such as `a[href*="example"]`
https://developer.mozilla.org/docs/Web/CSS/Attribute_selectors
Translated to `.//a[contains(@href,"example")]`

Upstream PR: PhpGt/CssXPath#181
  • Loading branch information
Alkarex committed Aug 17, 2022
1 parent e27eb1c commit aa3df0a
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 16 deletions.
4 changes: 1 addition & 3 deletions lib/.gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
autoload.php
composer.lock
composer/
phpgt/cssxpath/.github/
phpgt/cssxpath/.gitignore
phpgt/cssxpath/.scrutinizer.yml
phpgt/cssxpath/.*
phpgt/cssxpath/composer.json
phpgt/cssxpath/CONTRIBUTING.md
phpgt/cssxpath/test/
Expand Down
8 changes: 7 additions & 1 deletion lib/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@
"type": "project",
"homepage": "https://freshrss.org/",
"license": "AGPL-3.0",
"repositories": [
{
"type": "git",
"url": "https://github.com/Alkarex/CssXPath.git"
}
],
"require": {
"php": ">=7.0.0",
"phpgt/cssxpath": "v1.1.4",
"phpgt/cssxpath": "dev-master",
"phpmailer/phpmailer": "6.6.0"
},
"config": {
Expand Down
4 changes: 2 additions & 2 deletions lib/phpgt/cssxpath/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Translate CSS selectors to XPath queries.
=========================================
Translate CSS selectors to XPath queries
========================================

A lightweight and dependency free CSS to XPath translator. This repository is used to bring modern DOM functionality like [`querySelectorAll()`][qsa] to PHP in the [PHP.Gt/Dom][gt-dom] project.

Expand Down
2 changes: 1 addition & 1 deletion lib/phpgt/cssxpath/src/CssXPathException.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

use RuntimeException;

class CssXPathException extends RuntimeException {}
class CssXPathException extends RuntimeException {}
2 changes: 1 addition & 1 deletion lib/phpgt/cssxpath/src/NotYetImplementedException.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
namespace Gt\CssXPath;

class NotYetImplementedException extends CssXPathException {}
class NotYetImplementedException extends CssXPathException {}
25 changes: 17 additions & 8 deletions lib/phpgt/cssxpath/src/Translator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Translator {
. '|(#(?P<id>[\w-]*))'
. '|(\.(?P<class>[\w-]*))'
. '|(?P<sibling>\s*\+\s*)'
. "|(\[(?P<attribute>[\w-]*)((?P<attribute_equals>[=~$]+)(?P<attribute_value>(.+\[\]'?)|[^\]]+))*\])+"
. "|(\[(?P<attribute>[\w-]*)((?P<attribute_equals>[=~$*]+)(?P<attribute_value>(.+\[\]'?)|[^\]]+))*\])+"
. '|(?P<descendant>\s+)'
. '/';

Expand Down Expand Up @@ -61,7 +61,7 @@ protected function convertSingleSelector(string $css):string {
$thread = array_values($thread);

$xpath = [$this->prefix];
$prevType = "";
$hasElement = false;
foreach($thread as $threadKey => $currentThreadItem) {
$next = isset($thread[$threadKey + 1])
? $thread[$threadKey + 1]
Expand All @@ -71,6 +71,7 @@ protected function convertSingleSelector(string $css):string {
case "star":
case "element":
$xpath []= $currentThreadItem['content'];
$hasElement = true;
break;

case "pseudo":
Expand Down Expand Up @@ -160,35 +161,40 @@ protected function convertSingleSelector(string $css):string {

case "child":
array_push($xpath, "/");
$hasElement = false;
break;

case "id":
array_push(
$xpath,
($prevType != "element" ? '*' : '')
($hasElement ? '' : '*')
. "[@id='{$currentThreadItem['content']}']"
);
$hasElement = true;
break;

case "class":
// https://devhints.io/xpath#class-check
array_push(
$xpath,
(($prevType != "element" && $prevType != "class") ? '*' : '')
($hasElement ? '' : '*')
. "[contains(concat(' ',normalize-space(@class),' '),' {$currentThreadItem['content']} ')]"
);
$hasElement = true;
break;

case "sibling":
array_push(
$xpath,
"/following-sibling::*[1]/self::"
);
$hasElement = false;
break;

case "attribute":
if(!$prevType) {
if(!$hasElement) {
array_push($xpath, "*");
$hasElement = true;
}

/** @var null|array<int, array<string, string>> $detail */
Expand Down Expand Up @@ -220,7 +226,11 @@ protected function convertSingleSelector(string $css):string {
break;

case self::EQUALS_CONTAINS:
throw new NotYetImplementedException();
array_push(
$xpath,
"[contains(@{$currentThreadItem['content']},\"{$valueString}\")]"
);
break;

case self::EQUALS_CONTAINS_WORD:
array_push(
Expand Down Expand Up @@ -257,10 +267,9 @@ protected function convertSingleSelector(string $css):string {

case "descendant":
array_push($xpath, "//");
$hasElement = false;
break;
}

$prevType = $currentThreadItem["type"];
}

return implode("", $xpath);
Expand Down

0 comments on commit aa3df0a

Please sign in to comment.