5

问题:当使用HTML Purifier处理用户输入的内容时,换行符没有被翻译成<br />标签。

考虑以下用户输入的内容:

Lorem ipsum dolor sit amet.
This is another line.

<pre>
.my-css-class {
    color: blue;
}
</pre>

Lorem ipsum:

<ul>
<li>Lorem</li>
<li>Ipsum</li>
<li>Dolor</li>
</ul>

Dolor sit amet,
MyName

使用 HTML Purifier 处理时,上述内容将更改为以下内容:

Lorem ipsum dolor sit amet。这是另一条线。

.my-css-class {
    color: blue;  
} 

Lorem ipsum:

  • 洛雷姆
  • 伊普苏姆
  • 多洛尔
Dolor 坐在一起,我的名字

正如您所看到的,“ MyName ”原本打算由用户放在单独的行上,现在与前一行一起显示。

怎么修?

当然是使用 PHPnl2br()函数。但是,无论我们在净化内容之前还是之后使用它,都会出现新的问题。

这是在 HTML Purifier 之前使用 nl2br() 的示例:

Lorem ipsum dolor sit amet。
这是另一条线。

.my-css-class {

    color: blue; 

} 

Lorem ipsum:

  • 洛雷姆
  • 伊普苏姆
  • 多洛尔

Dolor 坐在一起,我的
名字

发生的情况是 nl2br()<br />为每个换行符添加,因此即使是<pre>块中的那些也正在处理,以及每个<li>标记之后的换行符。

我试过的

我尝试了一个自定义 nl2br() 函数,它用标签替换换行符<br />,然后从块中删除所有<br />标签。<pre>它工作得很好,但是问题仍然存在于这些<li>项目中。

对块尝试相同的方法<ul>也会删除子<br />元素的所有标签<li>,除非我们使用更复杂的正则表达式来删除元素<br />内部<ul>但元素外部的标签<li>。但是嵌套<ul>在一个<li>项目中呢?为了处理所有这些情况,我们必须有一个更复杂的正则表达式!

  • 如果这是正确的方法,你能帮我解决正则表达式吗?
  • 如果这不是正确的方法,我该如何解决这个问题?我也对 HTML Purifier 的替代品持开放态度。

我已经看过的其他资源:

4

2 回答 2

6

nl2br()可以使用自定义函数部分(如果不是完全)解决此问题:

function nl2br_special($string){

    // Step 1: Add <br /> tags for each line-break
    $string = nl2br($string); 

    // Step 2: Remove the actual line-breaks
    $string = str_replace("\n", "", $string);
    $string = str_replace("\r", "", $string);

    // Step 3: Restore the line-breaks that are inside <pre></pre> tags
    if(preg_match_all('/\<pre\>(.*?)\<\/pre\>/', $string, $match)){
        foreach($match as $a){
            foreach($a as $b){
            $string = str_replace('<pre>'.$b.'</pre>', "<pre>".str_replace("<br />", PHP_EOL, $b)."</pre>", $string);
            }
        }
    }

    // Step 4: Removes extra <br /> tags

    // Before <pre> tags
    $string = str_replace("<br /><br /><br /><pre>", '<br /><br /><pre>', $string);
    // After </pre> tags
    $string = str_replace("</pre><br /><br />", '</pre><br />', $string);

    // Arround <ul></ul> tags
    $string = str_replace("<br /><br /><ul>", '<br /><ul>', $string);
    $string = str_replace("</ul><br /><br />", '</ul><br />', $string);
    // Inside <ul> </ul> tags
    $string = str_replace("<ul><br />", '<ul>', $string);
    $string = str_replace("<br /></ul>", '</ul>', $string);

    // Arround <ol></ol> tags
    $string = str_replace("<br /><br /><ol>", '<br /><ol>', $string);
    $string = str_replace("</ol><br /><br />", '</ol><br />', $string);
    // Inside <ol> </ol> tags
    $string = str_replace("<ol><br />", '<ol>', $string);
    $string = str_replace("<br /></ol>", '</ol>', $string);

    // Arround <li></li> tags
    $string = str_replace("<br /><li>", '<li>', $string);
    $string = str_replace("</li><br />", '</li>', $string);

    return $string;
}

这必须在内容经过 H​​TML 纯化之前应用于内容。除非您知道自己在做什么,否则切勿重新处理纯化的内容。

请注意,由于已经保留了每个换行符和双换行符,因此您不应使用AutoFormat.AutoParagraphHTML Purifier 的功能:

// Process line-breaks
$string = nl2br_special($string);

// Initiate HTML Purifier config
$purifier_config = HTMLPurifier_Config::createDefault();
$purifier_config->set('HTML.Allowed', 'p,ul,ol,li,strong,b,em,i,u,a[href],code,pre,blockquote,cite,img[src|alt],br,hr,h3,h4');
//$purifier_config->set('AutoFormat.AutoParagraph', true); // Make sure to NOT use this

// Initiate HTML Purifier
$purifier = new HTMLPurifier($purifier_config);

// Purify the content!
$string = $purifier->purify($string);

而已!


此外,因为允许基本 HTML 标记最初是为了通过不添加其他标记语法来改善用户体验,所以您可能希望允许用户发布代码,尤其是 HTML 代码,这些代码不会被 HTML Purifier 解释/删除。

