Alpha 阶段进度报告 3——修正/添加 UBB Code


作者:郑凯

目前想到的所有要支持的代码:\[hr] \[url] \[quote] \[img] [file] \[phpcode] \[code] [color] [size] \[b] \[u] \[del] [file]
已经支持的代码 \[hr] \[url] \[quote] \[phpcode]

最具难度的就是这 phpcode 了,传统的 preg_replace 无法派上用场,最后用 preg_match_all + str_replace 才搞定

还得去考虑用 \[标签] 代替 [标签],麻烦事还很多

另一个产生麻烦的事情是通常的转换直接把所有 \n 变成 <br> 了事,可 <p> 就没事干了,其实不应该这么定,我的规则是,两个 \n 表示大段落的间隔,普通 \n 表示普通的换行,再通过对 <p> 的 line-height 和 padding 等属性的定义,<p> 要比两个 <br> 好看的多,不过可能一般人根本看不出来,不过我喜欢就行

[hr]

整整弄了一晚上,到 2004-02-29 09:36,上述全部标签统统摆平,现在的方法略微牺牲了些效率,可效果相信是最好的。整晚在和正则表达式打交道,[url=http://cn.php.net/manual/zh/pcre.pattern.syntax.php]PCRE 模式语法[/url] 和 [url=http://cn.php.net/manual/zh/pcre.pattern.modifiers.php]PCRE 模式修正符[/url] 这两页让我来来回回的翻,只说是在自己 PCRE 理解上的最佳解决方案了。

做点说明

\[img] 无边框无内外补丁,似乎 line-height 对 <img> 标签也回避,一堆 \[img] 可以拼出一个完整的图来,这点我很满意

\[phpcode] 和 \[quote] 标签必须独立成行,既受正则 /U 匹配模式的影响,也是为了易于观看,\[phpcode] 标签里不得有其他标签,否则会把转换后的代码夹在里面

[size] 标签里的数字不是通常 UBB 里的 <font 标签的 size= ,一方面 font 标签本身已经是 XHTML 不提倡的了,同时 size = 1 - 7 对于汉字来说很不合适,因实际单位是 px,范围从 1 - 99

[face] 指定的字体可以带空格,下面的演示会提到

[file] 实际是改下格式,为了以后对附件的 URL 指向能统一,主要是用来套到 \[img] 和 \[url]里的,像 \[img][file=xx]\[/img] 这样,xx 必须为数字

大体上能想的到的测试方法都已经用了,还有什么 BUG 的话只能在使用中发现了,相信这也是一个漏洞的高危区,因此如果做为一个 BBS 的 HTML 过滤可能不大把握,但对于只有我一个人来使用的 blog 就应该没什么问题了

下面这些,既是效果演示,同时也是为了方便测试

分隔符 \[hr]
[hr]

\[url]http://www.example.com\[/url]
[url]http://www.example.com[/url]

\[url=http://www.example.com]链接的另一种写法\[/url]
[url=http://www.example.com]链接的另一种写法[/url]

\[u]下划线\[/u]
[u]下划线[/u]

\[del]删除线\[/del]
[del]删除线[/del]

\[size=16]16px 文字\[/size]
[size=16]16px 文字[/size]

\[color=green]绿色文字\[/color]
[color=green]绿色文字[/color]

[hr]

\[face=Comic Sans MS]Comic Sans MS 字体\[/face]
[face=Comic Sans MS]Comic Sans MS 字体[/face]

图片:\[img]http://www.google.com/images/logo.gif\[/img]
[img]http://www.google.com/images/logo.gif[/img]

文件:\[file=1]
[file=1]

[quote]
引用一段文字
[/quote]

[quote]
再引用一段文字
[/quote]

转换代码:

[phpcode]
function html_out($string) {

$string = str_replace("\r\n", "\n", $string);
$string = str_replace("\t", " ", $string);

$html_search = array(
"&",
"<",
">",
"\"",
'\''
);
$html_replace = array("&#38;#38;",
"&#38;#60;",
"&#38;#62;",
"&#34;",
"&#38;#39;"
);
$string = str_replace($html_search, $html_replace, $string);
krsort($html_search);
krsort($html_replace);

// 转义标签
$ubb_code = array("\[hr]", "\[url]", "\[url", "\[/url]", "\[quote]", "\[/quote]",
"\[img]", "\[/img]", "\[file=", "\[phpcode]", "\[/phpcode]", "\[code]", "\[/code]",
"\[color=", "\[/color]", "\[size=", "\[/size]", "\[del]", "\[/del]", "\[face=", "\[/face]", "\[b]", "\[/b]", "\[u]", "\[/u]");

foreach ($ubb_code as $value) {
$value2 = str_replace("[", "&#91;", $value);
$string = str_replace("\\".$value, $value2, $string);
}

// 段落起始/结束标志
$paragraph_begin = "<p class=\"m\">";
$paragraph_end = "</p>";

// 替换标签
$searcharray = array(
"/\[file=([1-9][0-9]*)\]/", // 字体大小
"/\[u\](.*)\[\/u\]/iU", // 下划线
"/\[del\](.*)\[\/del\]/iU", // 删除线
"/\[img\](.*)\[\/img\]/iU", // 图片
"/\[size=([0-9][0-9]?)\](.*)\[\/size\]/iU", // 字体大小
"/\[color=(.*)\](.*)\[\/color\]/iU", // 颜色
"/\[face=(.*)\](.*)\[\/face\]/iU", // 字体
"/\[hr\]/i",
"/\[url=www\.(.*)\](.*)\[\/url\]/iU",
"/\[url=(.*)\](.*)\[\/url\]/iU",
"/\[url\]www\.(.*)(\[\/url\])/iU",
"/\[url\](.*)\[\/url\]/iU",
"/\[quote\]\n(.*)\n\[\/quote\]/siU",
);
$replacearray = array(
$GLOBALS["cfg"]['AbsoluteUri']."archives/\\1.files",
"<span style=\"text-decoration: underline;\">\\1</span>",
"<span style=\"text-decoration: line-through;\">\\1</span>",
"<img src=\"\\1\">",
"<span style=\"font-size: \\1;\">\\2</span>",
"<span style=\"color: \\1;\">\\2</span>",
"<span style=\"font-family: '\\1';\">\\2</span>",
$paragraph_end."<hr class=\"m\" align=\"left\" size=\"1\">".$paragraph_begin,
"<a hidefocus=\"true\" href=\"http://www.\\1\" target=\"_blank\">\\2</a>",
"<a hidefocus=\"true\" href=\"\\1\" target=\"_blank\">\\2</a>",
"<a hidefocus=\"true\" href=\"http://www.\\1\" target=\"_blank\">\\1</a>",
"<a hidefocus=\"true\" href=\"\\1\" target=\"_blank\">\\1</a>",
$paragraph_end."<div class=\"m\">".$paragraph_begin."\\1".$paragraph_end."</div>".$paragraph_begin,
);
$string = preg_replace($searcharray, $replacearray, $string);

// 替换标签 \[phpcode]
$search = "/\[phpcode](\n.*)\[\/phpcode\]/siU";
if (preg_match_all($search, $string, $matches)) {
$replace = array();
foreach ($matches[1] as $key => $value) {
foreach ($ubb_code as $ubb_code_value) { // 还原转义标签
$ubb_code_value2 = str_replace("[", "&#91;", $ubb_code_value);
$value = str_replace($ubb_code_value2, $ubb_code_value, $value);
}
$value = str_replace($html_replace, $html_search, $value); // 还原 HTML
$value = highlight_string("<?PHP".$value."\n?>", true);
$value = substr($value, 29); // 去掉 <code>
$value = substr($value, 0, -16);
$value = preg_replace("/<font color=\"(.*)\">(.*)<\/font>/siU",
"<span style=\"color: \\1\">\\2</span>", $value);
$value = $paragraph_end."<div class=\"phpcode\">".$value."</div>".$paragraph_begin;
$replace[] = $value;
}
$string = str_replace($matches[0], $replace, $string);
}

// 加段落、去掉空段落
$string = str_replace("\n\n", $paragraph_end.$paragraph_begin, $string);
$string = str_replace($paragraph_begin.$paragraph_end, "", $paragraph_begin.$string.$paragraph_end);
$string = str_replace(" ", " &#160;", $string);

// 还原转义标签
foreach ($ubb_code as $value) {
$value2 = str_replace("[", "&#91;", $value);
$string = str_replace($value2, $value, $string);
}

// 加换行
$string = str_replace("\n", "<br />", $string);

return $string;
}
[/phpcode]

comment:

注释效果测试