Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7796364

Browse filesBrowse 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: * loose: The pattern from previous Symfony versions (default) * strict: Strictly matching the RFC * html5: The regex used for the HTML5 Element Deprecates the `strict=true` parameter in favour of `mode='strict'`
1 parent efc19fc commit 7796364
Copy full SHA for 7796364

File tree

Expand file treeCollapse file tree

6 files changed

+280
-9
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+280
-9
lines changed

‎UPGRADE-4.1.md

Copy file name to clipboardExpand all lines: UPGRADE-4.1.md
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ Translation
1717
* The `FileDumper::setBackup()` method is deprecated and will be removed in 5.0.
1818
* The `TranslationWriter::disableBackup()` method is deprecated and will be removed in 5.0.
1919

20+
Validator
21+
--------
22+
23+
* The `Email::__construct()` 'strict' property is deprecated and will be removed in 5.0. Use 'mode'=>"strict" instead.
24+
* Calling `EmailValidator::__construct()` method with a boolean parameter is deprecated and will be removed in 5.0, use `EmailValidator("strict")` instead.
25+
2026
Workflow
2127
--------
2228

‎UPGRADE-5.0.md

Copy file name to clipboardExpand all lines: UPGRADE-5.0.md
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ Translation
1717
* The `FileDumper::setBackup()` method has been removed.
1818
* The `TranslationWriter::disableBackup()` method has been removed.
1919

20+
Validator
21+
--------
22+
23+
* The `Email::__construct()` 'strict' property has been removed. Use 'mode'=>"strict" instead.
24+
* Calling `EmailValidator::__construct()` method with a boolean parameter has been removed, use `EmailValidator("strict")` instead.
25+
26+
2027
Workflow
2128
--------
2229

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Validator/Constraints/Email.php
+33Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
*/
2222
class Email extends Constraint
2323
{
24+
public const VALIDATION_MODE_HTML5 = 'html5';
25+
public const VALIDATION_MODE_STRICT = 'strict';
26+
public const VALIDATION_MODE_LOOSE = 'loose';
27+
2428
const INVALID_FORMAT_ERROR = 'bd79c0ab-ddba-46cc-a703-a7a4b08de310';
2529
const MX_CHECK_FAILED_ERROR = 'bf447c1c-0266-4e10-9c6c-573df282e413';
2630
const HOST_CHECK_FAILED_ERROR = '7da53a8b-56f3-4288-bb3e-ee9ede4ef9a1';
@@ -31,8 +35,37 @@ class Email extends Constraint
3135
self::HOST_CHECK_FAILED_ERROR => 'HOST_CHECK_FAILED_ERROR',
3236
);
3337

