Skip to content

Commit

Permalink
Small fixes. Add nth-of-type pseudo class.
Browse files Browse the repository at this point in the history
  • Loading branch information
Imangazaliev committed Jul 27, 2016
1 parent 656263c commit f4b976c
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 15 deletions.
20 changes: 14 additions & 6 deletions src/DiDom/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ protected static function convertProperty($name, $args = [])
*/
public static function buildXpath($segments, $prefix = '//')
{
$tagName = isset($segments['tag']) ? $segments['tag'] : '*';

$attributes = array();

// if the id attribute specified
Expand All @@ -179,14 +181,13 @@ public static function buildXpath($segments, $prefix = '//')
// if the pseudo class specified
if (isset($segments['pseudo'])) {
$expression = isset($segments['expr']) ? trim($segments['expr']) : '';
$attributes[] = self::convertPseudo($segments['pseudo'], explode(',', $expression));
$attributes[] = self::convertPseudo($segments['pseudo'], explode(',', $expression), $tagName);
}

if (count($attributes) === 0 and !isset($segments['tag'])) {
throw new InvalidArgumentException('The array of segments should contain the name of the tag or at least one attribute');
}

$tagName = isset($segments['tag']) ? $segments['tag'] : '*';
$xpath = $prefix.$tagName;

if ($count = count($attributes)) {
Expand Down Expand Up @@ -243,12 +244,13 @@ protected static function convertAttribute($name, $value)
*
* @param string $pseudo Pseudo-class
* @param string $parameters
* @param string $tagName
*
* @return string
*
* @throws \RuntimeException if passed an unknown pseudo-class
*/
protected static function convertPseudo($pseudo, $parameters = [])
protected static function convertPseudo($pseudo, $parameters = [], &$tagName)
{
switch ($pseudo) {
case 'first-child':
Expand All @@ -264,7 +266,10 @@ protected static function convertPseudo($pseudo, $parameters = [])
return 'count(descendant::*) > 0';
break;
case 'nth-child':
return self::convertNthChildExpression($parameters[0]);
$xpath = sprintf('name()="%s"][%s', $tagName, self::convertNthExpression($parameters[0]));
$tagName = '*';

return $xpath;
break;
case 'contains':
$string = trim($parameters[0], ' \'"');
Expand All @@ -275,13 +280,16 @@ protected static function convertPseudo($pseudo, $parameters = [])
case 'has':
return self::cssToXpath($parameters[0], './/');
break;
case 'nth-of-type':
return self::convertNthExpression($parameters[0]);
break;
}

throw new RuntimeException('Invalid selector: unknown pseudo-class');
}

/**
* Converts nth-child expression into an XPath expression.
* Converts nth-expression into an XPath expression.
*
* @param string $expression nth-expression
*
Expand All @@ -290,7 +298,7 @@ protected static function convertPseudo($pseudo, $parameters = [])
* @throws \RuntimeException if passed nth-child is empty
* @throws \RuntimeException if passed an unknown nth-child expression
*/
protected static function convertNthChildExpression($expression)
protected static function convertNthExpression($expression)
{
if ($expression === '') {
throw new RuntimeException('Invalid selector: nth-child expression must not be empty');
Expand Down
27 changes: 18 additions & 9 deletions tests/DiDom/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,24 @@ public function compileCssTests()
['li:last-child', '//li[position() = last()]'],
['ul:empty', '//ul[count(descendant::*) = 0]'],
['ul:not-empty', '//ul[count(descendant::*) > 0]'],
['li:nth-child(odd)', '//li[(position() -1) mod 2 = 0 and position() >= 1]'],
['li:nth-child(even)', '//li[position() mod 2 = 0 and position() >= 0]'],
['li:nth-child(3)', '//li[position() = 3]'],
['li:nth-child(-3)', '//li[position() = -3]'],
['li:nth-child(3n)', '//li[(position() + 0) mod 3 = 0 and position() >= 0]'],
['li:nth-child(3n+1)', '//li[(position() - 1) mod 3 = 0 and position() >= 1]'],
['li:nth-child(3n-1)', '//li[(position() + 1) mod 3 = 0 and position() >= 1]'],
['li:nth-child(n+3)', '//li[(position() - 3) mod 1 = 0 and position() >= 3]'],
['li:nth-child(n-3)', '//li[(position() + 3) mod 1 = 0 and position() >= 3]'],
['li:nth-child(odd)', '//*[name()="li"][(position() -1) mod 2 = 0 and position() >= 1]'],
['li:nth-child(even)', '//*[name()="li"][position() mod 2 = 0 and position() >= 0]'],
['li:nth-child(3)', '//*[name()="li"][position() = 3]'],
['li:nth-child(-3)', '//*[name()="li"][position() = -3]'],
['li:nth-child(3n)', '//*[name()="li"][(position() + 0) mod 3 = 0 and position() >= 0]'],
['li:nth-child(3n+1)', '//*[name()="li"][(position() - 1) mod 3 = 0 and position() >= 1]'],
['li:nth-child(3n-1)', '//*[name()="li"][(position() + 1) mod 3 = 0 and position() >= 1]'],
['li:nth-child(n+3)', '//*[name()="li"][(position() - 3) mod 1 = 0 and position() >= 3]'],
['li:nth-child(n-3)', '//*[name()="li"][(position() + 3) mod 1 = 0 and position() >= 3]'],
['li:nth-of-type(odd)', '//li[(position() -1) mod 2 = 0 and position() >= 1]'],
['li:nth-of-type(even)', '//li[position() mod 2 = 0 and position() >= 0]'],
['li:nth-of-type(3)', '//li[position() = 3]'],
['li:nth-of-type(-3)', '//li[position() = -3]'],
['li:nth-of-type(3n)', '//li[(position() + 0) mod 3 = 0 and position() >= 0]'],
['li:nth-of-type(3n+1)', '//li[(position() - 1) mod 3 = 0 and position() >= 1]'],
['li:nth-of-type(3n-1)', '//li[(position() + 1) mod 3 = 0 and position() >= 1]'],
['li:nth-of-type(n+3)', '//li[(position() - 3) mod 1 = 0 and position() >= 3]'],
['li:nth-of-type(n-3)', '//li[(position() + 3) mod 1 = 0 and position() >= 3]'],
['ul:has(li.item)', '//ul[.//li[contains(concat(" ", normalize-space(@class), " "), " item ")]]'],
['ul li a::text', '//ul//li//a/text()'],
['ul li a::attr(href)', '//ul//li//a/@*[name() = "href"]'],
Expand Down

0 comments on commit f4b976c

Please sign in to comment.