misaf / laravel-email-validation
Provider-neutral email domain and deliverability validation for Laravel applications
Requires
- php: ^8.4
- illuminate/contracts: ^13.0
- illuminate/support: ^13.0
- spatie/laravel-package-tools: ^1.93
- symfony/dependency-injection: ^8.1
- symfony/http-kernel: ^8.1
Requires (Dev)
- larastan/larastan: ^3.10
- laravel/boost: ^2.5
- laravel/pint: ^1.3
- mockery/mockery: ^1.6
- monorepo-php/monorepo: ^12.7
- nunomaduro/collision: ^8.9
- orchestra/testbench: ^11.1
- pestphp/pest: ^5.1
- pestphp/pest-plugin-arch: ^5.0
- pestphp/pest-plugin-laravel: ^5.0
- pestphp/pest-plugin-profanity: ^5.0
- pestphp/pest-plugin-type-coverage: ^5.0
- phpstan/extension-installer: ^1.4
Suggests
- misaf/laravel-email-validation-bouncer: Adds the "bouncer" deliverability driver, backed by the Bouncer API (https://usebouncer.com)
- misaf/laravel-email-validation-emailable: Adds the "emailable" deliverability driver, backed by the Emailable API (https://emailable.com)
README
A standalone, reusable email validation rule for Laravel applications.
Features
- A
ValidationRulethat enforces an optional domain allow-list plus pluggable deliverability verification - Driver-based deliverability via a Laravel
Manager - Configurable allowed domains (empty by default — no restriction)
- Localized failure messages (en, de, fa)
- Explicit deliverable, risky, undeliverable, and unverifiable outcomes
The core package is provider-neutral: it ships only the null driver, which
performs no external check. Real deliverability verification comes from driver
packages that register themselves via the manager's extend. Install the
driver(s) you want — one, both, or none.
The core depends only on framework packages, so it can be reused by any Laravel application without pulling in a wider ecosystem.
Requirements
- PHP 8.4+
- Laravel 13
Installation
The core package is required in every case:
composer require misaf/laravel-email-validation
On its own this gives you the domain allow-list plus the null driver, which
treats every address as deliverable — useful for local and testing
environments, but it performs no real verification.
First-party drivers
To actually verify deliverability, add one or both driver packages. They are independent of each other and can be installed together:
composer require misaf/laravel-email-validation-emailable composer require misaf/laravel-email-validation-bouncer
| Package | Driver name | Provider | Config file |
|---|---|---|---|
| (core) | null |
none — always Deliverable |
laravel-email-validation.php |
misaf/laravel-email-validation-emailable |
emailable |
Emailable | laravel-email-validation-emailable.php |
misaf/laravel-email-validation-bouncer |
bouncer |
Bouncer | laravel-email-validation-bouncer.php |
Each driver package requires the core and is listed under the core's composer
suggest, so composer require misaf/laravel-email-validation will prompt you
with what is available.
All service providers are auto-registered.
Publish the config to customise the allowed domains and deliverability wiring:
php artisan vendor:publish --tag=laravel-email-validation-config
Each driver package publishes its own config under a matching tag, e.g.:
php artisan vendor:publish --tag=laravel-email-validation-emailable-config php artisan vendor:publish --tag=laravel-email-validation-bouncer-config
Using both drivers together
Both drivers register under distinct names on the same manager, so they
coexist. default picks the one used when no driver is named, and any rule or
facade call can override it per use:
EMAIL_VERIFIER_DRIVER=emailable EMAILABLE_HOST=https://api.emailable.com/v1/verify EMAILABLE_API_KEY=... BOUNCER_HOST=https://api.usebouncer.com/v1.1/email/verify BOUNCER_API_KEY=...
new EmailValidation(); // the configured default — "emailable" above new EmailValidation('bouncer'); // this rule only, regardless of the default
Configuration
config/laravel-email-validation.php:
allowed_domains— array of accepted domains. Leave empty to allow any domain. Defaults to the comma-separatedEMAIL_ALLOWED_DOMAINSenv value.default— the deliverability driver name (EMAIL_VERIFIER_DRIVER). The core package provides onlynull; installing a driver package makes its driver name (emailable,bouncer) available here.
Entries in allowed_domains are trimmed and lower-cased before comparison, so EMAIL_ALLOWED_DOMAINS=example.com, Example.org works as written.
Usage
use Misaf\LaravelEmailValidation\Rules\EmailValidation; TextInput::make('email') ->email() ->rules([ 'bail', 'email:rfc,strict,spoof,filter,filter_unicode', new EmailValidation(), ]);
new EmailValidation() uses the configured default driver. Pass a driver name to override per use: new EmailValidation('bouncer').
Verification Outcomes
| Status | Meaning | Validation result |
|---|---|---|
Deliverable |
The provider positively classified the address as deliverable | Pass |
Risky |
The address may accept mail but has deliverability or quality concerns | Fail |
Undeliverable |
The provider positively classified the address as invalid | Fail |
Unverifiable |
Verification failed or produced no reliable result | Fail |
The null driver performs no external verification and always returns
Deliverable. Unknown provider states and provider failures must never be
reported as deliverable by concrete drivers.
Verifying an address directly
use Misaf\LaravelEmailValidation\Facades\EmailVerifier; use Misaf\LaravelEmailValidation\Enums\EmailVerificationStatus; $status = EmailVerifier::verify('user@example.com'); // default driver $status = EmailVerifier::driver('bouncer')->verify($email); // specific driver if ($status === EmailVerificationStatus::Deliverable) { // ... }
Registering a custom driver
use Misaf\LaravelEmailValidation\Contracts\EmailVerifier as EmailVerifierContract; use Misaf\LaravelEmailValidation\EmailVerifierManager; app(EmailVerifierManager::class)->extend('my-provider', fn (): EmailVerifierContract => new MyProviderVerifier());
Testing
composer test
composer analyse
License
MIT. See LICENSE.