HTML Purifier 目前允许发布代码,但需要复杂的 CDATA 标记:

<![CDATA[
Place code here
]]>

很难记住和写。为了尽可能简化用户体验,我认为最好允许用户通过嵌入简单<code>(对于内联代码)和<pre>(对于代码块)标签来添加代码。以下是如何做到这一点:

function custom_code_tag_callback($code) {

    return '<code>'.trim(htmlspecialchars($code[1])).'</code>';
}
function custom_pre_tag_callback($code) {

    return '<pre><code>'.trim(htmlspecialchars($code[1])).'</code></pre>';
}

// Don't require HTMLPurifier's CDATA enclosing, instead allow simple <code> or <pre> tags
$string = preg_replace_callback("/\<code\>(.*?)\<\/code\>/is", 'custom_code_tag_callback', $string);
$string = preg_replace_callback("/\<pre\>(.*?)\<\/pre\>/is", 'custom_pre_tag_callback', $string);

请注意,与 nl2br 处理一样,它必须在内容经过 H​​TML Purified 之前完成。另外,请记住,如果用户在他自己发布的代码中放置<code><pre>标记,那么它将关闭包含他的代码的父级<code>或标记。<pre>这无法解决,也适用于原始 CDATA 标记或任何标记,甚至是 StackOverflow 上使用的标记(例如,在代码示例中使用 ` 符号将关闭代码标记)。

最后,为了获得良好的用户体验,我们可能还需要自动化其他一些事情,例如我们希望使其可点击的链接。幸运的是,这可以通过 HTML PurifierAutoFormat.Linkify功能来完成。

这是包含最终设置的所有内容的最终代码:

// === Declare functions ===

function nl2br_special($string){

    // Step 1: Add <br /> tags for each line-break
    $string = nl2br($string); 

    // Step 2: Remove the actual line-breaks
    $string = str_replace("\n", "", $string);
    $string = str_replace("\r", "", $string);

    // Step 3: Restore the line-breaks that are inside <pre></pre> tags
    if(preg_match_all('/\<pre\>(.*?)\<\/pre\>/', $string, $match)){
        foreach($match as $a){
            foreach($a as $b){
            $string = str_replace('<pre>'.$b.'</pre>', "<pre>".str_replace("<br />", PHP_EOL, $b)."</pre>", $string);
            }
        }
    }

    // Step 4: Removes extra <br /> tags

    // Before <pre> tags
    $string = str_replace("<br /><br /><br /><pre>", '<br /><br /><pre>', $string);
    // After </pre> tags
    $string = str_replace("</pre><br /><br />", '</pre><br />', $string);

    // Arround <ul></ul> tags
    $string = str_replace("<br /><br /><ul>", '<br /><ul>', $string);
    $string = str_replace("</ul><br /><br />", '</ul><br />', $string);
    // Inside <ul> </ul> tags
    $string = str_replace("<ul><br />", '<ul>', $string);
    $string = str_replace("<br /></ul>", '</ul>', $string);

    // Arround <ol></ol> tags
    $string = str_replace("<br /><br /><ol>", '<br /><ol>', $string);
    $string = str_replace("</ol><br /><br />", '</ol><br />', $string);
    // Inside <ol> </ol> tags
    $string = str_replace("<ol><br />", '<ol>', $string);
    $string = str_replace("<br /></ol>", '</ol>', $string);

    // Arround <li></li> tags
    $string = str_replace("<br /><li>", '<li>', $string);
    $string = str_replace("</li><br />", '</li>', $string);

    return $string;
}


function custom_code_tag_callback($code) {

    return '<code>'.trim(htmlspecialchars($code[1])).'</code>';
}

function custom_pre_tag_callback($code) {

    return '<pre><code>'.trim(htmlspecialchars($code[1])).'</code></pre>';
}



// === Process user's input ===

// Process line-breaks
$string = nl2br_special($string);

// Allow simple <code> or <pre> tags for posting code
$string = preg_replace_callback("/\<code\>(.*?)\<\/code\>/is", 'custom_code_tag_callback', $string);
$string = preg_replace_callback("/\<pre\>(.*?)\<\/pre\>/is", 'custom_pre_tag_callback', $string);


// Initiate HTML Purifier config
$purifier_config = HTMLPurifier_Config::createDefault();
$purifier_config->set('HTML.Allowed', 'p,ul,ol,li,strong,b,em,i,u,a[href],code,pre,blockquote,cite,img[src|alt],br,hr,h3,h4');
$purifier_config->set('AutoFormat.Linkify', true); // Make links clickable
//$purifier_config->set('HTML.TargetBlank', true); // Uncomment if you want links to open new tabs
//$purifier_config->set('AutoFormat.AutoParagraph', true); // Leave this commented as it conflicts with nl2br


// Initiate HTML Purifier
$purifier = new HTMLPurifier($purifier_config);

// Purify the content!
$string = $purifier->purify($string);

干杯!

于 2013-08-05T18:26:08.630 回答
1

也许这会有所帮助。

function custom_nl2br($html) {
    $pattern = "/<ul>(.*?)<\/ul>/s";
    preg_match($pattern, $html, $matches);

    $html = nl2br(str_replace($matches[0], '[placeholder]', $html));
    $html = str_replace('[placeholder]',$matches[0], $html);

    return $html;
}
于 2013-07-15T09:03:47.700 回答