為了 SEO 優化和安全性,CMS 系統常需自動識別並移除非本站域名的外部鏈接,保留純文本錨點。本文提供基於正則的完整實現,參考織夢 CMS 核心邏輯優化而來。
/**
* 刪除非站內鏈接,保留錨文本
* @param string $body 文章內容
* @param array $allow_urls 允許的域名列表(含主域名)
* @return string 處理後內容
*/
function replace_external_links(&$body, $allow_urls = array()) {
// 1. 構建允許域名正則
$host_rule = join('|', $allow_urls);
$host_rule = preg_replace("#[\n\r]#", '', $host_rule);
$host_rule = str_replace('.', '\\.', $host_rule); // 轉義點號
$host_rule = str_replace('/', '\\/', $host_rule); // 轉義斜杠
// 2. 匹配所有 標簽
$matches = array();
preg_match_all("#]*)>(.*)#iU", $body, $matches);
if (!is_array($matches[0]) || empty($matches[0])) {
return $body;
}
$replace_pairs = array(); // 待替換對應關係
// 3. 遍歷判斷每個鏈接
foreach ($matches[0] as $i => $full_tag) {
$attrs = $matches[1][$i]; // href 等屬性
$anchor_text = $matches[2][$i]; // 錨文本
// 檢查 href 是否包含允許域名
$is_allowed = false;
if ($host_rule !== '' && preg_match('#' . $host_rule . '#i', $attrs)) {
$is_allowed = true;
}
if (!$is_allowed) {
// 外部鏈接:記錄 完整標簽 => 錨文本 的替換對
$replace_pairs[$full_tag] = $anchor_text;
}
}
// 4. 批量替換
if (!empty($replace_pairs)) {
$body = str_replace(array_keys($replace_pairs), array_values($replace_pairs), $body);
}
return $body;
}
$content = '歡迎訪問 官網 了解詳情。
參考文檔:幫助中心
合作夥伴:合作方
'; // 允許本站域名(支持多域名) $allowed = array('example.com', 'www.example.com', 'static.example.com'); $result = replace_external_links($content, $allowed); echo $result; /* 輸出:歡迎訪問 官網 了解詳情。
參考文檔:幫助中心
合作夥伴:合作方
*/
function replace_external_links_advanced(&$body, $allow_urls = array(), $options = array()) {
$defaults = array(
'keep_anchor' => true, // 保留錨文本
'add_nofollow' => false, // 外鏈加 nofollow 而非刪除
'target_blank' => false, // 外鏈加 target=_blank
'allowed_domains' => array(), // 額外允許域名
);
$opts = array_merge($defaults, $options);
$all_allowed = array_merge($allow_urls, $opts['allowed_domains']);
$host_rule = join('|', array_map(function($d) {
return str_replace(array('.', '/'), array('\\.', '\\/'), $d);
}, $all_allowed));
return preg_replace_callback(
"#]*href\s*=\s*[\"']([^\"']+)[\"'][^>]*)>(.*?)#i",
function($m) use ($host_rule, $opts) {
$full_attrs = $m[1];
$href = $m[2];
$text = $m[3];
// 判斷是否站內鏈接
$is_internal = ($host_rule && preg_match('#' . $host_rule . '#i', $href));
// 相對路徑視為站內
if (!$is_internal && strpos($href, 'http') !== 0) {
$is_internal = true;
}
if ($is_internal) {
return $m[0]; // 保持原樣
}
// 外部鏈接處理策略
if ($opts['keep_anchor'] && !$opts['add_nofollow']) {
return $text; // 純文本
}
// 保留標簽但添加屬性
$attrs = $full_attrs;
if ($opts['add_nofollow'] && strpos($attrs, 'nofollow') === false) {
$attrs .= ' rel="nofollow"';
}
if ($opts['target_blank'] && strpos($attrs, 'target=') === false) {
$attrs .= ' target="_blank"';
}
return "{$text}";
},
$body
);
}
| 場景 | 策略 | 配置示例 |
|---|---|---|
| SEO 權重控制 | 刪除外鏈保留文本 | replace_external_links($content, ['mysite.com']) |
| 用戶生成內容(UGC) | 外鏈加 nofollow | replace_external_links_advanced($c, [], ['add_nofollow'=>true]) |
| 安全防範 | 外鏈新窗口+nofollow | ['add_nofollow'=>true, 'target_blank'=>true] |
| 友鏈/合作伙伴 | 白名單放行 | allowed_domains => ['partner.com'] |
更多內容安全技術:《PHP XSS 防護實戰》、《CMS 系統安全架構設計》