Skip to content

Commit 05f981c

Browse files
committed
[Validator] Html5 Email Validation
Currently we only support a very loose validation. There is now a standard HTML5 element with matching regex. This will add the ability to set a "Mode" on the email validator. The mode will change the validation that is applied to the field as a whole. These modes are: * RFC - Previously called "strict" * HTML5 * Loose
1 parent 522d079 commit 05f981c

File tree

4 files changed

+395
-20
lines changed

4 files changed

+395
-20
lines changed

src/Symfony/Component/Validator/Constraints/Email.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,13 @@ class Email extends Constraint
3131
self::HOST_CHECK_FAILED_ERROR => 'HOST_CHECK_FAILED_ERROR',
3232
);
3333

34+
const VALIDATION_MODE_HTML5 = 'html5';
35+
const VALIDATION_MODE_STRICT = 'strict';
36+
const VALIDATION_MODE_LOOSE = 'loose';
37+
3438
public $message = 'This value is not a valid email address.';
3539
public $checkMX = false;
3640
public $checkHost = false;
3741
public $strict;
42+
public $mode;
3843
}

src/Symfony/Component/Validator/Constraints/EmailValidator.php

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@
2424
class EmailValidator extends ConstraintValidator
2525
{
2626
/**
27-
* @var bool
27+
* @var string
2828
*/
29-
private $isStrict;
29+
private $defaultMode;
3030

31-
public function __construct($strict = false)
31+
public function __construct($strict = false, $defaultMode = 'html5')
3232
{
33-
$this->isStrict = $strict;
33+
$this->defaultMode = $defaultMode;
34+
35+
if (true === $strict) {
36+
$this->defaultMode = Email::VALIDATION_MODE_STRICT;
37+
}
3438
}
3539

3640
/**
@@ -52,11 +56,11 @@ public function validate($value, Constraint $constraint)
5256

5357
$value = (string) $value;
5458

55-
if (null === $constraint->strict) {
56-
$constraint->strict = $this->isStrict;
59+
if (null === $constraint->mode) {
60+
$constraint->mode = $this->defaultMode;
5761
}
5862

59-
if ($constraint->strict) {
63+
if (Email::VALIDATION_MODE_STRICT === $constraint->mode) {
6064
if (!class_exists('\Egulias\EmailValidator\EmailValidator')) {
6165
throw new RuntimeException('Strict email validation requires egulias/email-validator ~1.2|~2.0');
6266
}
@@ -78,13 +82,27 @@ public function validate($value, Constraint $constraint)
7882

7983
return;
8084
}
81-
} elseif (!preg_match('/^.+\@\S+\.\S+$/', $value)) {
82-
$this->context->buildViolation($constraint->message)
83-
->setParameter('{{ value }}', $this->formatValue($value))
84-
->setCode(Email::INVALID_FORMAT_ERROR)
85-
->addViolation();
85+
} elseif (Email::VALIDATION_MODE_LOOSE === $constraint->mode) {
86+
if (!preg_match('/^.+\@\S+\.\S+$/', $value)) {
87+
$this->context->buildViolation($constraint->message)
88+
->setParameter('{{ value }}', $this->formatValue($value))
89+
->setCode(Email::INVALID_FORMAT_ERROR)
90+
->addViolation();
8691

87-
return;
92+
return;
93+
}
94+
} elseif (Email::VALIDATION_MODE_HTML5 === $constraint->mode) {
95+
if (!preg_match(
96+
'/^[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])?)+$/',
97+
$value
98+
)) {
99+
$this->context->buildViolation($constraint->message)
100+
->setParameter('{{ value }}', $this->formatValue($value))
101+
->setCode(Email::INVALID_FORMAT_ERROR)
102+
->addViolation();
103+
104+
return;
105+
}
88106
}
89107

90108
$host = (string) substr($value, strrpos($value, '@') + 1);

src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class EmailValidatorTest extends ConstraintValidatorTestCase
2323
{
2424
protected function createValidator()
2525
{
26-
return new EmailValidator(false);
26+
return new EmailValidator(false );
2727
}
2828

2929
public function testNullIsValid()
@@ -53,8 +53,16 @@ public function testExpectsStringCompatibleType()
5353
*/
5454
public function testValidEmails($email)
5555
{
56-
$this->validator->validate($email, new Email());
56+
$this->validator->validate($email, new Email(array('mode' => 'loose')));
57+
$this->assertNoViolation();
58+
}
5759

60+
/**
61+
* @dataProvider getValidEmails
62+
*/
63+
public function testValidEmailsHtml5($email)
64+
{
65+
$this->validator->validate($email, new Email(array('mode' => 'html5')));
5866
$this->assertNoViolation();
5967
}
6068

@@ -68,9 +76,27 @@ public function getValidEmails()
6876
}
6977

7078
/**
71-
* @dataProvider getInvalidEmails
79+
* @dataProvider getInvalidLooseEmails
80+
*/
81+
public function testInvalidLooseEmails($email)
82+
{
83+
$constraint = new Email(array(
84+
'message' => 'myMessage',
85+
'mode' => 'loose',
86+
));
87+
88+
$this->validator->validate($email, $constraint);
89+
90+
$this->buildViolation('myMessage')
91+
->setParameter('{{ value }}', '"'.$email.'"')
92+
->setCode(Email::INVALID_FORMAT_ERROR)
93+
->assertRaised();
94+
}
95+
96+
/**
97+
* @dataProvider getInvalidHtml5Emails
7298
*/
73-
public function testInvalidEmails($email)
99+
public function testInvalidHtml5Emails($email)
74100
{
75101
$constraint = new Email(array(
76102
'message' => 'myMessage',
@@ -84,19 +110,40 @@ public function testInvalidEmails($email)
84110
->assertRaised();
85111
}
86112

87-
public function getInvalidEmails()
113+
public function getInvalidLooseEmails()
114+
{
115+
return array(
116+
array('example'),
117+
array('example@'),
118+
array('example@localhost'),
119+
array('foo@example.com bar'),
120+
);
121+
}
122+
123+
public function getInvalidHtml5Emails()
88124
{
89125
return array(
90126
array('example'),
91127
array('example@'),
92128
array('example@localhost'),
129+
array('example@example.co..uk'),
93130
array('foo@example.com bar'),
131+
array('a@a.'),
132+
array('a@.nl'),
133+
array('@a.nl'),
134+
array('a@a.nl;a@a.nl'),
135+
array('a@.'),
136+
array(' a@a.nl'),
137+
array('a@ '),
138+
array(' a@a.nl '),
139+
array(' a @a .nl '),
140+
array('a@-invalid.hostname'),
94141
);
95142
}
96143

97144
public function testStrict()
98145
{
99-
$constraint = new Email(array('strict' => true));
146+
$constraint = new Email(array('mode' => Email::VALIDATION_MODE_STRICT));
100147

101148
$this->validator->validate('example@localhost', $constraint);
102149

@@ -110,7 +157,7 @@ public function testStrictWithInvalidEmails($email)
110157
{
111158
$constraint = new Email(array(
112159
'message' => 'myMessage',
113-
'strict' => true,
160+
'mode' => Email::VALIDATION_MODE_STRICT,
114161
));
115162

116163
$this->validator->validate($email, $constraint);

0 commit comments

Comments
 (0)