用戶註冊是 Web 應用的入口,服務端嚴格驗證是安全基石。本文提供生產級正則表達式,涵蓋用戶名、密碼、郵箱、手機號四大核心字段,並說明常見誤區與擴展建議。
規則:2-20 位,字母/數字/下劃線,首字符不能為數字(可選),支援中文(可選)。
<?php
/**
* 驗證用戶名
* @param string $username
* @return bool
*/
function is_username($username) {
$len = strlen($username);
// 基礎版:字母數字下劃線,2-20 位
if (!preg_match('/^[a-zA-Z0-9_]{2,20}$/', $username)) {
return false;
}
// 進階版:首字母不為數字,允許中文(UTF-8)
// if (!preg_match('/^[a-zA-Z_\x{4e00}-\x{9fa5}][a-zA-Z0-9_\x{4e00}-\x{9fa5}]{1,19}$/u', $username)) return false;
return true;
}
?>
正則解析:
^ $:全字符串匹配[a-zA-Z0-9_]:等價於 \w(ASCII 模式下){2,20}:長度限制u 修飾符:啟用 UTF-8 模式,支援 \x{4e00}-\x{9fa5} 中文範圍建議規則:8-20 位,包含大小寫字母、數字、特殊字符至少三類。
<?php
function is_password($pwd, $min = 8, $max = 20) {
$len = strlen($pwd);
if ($len < $min || $len > $max) return false;
// 至少包含三類:小寫、大寫、數字、特殊符號
$checks = [
'/[a-z]/', // 小寫
'/[A-Z]/', // 大寫
'/\d/', // 數字
'/[~!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/' // 特殊字符
];
$count = 0;
foreach ($checks as $regex) {
if (preg_match($regex, $pwd)) $count++;
}
return $count >= 3;
}
// 簡易版:僅長度+可見字符
function is_pwd_simple($v, $min=6, $max=20) {
return preg_match('/^[\x21-\x7E]{' . $min . ',' . $max . '}$/', $v);
}
?>
RFC 5322 完整規範極其複雜,實際開發採用「實用子集」即可:
<?php
function is_email($email) {
// 實用版:本地部分允許字母數字._%+-,域名標準格式
$pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
return (bool)preg_match($pattern, $email);
}
// 更嚴格版(支援國際化域名 IDN 需配合 idn_to_ascii)
function is_email_strict($email) {
if (strlen($email) > 254) return false;
// 使用 PHP 內建過濾器 (PHP 5.2+)
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
?>
推薦:直接使用 filter_var($email, FILTER_VALIDATE_EMAIL),內建處理了大量邊界情況。
規則:11 位,以 1 開頭,第二位 3-9,其餘任意數字(覆蓋三大運營商現有號段)。
<?php
function is_mobile_cn($mobile) {
return preg_match('/^1[3-9]\d{9}$/', $mobile);
}
// 如果需精確到具體運營商號段(需定期更新)
function is_mobile_cn_strict($mobile) {
$pattern = '/^1(?:3[\d]|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8[\d]|9[189])\d{8}$/';
return preg_match($pattern, $mobile);
}
?>
<?php
class RegisterValidator {
public function validate(array $data): array {
$errors = [];
if (empty($data['username']) || !$this->username($data['username'])) {
$errors['username'] = '用戶名 2-20 位字母數字下劃線';
}
if (empty($data['password']) || !$this->password($data['password'])) {
$errors['password'] = '密碼 8-20 位,含大小寫數字特殊符號至少三類';
}
if (empty($data['email']) || !$this->email($data['email'])) {
$errors['email'] = '郵箱格式不正確';
}
if (!empty($data['mobile']) && !$this->mobile($data['mobile'])) {
$errors['mobile'] = '手機號格式不正確';
}
return $errors;
}
private function username($v) { return preg_match('/^[a-zA-Z0-9_]{2,20}$/', $v); }
private function password($v) {
if (strlen($v) < 8 || strlen($v) > 20) return false;
$c = 0;
foreach (['/[a-z]/','/[A-Z]/','/\d/','/[~!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/'] as $r) {
if (preg_match($r, $v)) $c++;
}
return $c >= 3;
}
private function email($v) { return filter_var($v, FILTER_VALIDATE_EMAIL); }
private function mobile($v) { return preg_match('/^1[3-9]\d{9}$/', $v); }
}
// 使用
$validator = new RegisterValidator();
$errors = $validator->validate($_POST);
if ($errors) {
// 返回錯誤
}
?>
filter_var 足夠 99% 場景。usleep(100000)),避免用戶枚舉。