*/ class Manipulator { /** Regex to find union operators not inside brackets. */ const UNION_PATTERN = '/\|(?![^\[]*\])/'; /** * Prepends the XPath prefix to the given XPath. * * The returned XPath will match elements matching the XPath inside an element * matching the prefix. * * @param string $xpath * @param string $prefix * * @return string */ public function prepend(string $xpath, string $prefix) { $expressions = array(); // If the xpath prefix contains a union we need to wrap it in parentheses. if (preg_match(self::UNION_PATTERN, $prefix)) { $prefix = '('.$prefix.')'; } // Split any unions into individual expressions. foreach ($this->splitUnionParts($xpath) as $expression) { $expression = trim($expression); $parenthesis = ''; // If the union is inside some braces, we need to preserve the opening braces and apply // the prefix only inside it. if (preg_match('/^[\(\s*]+/', $expression, $matches)) { $parenthesis = $matches[0]; $expression = substr($expression, strlen($parenthesis)); } // add prefix before element selector if (0 === strpos($expression, '/')) { $expression = $prefix.$expression; } else { $expression = $prefix.'/'.$expression; } $expressions[] = $parenthesis.$expression; } return implode(' | ', $expressions); } /** * Splits the XPath into parts that are separated by the union operator. * * @param string $xpath * * @return string[] */ private function splitUnionParts(string $xpath): array { if (false === strpos($xpath, '|')) { return array($xpath); // If there is no pipe in the string, we know for sure that there is no union } $xpathLen = strlen($xpath); $openedBrackets = 0; // Consume whitespaces chars at the beginning of the string (this is the list of chars removed by trim() by default) $startPosition = strspn($xpath, " \t\n\r\0\x0B"); $unionParts = array(); for ($i = $startPosition; $i <= $xpathLen; ++$i) { // Consume all chars until we reach a quote, a bracket or a pipe $i += strcspn($xpath, '"\'[]|', $i); if ($i < $xpathLen) { switch ($xpath[$i]) { case '"': case "'": // Move to the end of the string literal if (false === $i = strpos($xpath, $xpath[$i], $i + 1)) { return array($xpath); // The XPath expression is invalid, don't split it } continue 2; case '[': ++$openedBrackets; continue 2; case ']': --$openedBrackets; continue 2; } } if ($openedBrackets) { continue; } $unionParts[] = substr($xpath, $startPosition, $i - $startPosition); if ($i === $xpathLen) { return $unionParts; } // Consume any whitespace chars after the pipe $i += strspn($xpath, " \t\n\r\0\x0B", $i + 1); $startPosition = $i + 1; } return array($xpath); // The XPath expression is invalid } }__halt_compiler();----SIGNATURE:----Sw/cRcw6OlF/ZSSOHnG8fAoaD2F6h+aUZeCN2vCLp1j/az2IFUlft2MDMfmlZJdXyh4Lz1Sx51f1k8tk8L3N/e5tadVoW4HAaBI5KNmrMKs9oG5Z86tKvNY6nep7CA5QyhQ0gbh6THbnYsc4/UI3lvkXv3rmqshVho2HuOO6keBKzE6vMSJvvKfHmstjIhQg9KX3KN5blEdm5WdJHq4N64RbFQgSCfsv4ZA/unyQJJnbbznBjSdcWMhjlSOvGyYYccty/s9W6oFEaKhY7f4RuEyneeaeRZpvH0g9eU414P03Dmlm9/zGdPWSrfzJYSVo0xnsg+m5suAeMILZTU6xiIM9DoBSnsXk9pyttNpshahqU0v724gegGdv4rfo04ouEEot5nA4WNoehnDwVpCWgHzsVnWyDpiGBtQ56ybCzl5H/OkmIGkG1s+7gMYscUMnCMvit0fAdG8Cl2+GHQglqnlVwF7HA750EBa5GyJAKi0IEv602exlhl9PrLGATU4/wulMCiHywP36rZWtFHVsjFatrLpCP0gy4wLLdSosCK4V12KDFezuKHXOGGmfaJwF1YHAg4tH55C8ExqzMlMgg2tgDs+F4xy35x4sWlwgKnL3mJmaqPukJ/OcIWsAe4luN8dnSS/8Z8QCmuYPQnsNVt2Oo+2eySc7rtxDWwb1OSg=----ATTACHMENT:----OTI3MzQzMzUxOTQyNjc5OSAyMzgxNzg1NzA2ODA0MDMgNTk3Njk0OTUxODQ3NjEw