Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 503effe

Browse files
committed
feature #24442 [Validator] Html5 Email Validation (PurpleBooth)
This PR was merged into the 4.1-dev branch. Discussion ---------- [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'` | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | #21531 | License | MIT | Doc PR | symfony/symfony-docs#8487 <!-- - Bug fixes must be submitted against the lowest branch where they apply (lowest branches are regularly merged to upper ones so they get the fixes too). - Features and deprecations must be submitted against the 3.4, legacy code removals go to the master branch. - Please fill in this template according to the PR you're about to submit. - Replace this comment by a description of what your PR is solving. --> Commits ------- cf04108 [Validator] Html5 Email Validation
2 parents efc19fc + cf04108 commit 503effe

File tree

6 files changed

+280
-9
lines changed

6 files changed

+280
-9
lines changed

UPGRADE-4.1.md

Lines 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

Lines 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

Lines 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

Lines 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)
Lines 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 <[email protected]>
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

Lines 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
6587
6688
89+
90+
array('{}~!@!@£$%%^&*().!@£$%^&*()'),
91+
92+
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+
111+
112+
113+
array('{}[email protected]'),
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+
171+
array('[email protected] bar'),
172+
array('example@example.'),
173+
174+
array('@example.com'),
175+
176+
array('example@.'),
177+
array(' [email protected]'),
178+
array('example@ '),
179+
array(' [email protected] '),
180+
array(' example @example .com '),
181+
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('[email protected]', $constraint);
200+
201+
$this->buildViolation('This value is not a valid email address.')
202+
->setParameter('{{ value }}', '"[email protected]"')
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('[email protected]', $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('[email protected]', $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)