38+
/**
39+
* @var string[]
40+
*
41+
* @internal
42+
*/
43+
public static $validationModes = array(
44+
self::VALIDATION_MODE_HTML5,
45+
self::VALIDATION_MODE_STRICT,
46+
self::VALIDATION_MODE_LOOSE,
47+
);
48+
3449
public $message = 'This value is not a valid email address.';
3550
public $checkMX = false;
3651
public $checkHost = false;
52+
53+
/**
54+
* @deprecated since version 4.1, to be removed in 5.0. Set mode to "strict" instead.
55+
*/
3756
public $strict;
57+
public $mode;
58+
59+
public function __construct($options = null)
60+
{
61+
if (is_array($options) && array_key_exists('strict', $options)) {
62+
@trigger_error(sprintf('The \'strict\' property is deprecated since version 4.1 and will be removed in 5.0. Use \'mode\'=>"%s" instead.', self::VALIDATION_MODE_STRICT), E_USER_DEPRECATED);
63+
}
64+
65+
if (is_array($options) && array_key_exists('mode', $options) && !in_array($options['mode'], self::$validationModes, true)) {
66+
throw new \InvalidArgumentException('The \'mode\' parameter value is not valid.');
67+
}
68+
69+
parent::__construct($options);
70+
}
3871
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Validator/Constraints/EmailValidator.php
+51-7Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,41 @@
2323
*/
2424
class EmailValidator extends ConstraintValidator
2525
{
26-
private $isStrict;
26+
/**
27+
* @internal
28+
*/
29+
const PATTERN_HTML5 = '/^[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])?)+$/';
30+
/**
31+
* @internal
32+
*/
33+
const PATTERN_LOOSE = '/^.+\@\S+\.\S+$/';
34+
35+
private static $emailPatterns = array(
36+
Email::VALIDATION_MODE_LOOSE => self::PATTERN_LOOSE,
37+
Email::VALIDATION_MODE_HTML5 => self::PATTERN_HTML5,
38+
);
2739

28-
public function __construct(bool $strict = false)
40+
/**
41+
* @var string
42+
*/
43+
private $defaultMode;
44+
45+
/**
46+
* @param string $defaultMode
47+
*/
48+
public function __construct($defaultMode = Email::VALIDATION_MODE_LOOSE)
2949
{
30-
$this->isStrict = $strict;
50+
if (is_bool($defaultMode)) {
51+
@trigger_error(sprintf('Calling `new %s(%s)` is deprecated since version 4.1 and will be removed in 5.0, use `new %s("%s")` instead.', self::class, $defaultMode ? 'true' : 'false', self::class, $defaultMode ? Email::VALIDATION_MODE_STRICT : Email::VALIDATION_MODE_LOOSE), E_USER_DEPRECATED);
52+
53+
$defaultMode = $defaultMode ? Email::VALIDATION_MODE_STRICT : Email::VALIDATION_MODE_LOOSE;
54+
}
55+
56+
if (!in_array($defaultMode, Email::$validationModes, true)) {
57+
throw new \InvalidArgumentException('The "defaultMode" parameter value is not valid.');
58+
}
59+
60+
$this->defaultMode = $defaultMode;
3161
}
3262

3363
/**
@@ -49,11 +79,25 @@ public function validate($value, Constraint $constraint)
4979

5080
$value = (string) $value;
5181

52-
if (null === $constraint->strict) {
53-
$constraint->strict = $this->isStrict;
82+
if (null !== $constraint->strict) {
83+
@trigger_error(sprintf('The %s::$strict property is deprecated since version 4.1 and will be removed in 5.0. Use %s::mode="%s" instead.', Email::class, Email::class, Email::VALIDATION_MODE_STRICT), E_USER_DEPRECATED);
84+
85+
if ($constraint->strict) {
86+
$constraint->mode = Email::VALIDATION_MODE_STRICT;
87+
} else {
88+
$constraint->mode = Email::VALIDATION_MODE_LOOSE;
89+
}
90+
}
91+
92+
if (null === $constraint->mode) {
93+
$constraint->mode = $this->defaultMode;
94+
}
95+
96+
if (!in_array($constraint->mode, Email::$validationModes, true)) {
97+
throw new \InvalidArgumentException(sprintf('The %s::$mode parameter value is not valid.', get_class($constraint)));
5498
}
5599

56-
if ($constraint->strict) {
100+
if (Email::VALIDATION_MODE_STRICT === $constraint->mode) {
57101
if (!class_exists('\Egulias\EmailValidator\EmailValidator')) {
58102
throw new RuntimeException('Strict email validation requires egulias/email-validator ~1.2|~2.0');
59103
}
@@ -75,7 +119,7 @@ public function validate($value, Constraint $constraint)
75119

76120
return;
77121
}
78-
} elseif (!preg_match('/^.+\@\S+\.\S+$/', $value)) {
122+
} elseif (!preg_match(self::$emailPatterns[$constraint->mode], $value)) {
79123
$this->context->buildViolation($constraint->message)
80124
->setParameter('{{ value }}', $this->formatValue($value))
81125
->setCode(Email::INVALID_FORMAT_ERROR)
+45Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Tests\Constraints;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Validator\Constraints\Email;
16+
17+
class EmailTest extends TestCase
18+
{
19+
/**
20+
* @expectedDeprecation The 'strict' property is deprecated since version 4.1 and will be removed in 5.0. Use 'mode'=>"strict" instead.
21+
* @group legacy
22+
*/
23+
public function testLegacyConstructorStrict()
24+
{
25+
$subject = new Email(array('strict' => true));
26+
27+
$this->assertTrue($subject->strict);
28+
}
29+
30+
public function testConstructorStrict()
31+
{
32+
$subject = new Email(array('mode' => Email::VALIDATION_MODE_STRICT));
33+
34+
$this->assertEquals(Email::VALIDATION_MODE_STRICT, $subject->mode);
35+
}
36+
37+
/**
38+
* @expectedException \InvalidArgumentException
39+
* @expectedExceptionMessage The 'mode' parameter value is not valid.
40+
*/
41+
public function testUnknownModesTriggerException()
42+
{
43+
new Email(array('mode' => 'Unknown Mode'));
44+
}
45+
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php
+138-2Lines changed: 138 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,29 @@ class EmailValidatorTest extends ConstraintValidatorTestCase
2323
{
2424
protected function createValidator()
2525
{
26-
return new EmailValidator(false);
26+
return new EmailValidator(Email::VALIDATION_MODE_LOOSE);
27+
}
28+
29+
/**
30+
* @expectedDeprecation Calling `new Symfony\Component\Validator\Constraints\EmailValidator(true)` is deprecated since version 4.1 and will be removed in 5.0, use `new Symfony\Component\Validator\Constraints\EmailValidator("strict")` instead.
31+
* @group legacy
32+
*/
33+
public function testLegacyValidatorConstructorStrict()
34+
{
35+
$this->validator = new EmailValidator(true);
36+
$this->validator->initialize($this->context);
37+
$this->validator->validate('example@localhost', new Email());
38+
39+
$this->assertNoViolation();
40+
}
41+
42+
/**
43+
* @expectedException \InvalidArgumentException
44+
* @expectedExceptionMessage The "defaultMode" parameter value is not valid.
45+
*/
46+
public function testUnknownDefaultModeTriggerException()
47+
{
48+
new EmailValidator('Unknown Mode');
2749
}
2850

2951
public function testNullIsValid()
@@ -64,6 +86,31 @@ public function getValidEmails()
6486
array('fabien@symfony.com'),
6587
array('example@example.co.uk'),
6688
array('fabien_potencier@example.fr'),
89+
array('example@example.co..uk'),
90+
array('{}~!@!@£$%%^&*().!@£$%^&*()'),
91+
array('example@example.co..uk'),
92+
array('example@-example.com'),
93+
array(sprintf('example@%s.com', str_repeat('a', 64))),
94+
);
95+
}
96+
97+
/**
98+
* @dataProvider getValidEmailsHtml5
99+
*/
100+
public function testValidEmailsHtml5($email)
101+
{
102+
$this->validator->validate($email, new Email(array('mode' => Email::VALIDATION_MODE_HTML5)));
103+
104+
$this->assertNoViolation();
105+
}
106+
107+
public function getValidEmailsHtml5()
108+
{
109+
return array(
110+
array('fabien@symfony.com'),
111+
array('example@example.co.uk'),
112+
array('fabien_potencier@example.fr'),
113+
array('{}~!@example.com'),
67114
);
68115
}
69116

