Skip to content

Commit

Permalink
Merge pull request minkphp#720 from klausi/xpath-manipulator-union
Browse files Browse the repository at this point in the history
Allow pipes (|) in attributes by implementing better XPath parsing
  • Loading branch information
stof authored Oct 14, 2016
2 parents 64e3a4b + 4846634 commit 39e3309
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 5 deletions.
42 changes: 41 additions & 1 deletion src/Selector/Xpath/Manipulator.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function prepend($xpath, $prefix)
}

// Split any unions into individual expressions.
foreach (preg_split(self::UNION_PATTERN, $xpath) as $expression) {
foreach ($this->splitUnionParts($xpath) as $expression) {
$expression = trim($expression);
$parenthesis = '';

Expand All @@ -66,4 +66,44 @@ public function prepend($xpath, $prefix)

return implode(' | ', $expressions);
}

/**
* Splits the XPath into parts that are separated by the union operator.
*
* @param string $xpath
*
* @return string[]
*/
private function splitUnionParts($xpath)
{
// Split any unions into individual expressions. We need to iterate
// through the string to correctly parse opening/closing quotes and
// braces which is not possible with regular expressions.
$unionParts = array();
$inSingleQuotedString = false;
$inDoubleQuotedString = false;
$openedBrackets = 0;
$lastUnion = 0;
for ($i = 0; $i < strlen($xpath); $i++) {
$char = $xpath[$i];

if ($char === "'" && !$inDoubleQuotedString) {
$inSingleQuotedString = !$inSingleQuotedString;
} elseif ($char === '"' && !$inSingleQuotedString) {
$inDoubleQuotedString = !$inDoubleQuotedString;
} elseif (!$inSingleQuotedString && !$inDoubleQuotedString) {
if ($char === '[') {
$openedBrackets++;
} elseif ($char === ']') {
$openedBrackets--;
} elseif ($char === '|' && $openedBrackets === 0) {
$unionParts[] = substr($xpath, $lastUnion, $i - $lastUnion);
$lastUnion = $i + 1;
}
}
}
$unionParts[] = substr($xpath, $lastUnion);
return $unionParts;
}

}
4 changes: 2 additions & 2 deletions tests/Element/NodeElementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -553,8 +553,8 @@ public function testSubmitForm()
public function testFindAllUnion()
{
$node = new NodeElement('some_xpath', $this->session);
$xpath = "some_tag1 | some_tag2[@foo =\n 'bar|'']\n | some_tag3[foo | bar]";
$expected = "some_xpath/some_tag1 | some_xpath/some_tag2[@foo =\n 'bar|''] | some_xpath/some_tag3[foo | bar]";
$xpath = "some_tag1 | some_tag2[@foo =\n 'bar|']\n | some_tag3[foo | bar]";
$expected = "some_xpath/some_tag1 | some_xpath/some_tag2[@foo =\n 'bar|'] | some_xpath/some_tag3[foo | bar]";

$this->driver
->expects($this->exactly(1))
Expand Down
9 changes: 7 additions & 2 deletions tests/Selector/Xpath/ManipulatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,13 @@ public function getPrependedXpath()
),
'multiline' => array(
'some_xpath',
"some_tag1 | some_tag2[@foo =\n 'bar|'']\n | some_tag3[foo | bar]",
"some_xpath/some_tag1 | some_xpath/some_tag2[@foo =\n 'bar|''] | some_xpath/some_tag3[foo | bar]",
"some_tag1 | some_tag2[@foo =\n 'bar|']\n | some_tag3[foo | bar]",
"some_xpath/some_tag1 | some_xpath/some_tag2[@foo =\n 'bar|'] | some_xpath/some_tag3[foo | bar]",
),
'containing pipe' => array(
'some_xpath',
"some_tag[(contains(normalize-space(string(.)), 'foo|bar') | other_tag[contains(./@some_attribute, 'foo|bar')])]",
"some_xpath/some_tag[(contains(normalize-space(string(.)), 'foo|bar') | other_tag[contains(./@some_attribute, 'foo|bar')])]",
),
);
}
Expand Down

0 comments on commit 39e3309

Please sign in to comment.