UBB (Universal Bulletin Board) 代碼是一種輕量級標記語言,廣泛用於論壇、博客評論、CMS 內容編輯,允許用戶通過簡單標籤(如 [b]粗體[/b]、[url=...]鏈接[/url])生成安全的 HTML,避免直接輸入 HTML 帶來的 XSS 風險。本文深度解析一個功能完整的 ubb2xhtml() 函數實現。
函數採用 正則表達式替換 策略,定義匹配模式數組 $match,再配合 preg_replace() 批量處理。這種設計易於擴展新標籤,維護成本低。
<?php
/**
* UBB 代碼轉換為 XHTML
* @param string $ubb 包含 UBB 標籤的文本
* @return string 轉換後的 XHTML
*/
function ubb2xhtml($ubb) {
// 1. 定義 Flash 嵌入模板(使用 heredoc 便於多行)
$flash = << '替換模板' 或 回調函數
$patterns = [
// [url=鏈接]文字[/url]
'%\[url=([^\s\]]+)\](.*?)\[/url\]%s' =>
'<a href="\1" target="_blank" rel="noopener">\2</a>',
// [email=郵箱]文字[/email]
'%\[email=([^\s\]]+)\](.*?)\[/email\]%i' =>
'<a href="mailto:\1">\2</a>',
// [img width=寬 height=高]圖片URL[/img]
'%\[img\s+width=(\d+)\s+height=(\d+)\](.*?)\[/img\]%s' =>
'<img src="\3" width="\1" height="\2" alt="" />',
// [img=圖片URL]
'%\[img=([^\s\]]+)\]%s' =>
'<img src="\1" alt="" />',
// [flash width=寬 height=高]SWF_URL[/flash] - 使用 sprintf 回調
'%\[flash\s+width=(\d+)\s+height=(\d+)\](.*?)\[/flash\]%se' =>
'sprintf($flash, \1, \2, "\3")',
// 基礎文本格式:[b][i][u][strike][sup][sub]
'%\[(b|i|u|strike|sup|sub)\](.*?)\[/\1\]%s' =>
'<\1>\2</\1>',
// 標題:[h1]...[/h1] 到 [h6]
'%\[h([1-6])\](.*?)\[/h\1\]%s' =>
'<h\1>\2</h\1>',
// 分隔線:[hr/]
'%\[hr/\]%s' =>
'<hr />',
// 顏色:[color=red]文字[/color]
'%\[color=([^\s\]]+)\](.*?)\[/color\]%s' =>
'<span style="color:\1">\2</span>',
// 字體:[font=Arial]文字[/font]
'%\[font=([^"\]]+)\](.*?)\[/font\]%s' =>
'<span style="font-family:\1">\2</span>',
// 字號:[size=14]文字[/size] (像素值)
'%\[size=([^\s\]]+)\](.*?)\[/size\]%s' =>
'<span style="font-size:\1px">\2</span>',
// 對齊:[align=center]文字[/align]
'%\[align=(center|right|left)\](.*?)\[/align\]%s' =>
'<div style="text-align:\1">\2</div>',
// 垂直對齊(表格單元格用)
'%\[valign=(middle|top|bottom)\](.*?)\[/valign\]%s' =>
'<div style="vertical-align:\1">\2</div>',
// 表格相關(簡化版)
'%\[table=([^\s\]]*)\](.*?)\[/table\]%s' =>
'<table\1>\2</table>',
'%\[caption\](.*?)\[/caption\]%s' =>
'<caption>\1</caption>',
'%\[tr=([^\s\]]*)\](.*?)\[/tr\]%s' =>
'<tr\1>\2</tr>',
'%\[th\](.*?)\[/th\]%s' =>
'<th>\1</th>',
'%\[td\](.*?)\[/td\]%s' =>
'<td>\1</td>',
// 註釋/引用/代碼塊
'%\[note\](.*?)\[/note\]%s' =>
'<div class="note">\1</div>',
'%\[quote=(.*?)\](.*?)\[/quote\]%s' =>
'<blockquote><cite>\1</cite>\2</blockquote>',
'%\[code\](.*?)\[/code\]%s' =>
'<pre><code>\1</code></pre>',
// 雙空格保留
'%[ ]{2}%s' =>
' ',
];
// 3. 執行替換
// 注意:順序很重要,巢狀標籤需從內向外或按優先級處理
foreach ($patterns as $pattern => $replacement) {
$ubb = preg_replace($pattern, $replacement, $ubb);
}
return $ubb;
}
?>
(.*?) 而非 (.*),防止跨標籤匹配。[^\s\]]+ 匹配非空白、非右括號字符,精確捕獲屬性值。s (DOTALL) 讓 . 匹配換行;i 忽略大小寫;e (已棄用) 執行替換代碼——現代寫法應改用 preg_replace_callback()。sprintf() 回調利用 %1\$d、%3\$s 等參數編號,配合 preg_replace() 的 e 修飾符(或回調函數)動態生成嵌入代碼。注意:PHP 7.0+ 移除了 /e 修飾符,必須遷移到 preg_replace_callback()。
javascript: 協議或惡意樣式。htmlspecialchars(),僅允許 UBB 標籤生成 HTML。[b][i]文字[/i][/b])可能需要多輪替換或狀態機解析。preg_replace_callback()<?php
// 以 [url] 標籤為例
$ubb = preg_replace_callback(
'%\[url=([^\s\]]+)\](.*?)\[/url\]%s',
function ($matches) {
// $matches[1] = URL, $matches[2] = 鏈接文本
$url = htmlspecialchars($matches[1], ENT_QUOTES, 'UTF-8');
$text = htmlspecialchars($matches[2], ENT_QUOTES, 'UTF-8');
// 簡單協議白名單
if (!preg_match('/^(https?|ftp):\/\//i', $url)) {
return $matches[0]; // 不合法則保持原樣
}
return '<a href="' . $url . '" target="_blank" rel="noopener noreferrer">' . $text . '</a>';
},
$ubb
);
?>
[ul]、[ol]、[li],注意列表嵌套需狀態機處理。[video]、[audio] 標籤對接 HTML5 播放器。[code=php] 整合 Prism.js 或 highlight.js。[emoji:smile] 映射圖片或 Unicode。