@@ -94,6 +141,95 @@ public function getInvalidEmails()
94141
);
95142
}
96143

144+
/**
145+
* @dataProvider getInvalidHtml5Emails
146+
*/
147+
public function testInvalidHtml5Emails($email)
148+
{
149+
$constraint = new Email(
150+
array(
151+
'message' => 'myMessage',
152+
'mode' => Email::VALIDATION_MODE_HTML5,
153+
)
154+
);
155+
156+
$this->validator->validate($email, $constraint);
157+
158+
$this->buildViolation('myMessage')
159+
->setParameter('{{ value }}', '"'.$email.'"')
160+
->setCode(Email::INVALID_FORMAT_ERROR)
161+
->assertRaised();
162+
}
163+
164+
public function getInvalidHtml5Emails()
165+
{
166+
return array(
167+
array('example'),
168+
array('example@'),
169+
array('example@localhost'),
170+
array('example@example.co..uk'),
171+
array('foo@example.com bar'),
172+
array('example@example.'),
173+
array('example@.fr'),
174+
array('@example.com'),
175+
array('example@example.com;example@example.com'),
176+
array('example@.'),
177+
array(' example@example.com'),
178+
array('example@ '),
179+
array(' example@example.com '),
180+
array(' example @example .com '),
181+
array('example@-example.com'),
182+
array(sprintf('example@%s.com', str_repeat('a', 64))),
183+
);
184+
}
185+
186+
public function testModeStrict()
187+
{
188+
$constraint = new Email(array('mode' => Email::VALIDATION_MODE_STRICT));
189+
190+
$this->validator->validate('example@localhost', $constraint);
191+
192+
$this->assertNoViolation();
193+
}
194+
195+
public function testModeHtml5()
196+
{
197+
$constraint = new Email(array('mode' => Email::VALIDATION_MODE_HTML5));
198+
199+
$this->validator->validate('example@example..com', $constraint);
200+
201+
$this->buildViolation('This value is not a valid email address.')
202+
->setParameter('{{ value }}', '"example@example..com"')
203+
->setCode(Email::INVALID_FORMAT_ERROR)
204+
->assertRaised();
205+
}
206+
207+
public function testModeLoose()
208+
{
209+
$constraint = new Email(array('mode' => Email::VALIDATION_MODE_LOOSE));
210+
211+
$this->validator->validate('example@example..com', $constraint);
212+
213+
$this->assertNoViolation();
214+
}
215+
216+
/**
217+
* @expectedException \InvalidArgumentException
218+
* @expectedExceptionMessage The Symfony\Component\Validator\Constraints\Email::$mode parameter value is not valid.
219+
*/
220+
public function testUnknownModesOnValidateTriggerException()
221+
{
222+
$constraint = new Email();
223+
$constraint->mode = 'Unknown Mode';
224+
225+
$this->validator->validate('example@example..com', $constraint);
226+
}
227+
228+
/**
229+
* @expectedDeprecation The 'strict' property is deprecated since version 4.1 and will be removed in 5.0. Use 'mode'=>"strict" instead.
230+
* @expectedDeprecation The Symfony\Component\Validator\Constraints\Email::$strict property is deprecated since version 4.1 and will be removed in 5.0. Use Symfony\Component\Validator\Constraints\Email::mode="strict" instead.
231+
* @group legacy
232+
*/
97233
public function testStrict()
98234
{
99235
$constraint = new Email(array('strict' => true));
@@ -110,7 +246,7 @@ public function testStrictWithInvalidEmails($email)
110246
{
111247
$constraint = new Email(array(
112248
'message' => 'myMessage',
113-
'strict' => true,
249+
'mode' => Email::VALIDATION_MODE_STRICT,
114250
));
115251

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

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.