From 550cf7ea099c9f2c8f9b7fb5e9f1e26603f54c0c Mon Sep 17 00:00:00 2001 From: Ash Allen Date: Mon, 6 Oct 2025 22:05:03 +0100 Subject: [PATCH 1/4] Added `Email::domainIs()` and `Email::domainIsNot()` rules. --- src/Illuminate/Validation/Rules/Email.php | 49 +++++++++++++++ tests/Validation/ValidationEmailRuleTest.php | 64 ++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/src/Illuminate/Validation/Rules/Email.php b/src/Illuminate/Validation/Rules/Email.php index bb803e8a15cc..53d8d93e60eb 100644 --- a/src/Illuminate/Validation/Rules/Email.php +++ b/src/Illuminate/Validation/Rules/Email.php @@ -7,6 +7,7 @@ use Illuminate\Contracts\Validation\ValidatorAwareRule; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Str; use Illuminate\Support\Traits\Conditionable; use Illuminate\Support\Traits\Macroable; use InvalidArgumentException; @@ -21,6 +22,8 @@ class Email implements Rule, DataAwareRule, ValidatorAwareRule public bool $nativeValidationWithUnicodeAllowed = false; public bool $rfcCompliant = false; public bool $strictRfcCompliant = false; + private array $allowedDomains = []; + private array $disallowedDomains = []; /** * The validator performing the validation. @@ -162,6 +165,28 @@ public function withNativeValidation(bool $allowUnicode = false) return $this; } + /** + * @param array $domains + * @return $this + */ + public function domainIs($domains) + { + $this->allowedDomains = $domains; + + return $this; + } + + /** + * @param array $domains + * @return $this + */ + public function domainIsNot($domains) + { + $this->disallowedDomains = $domains; + + return $this; + } + /** * Specify additional validation rules that should be merged with the default rules during validation. * @@ -245,6 +270,30 @@ protected function buildValidationRules() $rules = ['email']; } + if ($this->allowedDomains) { + $rules[] = function ($attribute, $value, $fail) { + $domain = explode('@', $value)[1]; + + $isAllowed = Str::of($domain)->is($this->allowedDomains, true); + + if (! $isAllowed) { + $fail('The :attribute must be a valid email address.'); + } + }; + } + + if ($this->disallowedDomains) { + $rules[] = function ($attribute, $value, $fail) { + $domain = explode('@', $value)[1]; + + $isDisallowed = Str::of($domain)->is($this->disallowedDomains, true); + + if ($isDisallowed) { + $fail('The :attribute must be a valid email address.'); + } + }; + } + return array_merge(array_filter($rules), $this->customRules); } diff --git a/tests/Validation/ValidationEmailRuleTest.php b/tests/Validation/ValidationEmailRuleTest.php index cbff32a5edb1..d3de20244745 100644 --- a/tests/Validation/ValidationEmailRuleTest.php +++ b/tests/Validation/ValidationEmailRuleTest.php @@ -876,6 +876,70 @@ public function testValidationMessages() ); } + public function testDomainIs() + { + $this->passes( + (new Email())->domainIs(['example.com', 'test.com']), + 'passes@example.com', + ); + + $this->passes( + (new Email())->domainIs(['*']), + 'passes@example.com', + ); + + $this->passes( + (new Email())->domainIs(['*example.com']), + 'passes@example.com', + ); + + $this->passes( + (new Email())->domainIs(['*ex*le.com']), + 'passes@subdomain.example.com', + ); + + $this->passes( + (new Email())->domainIs(['*example.com']), + 'passes@subdomain.example.com', + ); + + $this->passes( + (new Email())->domainIs(['example*']), + 'passes@example.com', + ); + + $this->fails( + (new Email())->domainIs(['test.com']), + 'fails@example.com', + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + } + + public function testDomainIsNot() + { + $this->passes( + (new Email())->domainIsNot(['test.com', 'example.org']), + 'passes@example.com', + ); + + $this->passes( + (new Email())->domainIsNot(['*test.com']), + 'passes@example.com', + ); + + $this->fails( + (new Email())->domainIsNot(['example.com']), + 'passes@example.com', + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + + $this->fails( + (new Email())->domainIsNot(['example*']), + 'passes@example.com', + ['The '.self::ATTRIBUTE_REPLACED.' must be a valid email address.'] + ); + } + protected function setUp(): void { $container = Container::getInstance(); From bd31426d1a2f0f67aeee21fb96cc89b293a64b43 Mon Sep 17 00:00:00 2001 From: Ash Allen Date: Mon, 6 Oct 2025 22:14:53 +0100 Subject: [PATCH 2/4] Lifted logic into separate method. --- src/Illuminate/Validation/Rules/Email.php | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Illuminate/Validation/Rules/Email.php b/src/Illuminate/Validation/Rules/Email.php index 53d8d93e60eb..89bc15e374af 100644 --- a/src/Illuminate/Validation/Rules/Email.php +++ b/src/Illuminate/Validation/Rules/Email.php @@ -2,6 +2,7 @@ namespace Illuminate\Validation\Rules; +use Closure; use Illuminate\Contracts\Validation\DataAwareRule; use Illuminate\Contracts\Validation\Rule; use Illuminate\Contracts\Validation\ValidatorAwareRule; @@ -271,24 +272,16 @@ protected function buildValidationRules() } if ($this->allowedDomains) { - $rules[] = function ($attribute, $value, $fail) { - $domain = explode('@', $value)[1]; - - $isAllowed = Str::of($domain)->is($this->allowedDomains, true); - - if (! $isAllowed) { + $rules[] = function (string $attribute, mixed $value, Closure $fail): void { + if (! $this->addressContainsDomain($value, $this->allowedDomains)) { $fail('The :attribute must be a valid email address.'); } }; } if ($this->disallowedDomains) { - $rules[] = function ($attribute, $value, $fail) { - $domain = explode('@', $value)[1]; - - $isDisallowed = Str::of($domain)->is($this->disallowedDomains, true); - - if ($isDisallowed) { + $rules[] = function (string $attribute, mixed $value, Closure $fail): void { + if ($this->addressContainsDomain($value, $this->disallowedDomains)) { $fail('The :attribute must be a valid email address.'); } }; @@ -332,4 +325,11 @@ public function setData($data) return $this; } + + private function addressContainsDomain($value, $domains) + { + $domain = explode('@', $value)[1]; + + return Str::of($domain)->is($domains, true); + } } From e2adc6d5830444562edf8a521fdee8ddf5bc85ad Mon Sep 17 00:00:00 2001 From: Ash Allen Date: Mon, 6 Oct 2025 22:19:38 +0100 Subject: [PATCH 3/4] Changed method name. --- src/Illuminate/Validation/Rules/Email.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Validation/Rules/Email.php b/src/Illuminate/Validation/Rules/Email.php index 89bc15e374af..668620476acb 100644 --- a/src/Illuminate/Validation/Rules/Email.php +++ b/src/Illuminate/Validation/Rules/Email.php @@ -273,7 +273,7 @@ protected function buildValidationRules() if ($this->allowedDomains) { $rules[] = function (string $attribute, mixed $value, Closure $fail): void { - if (! $this->addressContainsDomain($value, $this->allowedDomains)) { + if (! $this->domainMatchesPattern($value, $this->allowedDomains)) { $fail('The :attribute must be a valid email address.'); } }; @@ -281,7 +281,7 @@ protected function buildValidationRules() if ($this->disallowedDomains) { $rules[] = function (string $attribute, mixed $value, Closure $fail): void { - if ($this->addressContainsDomain($value, $this->disallowedDomains)) { + if ($this->domainMatchesPattern($value, $this->disallowedDomains)) { $fail('The :attribute must be a valid email address.'); } }; @@ -326,7 +326,14 @@ public function setData($data) return $this; } - private function addressContainsDomain($value, $domains) + /** + * Determine whether the email address matches one of the given domains. + * + * @param string $value + * @param string[] $domains + * @return bool + */ + protected function domainMatchesPattern($value, $domains) { $domain = explode('@', $value)[1]; From f49b3f994dcc69708f3f5d0b7815e924c2cc5354 Mon Sep 17 00:00:00 2001 From: Ash Allen Date: Mon, 6 Oct 2025 23:01:22 +0100 Subject: [PATCH 4/4] Added comments. --- src/Illuminate/Validation/Rules/Email.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Illuminate/Validation/Rules/Email.php b/src/Illuminate/Validation/Rules/Email.php index 668620476acb..08ae4326e8ee 100644 --- a/src/Illuminate/Validation/Rules/Email.php +++ b/src/Illuminate/Validation/Rules/Email.php @@ -167,6 +167,8 @@ public function withNativeValidation(bool $allowUnicode = false) } /** + * Ensure the domain of the email address matches one of the given patterns. + * * @param array $domains * @return $this */ @@ -178,6 +180,8 @@ public function domainIs($domains) } /** + * Ensure the domain of the email address does not match any of the given patterns. + * * @param array $domains * @return $this */