【问题标题】:How to escape xpath in php如何在php中转义xpath
【发布时间】:2014-08-16 03:13:50
【问题描述】:

转义给 xpath 的 var 的最佳方法是什么。

$test = simplexml_load_file('test.xml');
$var = $_GET['var']; // injection heaven
$result = $test->xpath('/catalog/items/item[title="'.$var.'"]');

通常我使用 PDO 绑定。或类似的东西,但它们都需要数据库连接。 仅addslasheshtmlentities 就够了吗。
还是有更好的办法?

【问题讨论】:

    标签: php xpath


    【解决方案1】:

    这个答案是对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) . ')';
    }
    

    【讨论】:

      【解决方案2】:

      根据XPath 1.0 spec,字面量的语法如下:

      [29]    Literal    ::=      '"' [^"]* '"'   
                                | "'" [^']* "'"
      

      这意味着在单引号字符串中,除了单引号之外的任何内容都是允许的。在双引号字符串中,允许使用除双引号之外的任何内容。

      【讨论】:

      • 只是检查,但如果我想在节点值中使用引号,它们也需要是 html 字符对吗?
      • 是的,这就是htmlspecialchars() 调用的目的
      • 好的,我想这可行。有没有办法在仍然能够选择实际报价的同时做到这一点?
      • 那应该选择实际的引号......您也可以将选择器中的所有引号转换为单引号,然后将它们全部用双引号括起来
      • 我查看了 XPath 1.0 规范 (w3.org/TR/1999/REC-xpath-19991116/#exprlex) 并找到了您引用的部分。但是,我找不到对在字符串文字中解码的 html 实体的任何引用。你有这方面的参考吗?或者这可能只是某些文字使用的扩展? 3.6 节引用了 XML 规范中的“字符”定义和 W3C 字符模型规范中的“字符规范化”定义,但它们都没有指定 HTML 实体解码(尽管字符模型规范确实建议使用 some i> 转义/编码形式)。
      【解决方案3】:

      以上答案适用于 XPath 1.0,这是 PHP 唯一支持的版本。为了完整起见,我会注意到从XPath 2.0 开始,字符串文字可以通过加倍来包含引号:

      [74]        StringLiteral      ::=      ('"' (EscapeQuot | [^"])* '"') | ("'" (EscapeApos | [^'])* "'")
      [75]        EscapeQuot     ::=      '""'
      [76]        EscapeApos     ::=      "''"
      

      例如要搜索标题 Some "quoted" title,您可以使用以下 xpath:

      /catalog/items/item[title="Some ""quoted"" title"]
      

      这可以通过简单的字符串转义来实现(但我不会给出示例,因为您使用的是 PHP,并且如前所述,PHP 不支持 XPath 2.0)。

      【讨论】:

        【解决方案4】:

        你不能真的做一个通用的xpath escape函数,但是你可以做一个XPath quote函数,可以像这样使用

        $result = $test->xpath('/catalog/items/item[title='.xpath_quote($var).']');
        

        实现:

        //based on https://stackoverflow.com/a/1352556/1067003
        function xpath_quote(string $value):string{
            if(false===strpos($value,'"')){
                return '"'.$value.'"';
            }
            if(false===strpos($value,'\'')){
                return '\''.$value.'\'';
            }
            // if the value contains both single and double quotes, construct an
            // expression that concatenates all non-double-quote substrings with
            // the quotes, e.g.:
            //
            //    concat("'foo'", '"', "bar")
            $sb='concat(';
            $substrings=explode('"',$value);
            for($i=0;$i<count($substrings);++$i){
                $needComma=($i>0);
                if($substrings[$i]!==''){
                    if($i>0){
                        $sb.=', ';
                    }
                    $sb.='"'.$substrings[$i].'"';
                    $needComma=true;
                }
                if($i < (count($substrings) -1)){
                    if($needComma){
                        $sb.=', ';
                    }
                    $sb.="'\"'";
                }
            }
            $sb.=')';
            return $sb;
        }
        

        它基于来自 https://stackoverflow.com/a/1352556/1067003 的 C# xpath 引用函数

        仅添加斜杠和 htmlentities 就足够了吗? 还是有更好的办法?

        如果使用适当的 xpath 引用函数而不是添加斜杠/htmlentities,我会在晚上睡得更好,但我真的不知道这些在技术上是否足够。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-07-28
          • 2012-08-01
          • 2019-08-26
          • 2022-11-19
          • 1970-01-01
          • 2017-11-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多