-
Notifications
You must be signed in to change notification settings - Fork 4.8k
[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
Changes from all commits
3883628
e7526ed
8cad670
6f7f34e
a1a541e
08e567c
e4bc18c
d85df2b
8c7a5f6
4da4cee
af2f2d5
10e3baf
42445e9
d18ecca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -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) | ||||||||||
|
@@ -1240,6 +1241,8 @@ The `filter` validator, which uses PHP's `filter_var` function, ships with Larav | |||||||||
> [!WARNING] | ||||||||||
> The `dns` and `spoof` validators require the PHP `intl` extension. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could link this:
Suggested change
|
||||||||||
|
||||||||||
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_,... | ||||||||||
|
||||||||||
|
@@ -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. | ||||||||||
SanderMuller marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
<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(), | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be |
||||||||||
|
||||||||||
return $this->app->isProduction() | ||||||||||
? $rule->validateMxRecord() | ||||||||||
: $rule; | ||||||||||
SanderMuller marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
}); | ||||||||||
} | ||||||||||
``` | ||||||||||
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 | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might alternatively be a table:
… There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: |
||||||||||
|
||||||||||
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` | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There's probably a reason, why this isn't the default anymore. |
||||||||||
| `admin@examрle.com` <br>(Cyrillic р) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||||||
|
||||||||||
|
There was a problem hiding this comment.
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: