如何同时进行自动链接和htmlspecialchars转换?

时间:2017-07-04 17:04:39

标签: php regex

我想创建同时满足以下两个条件的代码。

  1. 如果文字包含任何网址,则该部分会自动转换为<a href ~ class='temp_class'>链接。

  2. func1()应用于文本上的html实体字符,但上述函数生成的某些部分除外。 (这是为了防止恶意脚本的攻击。)

  3. 所以我写了下面的代码:

    <?php
        function func1($text) //function like htmlspecialchars
        {
            $text = str_replace("&", "&#38;", $text);
            $text = str_replace("\"", "&#34;", $text);
            $text = str_replace("'", "&#39;", $text);
            $text = str_replace("<", "&#60;", $text);
            $text = str_replace(">", "&#62;", $text);
            $text = str_replace(" ", "&nbsp;", $text);
            return $text;
        }
        function func2($text)
        {
            $text = func1($text);
            $url_pattern = "(http|https):\/\/([a-zA-Z0-9.\/?&=%_+-@~:#$]+)";
            $text = preg_replace("/(".$url_pattern.")/i", "<a href='\\1' target='_blank' class='temp_class'>\\1</a>", $text);
            return $text;
        }
        $test_string ="hello universe! https://www.youtube.com/watch?v=test     <iframe src='https://youtube.com/watch?v=good'></iframe> hello world.";
        echo func2($test_string);
    ?>
    

    然而,当我运行上面的代码时,应用“太广泛”。 换句话说,'https://www.~'(...)'~hello world'被视为'单个链接'。

    我想要的是三件事:

    1. <a href ~ class='temp_class'>适用于https://www.youtube.com/watch?v=test

    2. <a href ~ class='temp_class'>适用于https://youtube.com/watch?v=good

    3. 本文中的
    4. <>'etc.func1()进行了适当的转换。因此<iframe ~ ></iframe>代码不起作用。

    5. 1和2无法正常运行。

      我可以猜到为什么会这样。也许func1()<>etc.转换为&#60;&#62;etc.,因此{{1}中的正则表达式将它们解释为url的一部分。

      我可以猜到为什么,但我现在不知道该怎么做。

      我正在考虑向func2()中的$url_pattern添加一些字词,以排除func2()%nbsp;等字符的解释。但是,如何用regexp表达这一点对我来说也是一个很大的障碍。

      我花了很长时间来解决这个问题,但这很困难。请帮帮我。

      如果有任何您无法理解的内容,请发表评论。

2 个答案:

答案 0 :(得分:1)

问题是你的func1()会转换&nbsp;中的所有空格,因此当正则表达式查看结果时,它会看到中断 - 例如在"hello"下一个字符之后是正则表达式中允许的&

在执行HTML转义之前,您应该运行URL捕获正则表达式,然后再进行HTML转义。

顺便说一句 - 请使用htmlspecialchars()代替您自己的自定义功能 - 正如@tadman所说。这样做的一个主要优点是htmlspecialchars()不会转换空格,因此不会遇到您描述的问题,而且 - 将空格转换为非中断空格通常不是一个好主意。

答案 1 :(得分:0)

我想你可以尝试一下。防范网址中的实体。

Formatted regex

 ( https? )                    # (1)
 : //
 (                             # (2 start)
      (?:
           (?!
                (?i)
                (?:
                     &
                     (?:
                          [a-z_:] [a-z\d_:.-]* 
                       |  (?:
                               \#
                               (?: [0-9]+ | x [0-9a-f]+ )
                          )
                     )
                  |  % [a-z_:] [a-z\d_:.-]* 
                )
                ;
           )
           [a-zA-Z0-9./?&=%_+-@~:#$] 
      )+
 )                             # (2 end)


http://sandbox.onlinephpfunctions.com/code/0bba1854a960c00d4946b9cdaa9cca2ca2e447fc

<?php
    function func1($text) //function like htmlspecialchars
    {
        $text = str_replace("&", "&#38;", $text);
        $text = str_replace("\"", "&#34;", $text);
        $text = str_replace("'", "&#39;", $text);
        $text = str_replace("<", "&#60;", $text);
        $text = str_replace(">", "&#62;", $text);
        $text = str_replace(" ", "&nbsp;", $text);
        return $text;
    }
    function func2($text)
    {
        $text = func1($text);
        $url_pattern = "(http|https):\/\/((?:(?!(?i)(?:&(?:[a-z_:][a-z\d_:.-]*|(?:\#(?:[0-9]+|x[0-9a-f]+)))|%[a-z_:][a-z\d_:.-]*);)[a-zA-Z0-9.\/?&=%_+-@~:#$])+)";
        $text = preg_replace("/(".$url_pattern.")/i", "<a href='\\1' target='_blank' class='temp_class'>\\1</a>", $text);
        return $text;
    }
    $test_string ="hello universe! https://www.youtube.com/watch?v=test     <iframe src='https://youtube.com/watch?v=good'></iframe> hello world.";
    echo func2($test_string);

输出(带有额外的换行符间距)

hello&nbsp;universe!&nbsp;

<a href='https://www.youtube.com/watch?v=test' target='_blank' class='temp_class'>
https://www.youtube.com/watch?v=test
</a>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#60;iframe&nbsp;src=&#39;

<a href='https://youtube.com/watch?v=good' target='_blank' class='temp_class'>
https://youtube.com/watch?v=good
</a>

&#39;&#62;&#60;/iframe&#62;&nbsp;hello&nbsp;world.