Skip to content

[11.x] Document the fluent email Rule validation #10112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 14 commits into from
210 changes: 210 additions & 0 deletions validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
- [Error Message Indexes and Positions](#error-message-indexes-and-positions)
- [Validating Files](#validating-files)
- [Validating Passwords](#validating-passwords)
- [Validating Emails](#validating-emails)
- [Custom Validation Rules](#custom-validation-rules)
- [Using Rule Objects](#using-rule-objects)
- [Using Closures](#using-closures)
Expand Down Expand Up @@ -1240,6 +1241,8 @@ The `filter` validator, which uses PHP's `filter_var` function, ships with Larav
> [!WARNING]
Copy link
Contributor

Choose a reason for hiding this comment

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

You might want to link this:

- The `filter` validator, which uses PHP's `filter_var` function, ships with Laravel and was Laravel's default email validation behavior prior to Laravel version 5.8.
+ The `filter` validator, which uses PHP's [`filter_var` function](https://www.php.net/manual/en/filter.constants.php#constant.filter-validate-email), ships with Laravel and was Laravel's default email validation behavior prior to Laravel version 5.8.
 
[!WARNING]

> The `dns` and `spoof` validators require the PHP `intl` extension.
Copy link
Contributor

Choose a reason for hiding this comment

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

You could link this:

Suggested change
> The `dns` and `spoof` validators require the PHP `intl` extension.
> The `dns` and `spoof` validators require the [PHP `intl` extension](https://www.php.net/manual/de/book.intl.php).


Since email validation can be quite complex, you may use [Rule::email()](#validating-emails) to fluently construct the rule.

<a name="rule-ends-with"></a>
#### ends_with:_foo_,_bar_,...

Expand Down Expand Up @@ -2241,6 +2244,213 @@ Occasionally, you may want to attach additional validation rules to your default
// ...
});

<a name="validating-emails"></a>
## Validating Emails

Laravel provides a variety of validation rules that may be used to validate uploaded files, such as `rfc`, `strict` and `dns`. While you are free to specify these rules individually when validating emails, Laravel also offers a fluent email validation rule builder that you may find convenient:
To ensure that emails are valid according to your application's requirements, you may use `Rule` class to fluently define the rule via ` Rule::email()`:

```php
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

$validator = Validator::make($request->all(), [
'email' => ['required', Rule::email()],
]);
```
The `Email` rule object allows you to easily customize how emails are validated for your application, such as specifying that emails require RFC compliance, DNS checks, or spoof detection:

```php
// Basic RFC compliance...
Rule::email()->rfcCompliant();

// Strict RFC compliance...
Rule::email()->rfcCompliant(strict: true);

// Check for valid MX records...
Rule::email()->validateMxRecord();

// Prevent spoofing...
Rule::email()->preventSpoofing();
```
Of course, you may chain all the methods in the examples above:

```php
Rule::email()
->rfcCompliant(strict: true)
->validateMxRecord()
->preventSpoofing();
```

> [!WARNING]
> The `validateMxRecord()` and `preventSpoofing()` validators require the PHP `intl` extension.

<a name="defining-default-email-rules"></a>
#### Defining Default Email Rules
You may find it convenient to specify the default validation rules for emails in a single location of your application. You can easily accomplish this using the Email::defaults method, which accepts a closure. The closure given to the defaults method should return the default configuration of the `Email` rule. Typically, the `defaults` rule should be called within the boot method of one of your application's service providers:

```php
use Illuminate\Validation\Rules\Email;

/**
* Bootstrap any application services.
*/
public function boot(): void
{
Email::defaults(function () {
$rule = (new Email())->rfcCompliant(strict: true)->preventSpoofing(),

Choose a reason for hiding this comment

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

Should be ; instead of , at the end


return $this->app->isProduction()
? $rule->validateMxRecord()
: $rule;
});
}
```
Then, when you would like to apply the default rules to a particular email undergoing validation, you may invoke the defaults method with no arguments:

```php
'email' => ['required', Email::defaults()],
```
Occasionally, you may want to attach additional validation rules to your default email validation rules. You may use the rules method to accomplish this:

```php
Email::defaults(function () {
return (new Email())->rfcCompliant(strict: true)
->preventSpoofing()
->rules(['ends_with:@example.com']);
});
```

<a name="email-validation-use-cases-examples"></a>
### Use Cases & Examples
Copy link
Contributor

Choose a reason for hiding this comment

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

This might alternatively be a table:

Method Description Use case example
rfcCompliant(strict: bool) Validates the email according to RFC 5322.
Passing true enforces stricter RFC checks (e.g., rejects [email protected]).
Use rfcCompliant() if you need standard RFC validation while allowing some unusual formats.
Use rfcCompliant(true) if you want to reject less typical but technically valid addresses.
Basic RFC validation
Rule::email()->rfcCompliant();
Strict RFC validation
Rule::email()->rfcCompliant(true);

Copy link
Contributor

Choose a reason for hiding this comment

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

You might want to add a link up there, where the string rule is explained:
#10112 (comment)


Each method on the `Email` rule addresses a different aspect of email validation.

<a name="rfc-compliant-email-validation"></a>
#### `rfcCompliant(strict: bool)`

**What it does**
- Validates the email according to [RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322).
- Passing `true` enforces stricter RFC checks (e.g., rejects trailing dots or multiple consecutive dots).

**When to use it**
- Use `rfcCompliant()` for typical RFC validation while allowing some uncommon formats.
- Use `rfcCompliant(strict: true)` to reject unusual but technically valid addresses.

**Email addresses that pass on `rfcCompliant()` but fail on `rfcCompliant(strict: true)`**
- `test@example` (No TLD)
- `"has space"@example.com` (quoted local part with space)
- `name@[127.0.0.1]` (Local-part with domain-literal IPv4 address)
- `some(comment)@example.com` (Comment in local part)

**Code sample**
```php
// Basic RFC validation
Rule::email()->rfcCompliant();

// Strict RFC validation
Rule::email()->rfcCompliant(strict: true);
```

<a name="validate-mx-record-email-validation"></a>
#### `validateMxRecord()`

**What it does**
- Ensures the domain has a valid MX record so it can receive mail.

**When to use it**
- Especially useful for sign-ups, password resets, or anywhere deliverability matters.

> [!NOTE]
> This rule triggers DNS lookups, which adds latency to the request and can fail if the domain’s DNS is temporarily unreachable.

**Code sample**
```php
Rule::email()->validateMxRecord();
```

<a name="prevent-spoofing-email-validation"></a>
#### `preventSpoofing()`

**What it does**
- Detects homograph or deceptive Unicode usage (e.g., Cyrillic “р” vs. Latin “r”) to prevent phishing or impersonation.

**When to use it**
- Ideal in security-sensitive contexts or where email spoofing is a concern.

**Code sample**
```php
Rule::email()->preventSpoofing();
```

> [!NOTE]
> The `validateMxRecord()` and `preventSpoofing()` methods require the PHP `intl` extension.

<a name="with-native-email-validation"></a>
#### `withNativeValidation()`

**What it does**
- Uses PHP’s `FILTER_VALIDATE_EMAIL` for validation.
- When `allowUnicode` is `true`, some Unicode characters are allowed.

**When to use it**
- If you want an additional layer of PHP’s built-in validation.
- Typically for ASCII addresses unless you explicitly allow Unicode.

**Email addresses that pass on `withNativeValidation()` but fail on `rfcCompliant(strict: true)`**
- `abc."test"@example.com`
- `name@[127.0.0.1]`
- `"ab\\(c"@example.com`

> [!NOTE]
> `withNativeValidation()` is **less strict** than RFC validation and was previously the default in Laravel. If you need strict RFC compliance, use `rfcCompliant(strict: true)` instead.

**Code sample**
```php
// Use native PHP validation (ASCII-only)
Rule::email()->withNativeValidation();

// Allow some Unicode
Rule::email()->withNativeValidation(allowUnicode: true);
```

<a name="email-validation-rule-matrix"></a>
### Email Validation Rule Matrix

Below is a single table showcasing different email address formats, highlighting whether each ✅ **passes** or ❌ **fails** the validation methods:

| **Email Address** | **rfcCompliant()** | **rfcCompliant(strict: true)** | **withNativeValidation()** | **withNativeValidation(allowUnicode: true)** | **validateMxRecord()** | **preventSpoofing()** |
|--------------------------------------|--------------------|--------------------------------|----------------------------|----------------------------------------------|------------------------|-----------------------|
| `"has space"@example.com` | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `abc."test"@example.com` | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| `"ab\\(c"@example.com` | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| `name@[127.0.0.1]` | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| `test@example` <br>(no TLD) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| `test@localhost` <br>(no TLD) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| `[email protected]` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `té[email protected]` | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
| `user@üñîçødé.com` | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ |

Choose a reason for hiding this comment

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

Similarly, user@üñîçødé.com is marked as failing withNativeValidation(allowUnicode: true), which seems counterintuitive—shouldn't it pass if Unicode is explicitly allowed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Similarly, user@üñîçødé.com is marked as failing withNativeValidation(allowUnicode: true), which seems counterintuitive—shouldn't it pass if Unicode is explicitly allowed?

I would've expected it to, but it doesn't. These docs don't determine the behavior but describe it. The table shows why IMO rfcCompliant(strict: true) is a much better choice than withNativeValidation(allowUnicode: true) and I think withNativeValidation all together shouldn't be used, but thats an opinion

Copy link
Contributor

Choose a reason for hiding this comment

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

I think withNativeValidation all together shouldn't be used, but thats an opinion

There's probably a reason, why this isn't the default anymore.

| `admin@examрle.com` <br>(Cyrillic р) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |

Choose a reason for hiding this comment

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

I think it would be helpful to clarify why admin@examрle.com (with Cyrillic р) passes rfcCompliant(strict: true) but fails preventSpoofing(). Since it’s a potentially deceptive address

Copy link
Contributor Author

@SanderMuller SanderMuller Jan 17, 2025

Choose a reason for hiding this comment

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

Because it's a valid email address, just deceptive so potentially unwanted. So the RFC has nothing to do with how. I am not sure how to add these kind of details without making the docs for email validation a book on itself 😄

| `admin@exam<U+0440>le.com` | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| `test@@example.com` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |

<a name="combining-email-validation-methods"></a>
### Combining Methods

You can **chain** multiple methods to cover various scenarios:

```php
$validator = Validator::make($request->all(), [
'email' => [
'required',
Rule::email()
->rfcCompliant(strict: true) // Strict RFC validation
->validateMxRecord() // Requires valid MX record
->preventSpoofing() // Detects homograph attacks
],
]);
```

<a name="custom-validation-rules"></a>
## Custom Validation Rules

Expand Down