首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

正則表達式驗證 Email 與 URL:PHP 實作與常見陷阱

     編輯:PHP 相關知識

從 eregi 到 preg_match:正則驗證的演進與最佳實踐

原始碼使用已棄用的 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);
?>

問題:

  1. eregi_replace 自 PHP 5.3 棄用,7.0 移除。
  2. 字元類別 [:alnum:] 在 PCRE 中需寫作 [[:alnum:]] (POSIX) 或直接用 \w / [a-zA-Z0-9]。
  3. Email 正則過於簡單:不支援 + 標籤、國際化域名、長 TLD (如 .museum, .technology)。
  4. URL 正則未涵蓋 https、IP 位址、埠號、路徑查詢片段完整語法。
  5. 直接將使用者輸入回傳 HTML 且未轉義,極高風險 XSS。

現代化重寫

<?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);
?>

驗證而非連結化:filter_var 最佳實踐

<?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)) { ... }
?>

進階:國際化域名 (IDN) 支援

<?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) 與成熟庫,僅在特定提取/轉換場景下撰寫精簡正則。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved