From 23047d978f270673823ec861d1a4d1b0c7966ef2 Mon Sep 17 00:00:00 2001 From: Guillem Fondin Date: Sat, 15 Oct 2022 15:00:51 +0200 Subject: [PATCH] [Validator] new email validation option to match with w3c official specification --- src/Symfony/Component/Validator/CHANGELOG.md | 1 + .../Component/Validator/Constraints/Email.php | 2 + .../Validator/Constraints/EmailValidator.php | 2 + .../Validator/Tests/Constraints/EmailTest.php | 7 ++++ .../Tests/Constraints/EmailValidatorTest.php | 38 +++++++++++++++++++ 5 files changed, 50 insertions(+) diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index 541a40a3e00f..ce537fe30fa3 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 6.2 --- + * Add option `Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD` with "html5-allow-no-tld" e-mail validation mode, to match with the W3C official specification * Add method `getCause()` to `ConstraintViolationInterface` * Add the `When` constraint and validator * Deprecate the "loose" e-mail validation mode, use "html5" instead diff --git a/src/Symfony/Component/Validator/Constraints/Email.php b/src/Symfony/Component/Validator/Constraints/Email.php index 4340e7a7217d..55399f977d28 100644 --- a/src/Symfony/Component/Validator/Constraints/Email.php +++ b/src/Symfony/Component/Validator/Constraints/Email.php @@ -25,6 +25,7 @@ #[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class Email extends Constraint { + public const VALIDATION_MODE_HTML5_ALLOW_NO_TLD = 'html5-allow-no-tld'; public const VALIDATION_MODE_HTML5 = 'html5'; public const VALIDATION_MODE_STRICT = 'strict'; /** @@ -35,6 +36,7 @@ class Email extends Constraint public const INVALID_FORMAT_ERROR = 'bd79c0ab-ddba-46cc-a703-a7a4b08de310'; public const VALIDATION_MODES = [ + self::VALIDATION_MODE_HTML5_ALLOW_NO_TLD, self::VALIDATION_MODE_HTML5, self::VALIDATION_MODE_STRICT, self::VALIDATION_MODE_LOOSE, diff --git a/src/Symfony/Component/Validator/Constraints/EmailValidator.php b/src/Symfony/Component/Validator/Constraints/EmailValidator.php index 44dc30b171d3..6ff845bb6f71 100644 --- a/src/Symfony/Component/Validator/Constraints/EmailValidator.php +++ b/src/Symfony/Component/Validator/Constraints/EmailValidator.php @@ -24,12 +24,14 @@ */ class EmailValidator extends ConstraintValidator { + private const PATTERN_HTML5_ALLOW_NO_TLD = '/^[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])?)*$/'; private 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])?)+$/'; private const PATTERN_LOOSE = '/^.+\@\S+\.\S+$/'; private const EMAIL_PATTERNS = [ Email::VALIDATION_MODE_LOOSE => self::PATTERN_LOOSE, Email::VALIDATION_MODE_HTML5 => self::PATTERN_HTML5, + Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD => self::PATTERN_HTML5_ALLOW_NO_TLD, ]; private string $defaultMode; diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EmailTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EmailTest.php index d70c6bd8b904..b181e61edec7 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EmailTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EmailTest.php @@ -26,6 +26,13 @@ public function testConstructorStrict() $this->assertEquals(Email::VALIDATION_MODE_STRICT, $subject->mode); } + public function testConstructorHtml5AllowNoTld() + { + $subject = new Email(['mode' => Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD]); + + $this->assertEquals(Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD, $subject->mode); + } + public function testUnknownModesTriggerException() { $this->expectException(InvalidArgumentException::class); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php index 65a449de8c20..d259a3f8a01c 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php @@ -232,6 +232,35 @@ public function getInvalidHtml5Emails() ]; } + /** + * @dataProvider getInvalidAllowNoTldEmails + */ + public function testInvalidAllowNoTldEmails($email) + { + $constraint = new Email([ + 'message' => 'myMessage', + 'mode' => Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD, + ]); + + $this->validator->validate($email, $constraint); + + $this->buildViolation('myMessage') + ->setParameter('{{ value }}', '"'.$email.'"') + ->setCode(Email::INVALID_FORMAT_ERROR) + ->assertRaised(); + } + + public function getInvalidAllowNoTldEmails() + { + return [ + ['example bar'], + ['example@'], + ['example@ bar'], + ['example@localhost bar'], + ['foo@example.com bar'], + ]; + } + public function testModeStrict() { $constraint = new Email(['mode' => Email::VALIDATION_MODE_STRICT]); @@ -253,6 +282,15 @@ public function testModeHtml5() ->assertRaised(); } + public function testModeHtml5AllowNoTld() + { + $constraint = new Email(['mode' => Email::VALIDATION_MODE_HTML5_ALLOW_NO_TLD]); + + $this->validator->validate('example@example', $constraint); + + $this->assertNoViolation(); + } + /** * @group legacy */