採用分層驗證:長度檢查 → 格式正則 → 字元合法性 → DNS MX/A 記錄驗證。比單純正則更精準,避免誤攔合法地址 (如 user+tag@example.com、"quoted@string"@example.com)。
<?php
function validateEmail($email) {
$isValid = true;
// 1. 基礎結構檢查:必須包含 @ 且不在首尾
$atIndex = strrpos($email, "@");
if ($atIndex === false || $atIndex === 0 || $atIndex === strlen($email) - 1) {
return false;
}
$domain = substr($email, $atIndex + 1);
$local = substr($email, 0, $atIndex);
$localLen = strlen($local);
$domainLen = strlen($domain);
// 2. 長度限制 (RFC 5321)
if ($localLen < 1 || $localLen > 64) return false; // Local-part 最長 64 字元
if ($domainLen < 1 || $domainLen > 255) return false; // Domain 最長 255 字元
// 3. Local-part 開頭結尾不得為點號
if ($local[0] === '.' || $local[$localLen - 1] === '.') return false;
// 4. Local-part 不得有連續雙點
if (strpos($local, '..') !== false) return false;
// 5. Domain 字元合法性:字母、數字、連字號、點號
if (!preg_match('/^[A-Za-z0-9\-\.]+$/', $domain)) return false;
// 6. Domain 不得有連續雙點
if (strpos($domain, '..') !== false) return false;
// 7. Local-part 字元合法性 (非引號情況)
// 允許:字母數字 ! # % & ` _ = / $ ' * + ? ^ { | } ~ . -
$localUnquotedPattern = '/^(\\\.|[A-Za-z0-9!#%&`_=\/\$\'*+\?^{}|~.-])+$/';
if (!preg_match($localUnquotedPattern, str_replace("\\\\", "", $local))) {
// 8. 若不符合非引號規則,檢查是否為合法引號字串
// 引號字串允許任意字元 (含空格、@、點號),但需雙引號包裹且內部引號需跳脫
$quotedPattern = '/^"(\\\\"|[^"])+"$/';
if (!preg_match($quotedPattern, str_replace("\\\\", "", $local))) {
return false;
}
}
// 9. DNS 驗證:域名必須存在 MX 或 A 記錄
if (!checkdnsrr($domain, "MX") && !checkdnsrr($domain, "A")) {
return false;
}
return true;
}
| 預期 | 說明 | |
|---|---|---|
| user@example.com | ✅ 通過 | 標準格式 |
| user.name@sub.domain.com | ✅ 通過 | 子域名、點號 |
| user+tag@example.com | ✅ 通過 | Plus addressing (Gmail 等支援) |
| "quoted string"@example.com | ✅ 通過 | 引號字串含空格 |
| "escaped\\"quote"@example.com | ✅ 通過 | 引號內跳脫引號 |
| user@[IPv6:2001:db8::1] | ❌ 失敗 | IP 字面形式未支援 (需擴展) |
| user@.com | ❌ 失敗 | 域名以點號開頭 |
| .user@example.com | ❌ 失敗 | Local-part 以點號開頭 |
| user..name@example.com | ❌ 失敗 | 連續雙點 |
| user@nonexistentdomain.xyz | ❌ 失敗 | DNS 無記錄 |
function validateEmailModern($email) {
// 1. 格式驗證
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
// 2. DNS 驗證 (可選,增加延遲)
$domain = substr(strrchr($email, "@"), 1);
return checkdnsrr($domain, "MX") || checkdnsrr($domain, "A");
}
// Laravel $request->validate(['email' => 'required|email|dns']); // Symfony use Symfony\Component\Validator\Constraints\Email; $constraint = new Email(['mode' => 'strict', 'checkMX' => true]);
composer require egulias/email-validator
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;
use Egulias\EmailValidator\Validation\DNSCheckValidation;
$validator = new EmailValidator();
$validator->isValid('user@example.com', new RFCValidation()); // 格式
$validator->isValid('user@example.com', new DNSCheckValidation()); // DNS
checkdnsrr 會產生網路延遲 (通常 50-500ms),高頻呼叫建議:/^.+@.+\..+$/ 會通過 a@b.c 但擋掉合法的 user+tag@example.comtype="email" 易繞過,後端必須再驗證建議採用「內建過濾器 + 可選 DNS 快取」的組合,兼顧效能、正確性與維護性。