Skip to content

Commit

Permalink
Add support of multiple pseudoclasses
Browse files Browse the repository at this point in the history
  • Loading branch information
Imangazaliev committed Jul 26, 2021
1 parent dda9476 commit 8f5200d
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 16 deletions.
29 changes: 18 additions & 11 deletions src/DiDom/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -292,13 +292,15 @@ public static function buildXpath(array $segments, $prefix = '//')
}

// if the pseudo class specified
if (isset($segments['pseudo'])) {
$expression = isset($segments['pseudo-expression']) ? trim($segments['pseudo-expression']) : '';
if (array_key_exists('pseudo', $segments)) {
foreach ($segments['pseudo'] as $pseudo) {
$expression = $pseudo['expression'] !== null ? $pseudo['expression'] : '';

$parameters = explode(',', $expression);
$parameters = array_map('trim', $parameters);
$parameters = explode(',', $expression);
$parameters = array_map('trim', $parameters);

$attributes[] = self::convertPseudo($segments['pseudo'], $tagName, $parameters);
$attributes[] = self::convertPseudo($pseudo['type'], $tagName, $parameters);
}
}

if (count($attributes) === 0 && ! isset($segments['tag'])) {
Expand Down Expand Up @@ -511,10 +513,15 @@ public static function getSegments($selector)

// if the pseudo class specified
if (isset($segments['pseudo']) && $segments['pseudo'] !== '') {
$result['pseudo'] = $segments['pseudo'];
preg_match_all('/:(?P<type>[\w\-]+)(?:\((?P<expr>[^\)]+)\))?/', $segments['pseudo'], $pseudoClasses);

$result['pseudo'] = [];

if (isset($segments['pseudoExpr']) && $segments['pseudoExpr'] !== '') {
$result['pseudo-expression'] = $segments['pseudoExpr'];
foreach ($pseudoClasses['type'] as $index => $pseudoType) {
$result['pseudo'][] = [
'type' => $pseudoType,
'expression' => $pseudoClasses['expr'][$index] !== '' ? $pseudoClasses['expr'][$index] : null,
];
}
}

Expand All @@ -532,9 +539,9 @@ private static function getSelectorRegex()
$id = '(?:#(?P<id>[\w|\-]+))?';
$classes = '(?P<classes>\.[\w|\-|\.]+)*';
$attrs = '(?P<attrs>(?:\[.+?\])*)?';
$pseudoType = '(?P<pseudo>[\w\-]+)';
$pseudoExpr = '(?:\((?P<pseudoExpr>[^\)]+)\))';
$pseudo = '(?::' . $pseudoType . $pseudoExpr . '?)?';
$pseudoType = '[\w\-]+';
$pseudoExpr = '(?:\([^\)]+\))?';
$pseudo = '(?P<pseudo>(?::' . $pseudoType . $pseudoExpr . ')+)?';
$rel = '\s*(?P<rel>>)?';

return '/' . $tag . $id . $classes . $attrs . $pseudo . $rel . '/is';
Expand Down
11 changes: 6 additions & 5 deletions tests/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ public function compileCssTests()
['li:first-child', '//li[position() = 1]'],
['li:last-child', '//li[position() = last()]'],
['*:not(a[href*="example.com"])', '//*[not(self::a[contains(@href, "example.com")])]'],
['*:not(a[href*="example.com"]):not(.foo)', '//*[(not(self::a[contains(@href, "example.com")])) and (not(self::*[contains(concat(" ", normalize-space(@class), " "), " foo ")]))]'],
['ul:empty', '//ul[count(descendant::*) = 0]'],
['ul:not-empty', '//ul[count(descendant::*) > 0]'],
['li:nth-child(odd)', '//*[(name()="li") and (position() mod 2 = 1 and position() >= 1)]'],
Expand Down Expand Up @@ -388,8 +389,8 @@ public function buildXpathTests()
['tag' => 'a', 'attributes' => ['href*' => 'example']],
['tag' => 'a', 'attributes' => ['href!' => 'http://foo.com/']],
['tag' => 'script', 'attributes' => ['!src' => null]],
['tag' => 'li', 'pseudo' => 'first-child'],
['tag' => '*', 'id' => 'id', 'classes' => ['foo'], 'attributes' => ['name' => 'value'], 'pseudo' => 'first-child', 'rel' => '>'],
['tag' => 'li', 'pseudo' => [['type' => 'first-child', 'expression' => null]]],
['tag' => '*', 'id' => 'id', 'classes' => ['foo'], 'attributes' => ['name' => 'value'], 'pseudo' => [['type' => 'first-child', 'expression' => null]], 'rel' => '>'],
];

$parameters = [];
Expand All @@ -416,10 +417,10 @@ public function getSegmentsTests()
['selector' => 'a[href=http://example.com/][title=Example Domain]', 'tag' => 'a', 'attributes' => ['href' => 'http://example.com/', 'title' => 'Example Domain']],
['selector' => 'a[href=http://example.com/][href=http://example.com/404]', 'tag' => 'a', 'attributes' => ['href' => 'http://example.com/404']],
['selector' => 'a[href^=https]', 'tag' => 'a', 'attributes' => ['href^' => 'https']],
['selector' => 'li:first-child', 'tag' => 'li', 'pseudo' => 'first-child'],
['selector' => 'li:first-child', 'tag' => 'li', 'pseudo' => [['type' => 'first-child', 'expression' => null]]],
['selector' => 'ul >', 'tag' => 'ul', 'rel' => '>'],
['selector' => '#id.foo[name=value]:first-child >', 'id' => 'id', 'classes' => ['foo'], 'attributes' => ['name' => 'value'], 'pseudo' => 'first-child', 'rel' => '>'],
['selector' => 'li.bar:nth-child(2n)', 'tag' => 'li', 'classes' => ['bar'], 'pseudo' => 'nth-child', 'pseudo-expression' => '2n'],
['selector' => '#id.foo[name=value]:first-child >', 'id' => 'id', 'classes' => ['foo'], 'attributes' => ['name' => 'value'], 'pseudo' => [['type' => 'first-child', 'expression' => null]], 'rel' => '>'],
['selector' => 'li.bar:nth-child(2n)', 'tag' => 'li', 'classes' => ['bar'], 'pseudo' => [['type' => 'nth-child', 'expression' => '2n']]],
];

$parameters = [];
Expand Down

0 comments on commit 8f5200d

Please sign in to comment.