Skip to content

Conversation

@SanderMuller
Copy link
Contributor

This PR aims to provide a similar experience as the Password validation rule and File validation rule (#43271) for emails. It does so by providing a fluent, extendable Email rule object.

Basic usage

// Before $request->validate([ 'email' => ['required', 'string', 'email', 'strict', 'dns'], ]); // After $request->validate([ 'email' => ['required', 'string', Rule::email()->strict()->dns()], ]);

Available methods

The following methods are available on the rule:

  • ::default: equivalent to the Password::default() rule
  • ::strictSecurity: equivalent to Rule::email()->strict()->dns()->spoof()
  • strict: equivalent to the strict rule
  • dns: equivalent to the dns rule
  • spoof: equivalent to the spoof rule
  • filter: equivalent to the filter rule
  • filterUnicode: equivalent to the filter_unicode rule
  • rfc: equivalent to the rfc rule

Extending for custom types

Whilst the File rule only ships with the ::image method as a custom type, the rule is Macroable, allowing each application to specify more granular file permissions as required.

// AppServiceProvider public function boot() { Email::macro('employee', function () { return Email::default()->rules('ends_with:@laravel.com'); }); } // Usage $request->validate([ 'email' => ['required', Email::employee()->spoof()] ]);

Open for discussion

I've added strictSecurity method as an option to easily create a solid email validation check, since the differences between the available rules are rather complex. Not including this method would be a valid choice as well. However, I hope this method will help Laravel developers better secure the email inputs, for example example against spoofing. The composition of this methods (strict + DNS + spoof) is a result of running applications in production and handling all kinds of edge cases with invalid emails that passed validation before using this combination.

Thanks to @lukeraymonddowning for providing #43271. I've used his PR as inspiration for the body text of this PR.

@github-actions
Copy link

github-actions bot commented Jan 3, 2025

Thanks for submitting a PR!

Note that draft PR's are not reviewed. If you would like a review, please mark your pull request as ready for review in the GitHub user interface.

Pull requests that are abandoned in draft may be closed due to inactivity.

*
* @param static|callable|null $callback
* @return static|null
* @return static|void
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

never returns null, but does return void

use Illuminate\Validation\Validator;
use PHPUnit\Framework\TestCase;

class ValidationEmailRuleTest extends TestCase
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test class inspired by ValidationFileRuleTest

use Stringable;
use function Illuminate\Support\enum_value;

class Email implements Rule, DataAwareRule, ValidatorAwareRule
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heavily inspired by src/Illuminate/Validation/Rules/File.php

@SanderMuller SanderMuller marked this pull request as ready for review January 3, 2025 11:58
@taylorotwell
Copy link
Member

While I like terse methods, I wonder if we should give these methods a bit more descriptive names? It's not super clear was spoof or dns means when chaining them together.

@taylorotwell taylorotwell marked this pull request as draft January 3, 2025 15:52
@SanderMuller
Copy link
Contributor Author

SanderMuller commented Jan 3, 2025

While I like terse methods, I wonder if we should give these methods a bit more descriptive names? It's not super clear was spoof or dns means when chaining them together.

@taylorotwell Thank you for reviewing the pull request and providing your feedback. I’ve updated the method names to make them more descriptive and aligned with Laravel’s validation conventions.

New names:

rfcCompliant(): Ensures compliance with RFC standards.
rfcCompliant(true): Adheres strictly to RFC without warnings (formerly strict).
nativeFilter(): Validates email format using FILTER_VALIDATE_EMAIL.
nativeFilter(true): Extends basic format validation to include Unicode support.
verifyMxRecord(): Verifies that the domain has a valid MX record.
preventEmailSpoofing(): Detects and prevents email spoofing.

I attempted to pick names aiming to balance clarity and brevity, ensuring developers can intuitively understand the intent of each rule when chaining them together. For example:

Rule::email() ->rfcCompliant(true) ->domainExists() ->preventEmailSpoofing();

Perhaps they are not perfect yet, but it might inspire you to make the final naming tweaks!

@SanderMuller SanderMuller marked this pull request as ready for review January 3, 2025 17:31
@SanderMuller
Copy link
Contributor Author

@taylorotwell If we’re moving away from string-based names, we could also consider @shaedrich’s suggestion of merging the rfcCompliant and rfcCompliantWithoutWarnings rules into a single rule, such as rfcCompliant(bool $strict). Similarly, combining basicFormat and basicFormatWithUnicode into basicFormat(bool $withUnicode) would simplify things, since these are essentially variants of each other.

This approach would reduce the number of methods from 6 to 4, making the API easier to use and more intuitive while maintaining clarity.

Let me know your thoughts!

@shaedrich
Copy link
Contributor

rfcCompliant: Ensures compliance with RFC standards.
rfcCompliantWithoutWarnings: Adheres strictly to RFC without warnings (formerly strict).

I would actually flip that negation around:
rfcCompliantWarningsAllowed: Ensures compliance with RFC standards.
strictRfcCompliance: Adheres strictly to RFC without warnings (formerly strict).

@taylorotwell taylorotwell merged commit 832ebc0 into laravel:11.x Jan 9, 2025
38 checks passed
@SanderMuller SanderMuller deleted the Add-Rule-email() branch January 10, 2025 14:02
@taylorotwell
Copy link
Member

@SanderMuller would you mind sending over some documentation for this?

@SanderMuller
Copy link
Contributor Author

@SanderMuller would you mind sending over some documentation for this?

Will work on it 👍

@SanderMuller
Copy link
Contributor Author

@SanderMuller would you mind sending over some documentation for this?

@taylorotwell done via laravel/docs#10112, let me know what you think

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants