function Crawler::relativize
Make the XPath relative to the current context.
The returned XPath will match elements matching the XPath inside the current crawler when running in the context of a node of the crawler.
1 call to Crawler::relativize()
- Crawler::filterXPath in vendor/
symfony/ dom-crawler/ Crawler.php - Filters the list of nodes with an XPath expression.
File
-
vendor/
symfony/ dom-crawler/ Crawler.php, line 947
Class
- Crawler
- Crawler eases navigation of a list of \DOMNode objects.
Namespace
Symfony\Component\DomCrawlerCode
private function relativize(string $xpath) : string {
$expressions = [];
// An expression which will never match to replace expressions which cannot match in the crawler
// We cannot drop
$nonMatchingExpression = 'a[name() = "b"]';
$xpathLen = \strlen($xpath);
$openedBrackets = 0;
$startPosition = strspn($xpath, " \t\n\r\x00\v");
for ($i = $startPosition; $i <= $xpathLen; ++$i) {
$i += strcspn($xpath, '"\'[]|', $i);
if ($i < $xpathLen) {
switch ($xpath[$i]) {
case '"':
case "'":
if (false === ($i = strpos($xpath, $xpath[$i], $i + 1))) {
return $xpath;
// The XPath expression is invalid
}
continue 2;
case '[':
++$openedBrackets;
continue 2;
case ']':
--$openedBrackets;
continue 2;
}
}
if ($openedBrackets) {
continue;
}
if ($startPosition < $xpathLen && '(' === $xpath[$startPosition]) {
// If the union is inside some braces, we need to preserve the opening braces and apply
// the change only inside it.
$j = 1 + strspn($xpath, "( \t\n\r\x00\v", $startPosition + 1);
$parenthesis = substr($xpath, $startPosition, $j);
$startPosition += $j;
}
else {
$parenthesis = '';
}
$expression = rtrim(substr($xpath, $startPosition, $i - $startPosition));
if (str_starts_with($expression, 'self::*/')) {
$expression = './' . substr($expression, 8);
}
// add prefix before absolute element selector
if ('' === $expression) {
$expression = $nonMatchingExpression;
}
elseif (str_starts_with($expression, '//')) {
$expression = 'descendant-or-self::' . substr($expression, 2);
}
elseif (str_starts_with($expression, './/')) {
$expression = 'descendant-or-self::' . substr($expression, 3);
}
elseif (str_starts_with($expression, './')) {
$expression = 'self::' . substr($expression, 2);
}
elseif (str_starts_with($expression, 'child::')) {
$expression = 'self::' . substr($expression, 7);
}
elseif ('/' === $expression[0] || '.' === $expression[0] || str_starts_with($expression, 'self::')) {
$expression = $nonMatchingExpression;
}
elseif (str_starts_with($expression, 'descendant::')) {
$expression = 'descendant-or-self::' . substr($expression, 12);
}
elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) {
// the fake root has no parent, preceding or following nodes and also no attributes (even no namespace attributes)
$expression = $nonMatchingExpression;
}
elseif (!str_starts_with($expression, 'descendant-or-self::')) {
$expression = 'self::' . $expression;
}
$expressions[] = $parenthesis . $expression;
if ($i === $xpathLen) {
return implode(' | ', $expressions);
}
$i += strspn($xpath, " \t\n\r\x00\v", $i + 1);
$startPosition = $i + 1;
}
return $xpath;
// The XPath expression is invalid
}