在寻找一个好的电子邮件验证例程后,我发现了一个类似问题的答案,并认为它看起来是最可能的候选人。我实现了以下类用于电子邮件验证(它继承的RegexMatch类根据关联配置数组的'needle'键提供的正则表达式验证字符串):
class Email extends RegexMatch implements iface'Prop
{
const
/**
* Regular expression for validating email addresses
*
* This regex is meant to validate against RFC 5322 and was taken from
* a post on Stack Overflow regarding email validation (see the links)
*
* @link http://www.ietf.org/rfc/rfc5322.txt, https://stackoverflow.com/questions/201323/what-is-the-best-regular-expression-for-validating-email-addresses/1917982#1917982
*/
PATTERN = '
/(?(DEFINE)
(?<address> (?&mailbox) | (?&group))
(?<mailbox> (?&name_addr) | (?&addr_spec))
(?<name_addr> (?&display_name)? (?&angle_addr))
(?<angle_addr> (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
(?<group> (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ;
(?&CFWS)?)
(?<display_name> (?&phrase))
(?<mailbox_list> (?&mailbox) (?: , (?&mailbox))*)
(?<addr_spec> (?&local_part) '@ (?&domain))
(?<local_part> (?&dot_atom) | (?"ed_string))
(?<domain> (?&dot_atom) | (?&domain_literal))
(?<domain_literal> (?&CFWS)? '[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
'] (?&CFWS)?)
(?<dcontent> (?&dtext) | (?"ed_pair))
(?<dtext> (?&NO_WS_CTL) | ['x21-'x5a'x5e-'x7e])
(?<atext> (?&ALPHA) | (?&DIGIT) | [!#'$%&''*+-'/=?^_`{|}~])
(?<atom> (?&CFWS)? (?&atext)+ (?&CFWS)?)
(?<dot_atom> (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
(?<dot_atom_text> (?&atext)+ (?: '. (?&atext)+)*)
(?<text> ['x01-'x09'x0b'x0c'x0e-'x7f])
(?<quoted_pair> '' (?&text))
(?<qtext> (?&NO_WS_CTL) | ['x21'x23-'x5b'x5d-'x7e])
(?<qcontent> (?&qtext) | (?"ed_pair))
(?<quoted_string> (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
(?&FWS)? (?&DQUOTE) (?&CFWS)?)
(?<word> (?&atom) | (?"ed_string))
(?<phrase> (?&word)+)
# Folding white space
(?<FWS> (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
(?<ctext> (?&NO_WS_CTL) | ['x21-'x27'x2a-'x5b'x5d-'x7e])
(?<ccontent> (?&ctext) | (?"ed_pair) | (?&comment))
(?<comment> '( (?: (?&FWS)? (?&ccontent))* (?&FWS)? ') )
(?<CFWS> (?: (?&FWS)? (?&comment))*
(?: (?:(?&FWS)? (?&comment)) | (?&FWS)))
# No whitespace control
(?<NO_WS_CTL> ['x01-'x08'x0b'x0c'x0e-'x1f'x7f])
(?<ALPHA> [A-Za-z])
(?<DIGIT> [0-9])
(?<CRLF> 'x0d 'x0a)
(?<DQUOTE> ")
(?<WSP> ['x20'x09])
)
(?&address)/x';
public function setConfig (array $config = array ())
{
$config = array_merge ($config, array ('needle' => self::PATTERN));
return (parent::setConfig ($config));
}
public function isValid ()
{
return ((is_null ($this -> getData ()))
|| (parent::isValid ()));
}
}
我还构建了一个PHPUnit测试,该测试针对从各种来源(主要是Wikipedia)挑选的有效和无效电子邮件地址的各种排列运行这个类。
这个类似乎在很多更普通的情况下都能起作用,但它遇到了问题,因为它传递了一些应该是无效的电子邮件,并且失败了一些应该是好的。我把它们列在下面:
-
much."more' unusual"@example.com
(失败,应该有效) -
"(),:;<>[']@example.com
(通过,应该无效) -
just"not"right@example.com
(通过,应该无效) -
A@b@c@example.com
(通过,应该无效) -
this' is'"really'"not''allowed@example.com
(通过,应该是无效的)
PHP似乎正确地解析了正则表达式,它不会发出任何错误、警告或通知。另外,我所有的其他测试用例(7个其他有效地址和2个其他无效地址)都通过或失败了,所以我怀疑这是因为我的PHP版本(5.3.8)不支持这里使用的regex语法。但是我同时得到了假阳性和假阴性,显然有什么地方出了问题。要么是我的测试数据不正确(正如我所说的,我主要是从维基百科中剔除的),要么是正则表达式在某种程度上是不正确的。
上面输入的正则表达式是否正确?如果不是,需要做哪些修正?如果它是正确的,那么我的测试用例有什么问题吗?
EDIT:我还忘了提,因为这是一个验证类,它只需要传递包含电子邮件地址的字符串,而不需要传递其他字符串。我不想在非电子邮件地址数据中传递包含有效电子邮件地址的字符串。我知道你通过使用^pattern_goes_here$
来做到这一点,但是这个正则表达式比我过去使用过的大多数正则表达式更高级,我不确定^和$应该去哪里。如果你也能帮忙的话,我会很感激的。
完全验证电子邮件地址是一件非常棘手的事情。
下面是一个列表,包含了测试,展示了解决这个问题的不同方法,但没有一种方法能通过所有的情况。
http://fightingforalostcause.net/misc/2006/compare-email-regex.php我强烈建议你使用目前得分最高的表达式是PHP的filter_var()使用的表达式,它基于Michael Rushton的正则表达式
filter_var()
如果您想添加^
和$
锚点,这里将是您的位置:
^(?&address)$ /x';
您还需要验证您的电子邮件测试用例资源。我更相信那些正则表达式子程序,因为有人通过翻译RFC中的BNF声明来编写它。