这个答案是对hanshenrik's answer 的补充,因为我喜欢通用解决方案,但发现示例函数难以阅读,并且就其结果而言不是最佳的。尽管如此,它的工作还是非常好。
关于 XPath 引用
XPath 1.0 允许在其文字中包含任何字符,但用于引用文字的引号除外。允许的引号是 " 和 ',因此引用最多包含其中一个引号的文字是微不足道的。但是要同时引用字符串,您需要在不同的字符串中引用它们并将它们与 XPath 的 concat() 连接起来:
He's telling you "Hello world!".
需要像这样被转义
concat("He's telling", ' you "Hello world!".')
当然,在 ' 和 " 之间拆分文字的位置无关紧要。
实现的差异
hanshenrik 的实现通过提取所有不是双引号的部分然后插入带引号的双引号来创建带引号的文字。但这会产生不良结果:
"""x'x"x""xx
会被他们的功能转义
concat('"', '"', '"', "x'x", '"', "x", '"', '"', "xx")
和上面的例子:
concat("He's telling you ", '"', "Hello world!", '"', ".")
另一方面的这种实现通过交替引用然后尽可能多地引用来最小化部分文字的数量:
第一个例子:
concat("He's telling you ", '"Hello world!".')
第二个例子:
concat('"""x', "'x", '"x""xx')
实施
/**
* Creates a properly quoted xpath 1.0 string literal. It prefers double quotes over
* single quotes. If both kinds of quotes are used in the literal then it will create a
* compound expression with concat(), using as few partial strings as possible.
*
* Based on {@link https://stackoverflow.com/a/54436185/6229450 hanshenrik's StackOverflow answer}.
*
* @param string $literal unquoted literal to use in xpath expression
* @return string quoted xpath literal for xpath 1.0
*/
public static function quoteXPathLiteral(string $literal): string
{
$firstDoubleQuote = strpos($literal, '"');
if ($firstDoubleQuote === false) {
return '"' . $literal . '"';
}
$firstSingleQuote = strpos($literal, '\'');
if ($firstSingleQuote === false) {
return '\'' . $literal . '\'';
}
$currentQuote = $firstDoubleQuote > $firstSingleQuote ? '"' : '\'';
$quoted = [];
$lastCut = 0;
// cut into largest possible parts that contain exactly one kind of quote
while (($nextCut = strpos($literal, $currentQuote, $lastCut))) {
$quotablePart = substr($literal, $lastCut, $nextCut - $lastCut);
$quoted[] = $currentQuote . $quotablePart . $currentQuote;
$currentQuote = $currentQuote === '"' ? '\'' : '"'; // toggle quote
$lastCut = $nextCut;
}
$quoted[] = $currentQuote . substr($literal, $lastCut) . $currentQuote;
return 'concat(' . implode(',', $quoted) . ')';
}