原始碼使用已棄用的 eregi_replace,現代 PHP 應使用 PCRE 函數族群 (preg_match, preg_replace) 並注意 Unicode、國際化域名支援。
<?php
// 原始碼片段
$a = $_POST['a'] ?? '';
$a1 = eregi_replace('(^[_.][:alnum:]-]+@([[:alnum:]][[:alnum:]-]*.)+[[:alpha:]]{2,3}$)', ' <a href="mailto:\1">\1</a> ', $a);
$a2 = eregi_replace('([[:alnum:]]+://([[:alnum:]][[:alnum:]-]*.)+[[:alpha:]]{2,3}([#?/][^#?/][:space:]][^[:space:]]*)?)', '<a href="\1" target="_blank">\1</a>', $a);
?>
問題:
eregi_replace 自 PHP 5.3 棄用,7.0 移除。[:alnum:] 在 PCRE 中需寫作 [[:alnum:]] (POSIX) 或直接用 \w / [a-zA-Z0-9]。+ 標籤、國際化域名、長 TLD (如 .museum, .technology)。https、IP 位址、埠號、路徑查詢片段完整語法。<?php
function autoLink($text) {
// 1. 先 HTML 轉義,防止 XSS
$text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
// 2. Email 連結化 (簡化版,生產建議用專用庫)
// RFC 5322 完整正則極其複雜,這裡採用實用子集
$emailPattern = '/
(^|[\s\>\(]) # 開頭或分隔字元
([a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+ # 本地部
@
[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])? # 域名標籤
(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+ # 多級域名
)
/x';
$text = preg_replace($emailPattern, '$1<a href="mailto:$2">$2</a>', $text);
// 3. URL 連結化 (支援 http/https/ftp, 域名/IP, 埠, 路徑, 查詢, 片段)
$urlPattern = '/
(^|[\s\>\(]) # 開頭或分隔字元
(
(?:https?|ftp):\/\/ # 協定
(?: # 主機
(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,} # 域名
|
\d{1,3}(?:\.\d{1,3}){3} # IPv4
|
\[[0-9a-fA-F:]+\] # IPv6
)
(?::\d+)? # 埠號
(?:[\/?#][^\s]*)? # 路徑/查詢/片段
)
/x';
$text = preg_replace($urlPattern, '$1<a href="$2" target="_blank" rel="noopener noreferrer">$2</a>', $text);
return $text;
}
// 使用
$input = "聯絡我: test@example.com 或訪問 https://example.com/path?query=1#section";
echo autoLink($input);
?>
<?php
// Email 驗證
$email = 'user@example.com';
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "格式有效";
}
// URL 驗證
$url = 'https://example.com/path';
if (filter_var($url, FILTER_VALIDATE_URL)) {
echo "格式有效";
}
// 可加旗標限制協定
if (filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)) { ... }
?>
<?php
// IDN 轉換需 intl 擴充套件
function validateEmailIdn($email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
list($local, $domain) = explode('@', $email, 2);
// 轉換為 ASCII (Punycode)
$domainAscii = idn_to_ascii($domain, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
if ($domainAscii === false) return false;
// 再次驗證 ASCII 版本
return filter_var($local . '@' . $domainAscii, FILTER_VALIDATE_EMAIL);
}
?>
| 場景 | 建議 |
|---|---|
| 使用者輸入顯示 | 必先 htmlspecialchars,再連結化 |
| 表單驗證 | 前端 HTML5 type="email/url" + 後端 filter_var |
| 大量文字處理 | 預編譯正則、避免過度捕獲群組、考慮 preg_replace_callback |
| 郵件/連結提取 | 使用成熟庫:league/uri, egulias/email-validator |
正則表達式威力強大但易寫錯、難維護。現代 PHP 開發優先使用內建驗證器 (filter_var) 與成熟庫,僅在特定提取/轉換場景下撰寫精簡正則。