Com*_*ity 4 html php regex line-breaks nl2br
问题:使用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
Run Code Online (Sandbox Code Playgroud)
使用HTML Purifier处理时,以上内容将更改为以下内容:
Lorem ipsum dolor坐在amet。这是另一行。
Run Code Online (Sandbox Code Playgroud).my-css-class { color: blue; }Lorem ipsum:
Dolor坐在amet,我的名字
- 洛雷姆
- 益普森
- 多洛
如您所见,原本打算由用户在单独行中显示的“ MyName ”将与前一行一起显示。
nl2br()当然,使用PHP 函数。但是,无论是在净化内容之前还是之后使用它,都会出现新的问题。
这是在HTML Purifier之前使用nl2br()的示例:
Lorem ipsum dolor坐在amet。
这是另一行。Run Code Online (Sandbox Code Playgroud).my-css-class { color: blue; }Lorem ipsum:
- 洛雷姆
- 益普森
- 多洛
悲坐阿梅德,
MYNAME
发生的情况是nl2br()<br />为每个换行符添加,因此,即使是<pre>块中的换行符也正在处理,以及每个<li>标签之后的换行符。
我尝试了一个自定义的nl2br()函数,该函数用<br />标签替换换行符,然后<br />从<pre>块中删除所有标签。效果很好,但是问题仍然存在<li>。
对<ul>块尝试相同的方法也会<br />从<li>子级中删除所有标签,除非我们使用更复杂的正则表达式删除元素<br />内部<ul>但<li>元素外部的标签。但是嵌套<ul>在一个<li>项目中呢?为了处理所有这些情况,我们必须有一个更复杂的正则表达式!
我已经看过的其他资源:
可以使用自定义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;
}
Run Code Online (Sandbox Code Playgroud)
必须先对内容进行 HTML纯化。除非您知道自己在做什么,否则切勿重新处理纯化的内容。
请注意,由于每个换行符和双换行符都已保留,因此您不应使用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);
Run Code Online (Sandbox Code Playgroud)
而已!
此外,由于允许基本HTML标签最初是通过不添加其他标记语法来改善用户体验的,所以您可能希望允许用户发布代码,尤其是HTML代码,而HTML Purifier不会解释/删除这些代码。
HTML Purifier当前允许发布代码,但需要复杂的CDATA标记:
<![CDATA[
Place code here
]]>
Run Code Online (Sandbox Code Playgroud)
难以记住和写作。为了尽可能简化用户体验,我相信最好允许用户通过将代码嵌入简单的<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);
Run Code Online (Sandbox Code Playgroud)
请注意,就像nl2br处理一样,它必须在内容被HTML净化之前完成。另外,请记住,如果用户在自己发布的代码中放置<code>或<pre>标记,则它将关闭包含其代码的父代<code>或<pre>标记。这是无法解决的,并且也适用于原始CDATA标记或任何标记,即使是StackOverflow上使用的标记(例如,在代码示例中使用`符号也会关闭代码标记)。
最后,为了获得良好的用户体验,我们可能还希望自动化其他事物,例如我们希望使链接可单击。幸运的是,这可以通过HTML Purifier AutoFormat.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);
Run Code Online (Sandbox Code Playgroud)
干杯!