Laravel
Email checker for Laravel. PHP email verification in Laravel controllers and forms.
Add email verification to your Laravel application using custom validation rules, middleware, and service providers.
Installation
composer require emailverify/laravelOr install the PHP SDK directly:
composer require emailverify/php-sdkConfiguration
Publish Configuration
php artisan vendor:publish --tag=emailverify-configEnvironment Variables
# .env
EMAILVERIFY_API_KEY=bv_live_xxxConfig File
// config/emailverify.php
return [
'api_key' => env('EMAILVERIFY_API_KEY'),
'timeout' => 10,
'cache_duration' => 3600, // 1 hour
'block_disposable' => true,
'block_role_based' => false,
];Service Provider
Register the service provider if not using auto-discovery:
// config/app.php
'providers' => [
EmailVerify\Laravel\EmailVerifyServiceProvider::class,
],
'aliases' => [
'EmailVerify' => EmailVerify\Laravel\Facades\EmailVerify::class,
],Validation Rule
Using the Built-in Rule
use App\Http\Requests\RegisterRequest;
class RegisterRequest extends FormRequest
{
public function rules(): array
{
return [
'email' => ['required', 'email', 'emailverify'],
'name' => ['required', 'string', 'max:255'],
'password' => ['required', 'confirmed', 'min:8'],
];
}
public function messages(): array
{
return [
'email.emailverify' => 'Please provide a valid email address.',
];
}
}Custom Validation Rule
// app/Rules/ValidEmail.php
<?php
namespace App\Rules;
use EmailVerify\Client;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class ValidEmail implements ValidationRule
{
protected bool $blockDisposable;
protected bool $blockRoleBased;
public function __construct(
bool $blockDisposable = true,
bool $blockRoleBased = false
) {
$this->blockDisposable = $blockDisposable;
$this->blockRoleBased = $blockRoleBased;
}
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$client = new Client(config('emailverify.api_key'));
try {
$result = $client->verify($value);
if ($result->status === 'invalid') {
$fail('The :attribute must be a valid email address.');
return;
}
if ($this->blockDisposable && $result->result->disposable) {
$fail('Disposable email addresses are not allowed.');
return;
}
if ($this->blockRoleBased && $result->result->role) {
$fail('Role-based email addresses are not allowed.');
return;
}
} catch (\Exception $e) {
// Log error but don't block submission
\Log::warning('EmailVerify API error', [
'email' => $value,
'error' => $e->getMessage(),
]);
}
}
}Usage
use App\Rules\ValidEmail;
$request->validate([
'email' => ['required', 'email', new ValidEmail(
blockDisposable: true,
blockRoleBased: false
)],
]);Facade Usage
use EmailVerify\Laravel\Facades\EmailVerify;
// Single verification
$result = EmailVerify::verify('user@example.com');
if ($result->status === 'valid') {
// Email is valid
}
// Check specific properties
if (EmailVerify::isDisposable('user@example.com')) {
// Block disposable email
}
if (EmailVerify::isValid('user@example.com')) {
// Email is deliverable
}Middleware
Create middleware for route-level verification.
Create Middleware
// app/Http/Middleware/VerifyEmailAddress.php
<?php
namespace App\Http\Middleware;
use EmailVerify\Client;
use Closure;
use Illuminate\Http\Request;
class VerifyEmailAddress
{
protected Client $client;
public function __construct()
{
$this->client = new Client(config('emailverify.api_key'));
}
public function handle(Request $request, Closure $next, string $field = 'email')
{
$email = $request->input($field);
if (!$email) {
return $next($request);
}
try {
$result = $this->client->verify($email);
if ($result->status === 'invalid') {
return response()->json([
'error' => 'Invalid email address',
], 422);
}
// Add verification result to request
$request->merge(['email_verification' => $result]);
} catch (\Exception $e) {
\Log::warning('Email verification failed', [
'email' => $email,
'error' => $e->getMessage(),
]);
}
return $next($request);
}
}Register Middleware
// bootstrap/app.php (Laravel 11+)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'verify.email' => \App\Http\Middleware\VerifyEmailAddress::class,
]);
})
// Or in app/Http/Kernel.php (Laravel 10)
protected $middlewareAliases = [
'verify.email' => \App\Http\Middleware\VerifyEmailAddress::class,
];Use in Routes
Route::post('/register', [AuthController::class, 'register'])
->middleware('verify.email');
Route::post('/subscribe', [NewsletterController::class, 'subscribe'])
->middleware('verify.email:subscriber_email');Controller Integration
// app/Http/Controllers/AuthController.php
<?php
namespace App\Http\Controllers;
use App\Models\User;
use EmailVerify\Client;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class AuthController extends Controller
{
protected Client $emailVerify;
public function __construct()
{
$this->emailVerify = new Client(config('emailverify.api_key'));
}
public function register(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|confirmed|min:8',
]);
// Verify email
$verification = $this->emailVerify->verify($validated['email']);
if ($verification->status === 'invalid') {
return back()->withErrors([
'email' => 'Please provide a valid email address.',
]);
}
if ($verification->result->disposable) {
return back()->withErrors([
'email' => 'Disposable emails are not allowed.',
]);
}
// Create user
$user = User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => Hash::make($validated['password']),
'email_verified_score' => $verification->score,
]);
auth()->login($user);
return redirect('/dashboard');
}
}Caching
Cache verification results to reduce API calls.
// app/Services/EmailVerificationService.php
<?php
namespace App\Services;
use EmailVerify\Client;
use Illuminate\Support\Facades\Cache;
class EmailVerificationService
{
protected Client $client;
protected int $cacheDuration;
public function __construct()
{
$this->client = new Client(config('emailverify.api_key'));
$this->cacheDuration = config('emailverify.cache_duration', 3600);
}
public function verify(string $email): object
{
$cacheKey = 'email_verification:' . md5($email);
return Cache::remember($cacheKey, $this->cacheDuration, function () use ($email) {
return $this->client->verify($email);
});
}
public function isValid(string $email): bool
{
return $this->verify($email)->status === 'valid';
}
public function isDisposable(string $email): bool
{
return $this->verify($email)->result->disposable ?? false;
}
public function clearCache(string $email): void
{
Cache::forget('email_verification:' . md5($email));
}
}Service Provider Registration
// app/Providers/AppServiceProvider.php
public function register(): void
{
$this->app->singleton(EmailVerificationService::class);
}Usage
use App\Services\EmailVerificationService;
class SomeController extends Controller
{
public function __construct(
protected EmailVerificationService $emailVerification
) {}
public function store(Request $request)
{
if (!$this->emailVerification->isValid($request->email)) {
return back()->withErrors(['email' => 'Invalid email']);
}
// Continue processing
}
}Bulk Verification
Verify multiple emails efficiently.
// app/Console/Commands/VerifyUserEmails.php
<?php
namespace App\Console\Commands;
use App\Models\User;
use EmailVerify\Client;
use Illuminate\Console\Command;
class VerifyUserEmails extends Command
{
protected $signature = 'users:verify-emails {--chunk=100}';
protected $description = 'Verify all user email addresses';
public function handle()
{
$client = new Client(config('emailverify.api_key'));
$chunk = (int) $this->option('chunk');
$users = User::whereNull('email_verified_at')
->orWhere('email_verification_date', '<', now()->subMonths(3))
->get();
$this->info("Verifying {$users->count()} emails...");
// Submit bulk job
$emails = $users->pluck('email')->toArray();
$job = $client->verifyBulk($emails);
$this->info("Job ID: {$job->job_id}");
// Poll for completion
$bar = $this->output->createProgressBar(100);
do {
sleep(5);
$status = $client->getBulkJobStatus($job->job_id);
$bar->setProgress($status->progress_percent);
} while ($status->status !== 'completed');
$bar->finish();
$this->newLine();
// Get results and update users
$results = $client->getBulkJobResults($job->job_id);
foreach ($results->results as $result) {
User::where('email', $result->email)->update([
'email_verification_status' => $result->status,
'email_verification_score' => $result->score,
'email_verification_date' => now(),
]);
}
$this->info('Verification complete!');
$this->table(
['Status', 'Count'],
collect($results->results)
->groupBy('status')
->map(fn($items, $status) => [$status, $items->count()])
->values()
);
}
}Event Listeners
React to verification results with events.
// app/Events/EmailVerified.php
<?php
namespace App\Events;
use Illuminate\Foundation\Events\Dispatchable;
class EmailVerified
{
use Dispatchable;
public function __construct(
public string $email,
public string $status,
public float $score,
public array $result
) {}
}
// app/Listeners/HandleEmailVerification.php
<?php
namespace App\Listeners;
use App\Events\EmailVerified;
use Illuminate\Support\Facades\Log;
class HandleEmailVerification
{
public function handle(EmailVerified $event): void
{
if ($event->status === 'invalid') {
Log::warning('Invalid email detected', [
'email' => $event->email,
]);
}
if ($event->result['disposable'] ?? false) {
Log::info('Disposable email blocked', [
'email' => $event->email,
]);
}
}
}Testing
Mock the Service
// tests/Feature/RegistrationTest.php
<?php
namespace Tests\Feature;
use EmailVerify\Client;
use Mockery;
use Tests\TestCase;
class RegistrationTest extends TestCase
{
public function test_registration_with_valid_email(): void
{
$mock = Mockery::mock(Client::class);
$mock->shouldReceive('verify')
->once()
->andReturn((object) [
'status' => 'valid',
'score' => 0.95,
'result' => (object) [
'disposable' => false,
'role' => false,
],
]);
$this->app->instance(Client::class, $mock);
$response = $this->post('/register', [
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => 'password123',
'password_confirmation' => 'password123',
]);
$response->assertRedirect('/dashboard');
}
public function test_registration_blocks_disposable_email(): void
{
$mock = Mockery::mock(Client::class);
$mock->shouldReceive('verify')
->once()
->andReturn((object) [
'status' => 'valid',
'score' => 0.5,
'result' => (object) [
'disposable' => true,
'role' => false,
],
]);
$this->app->instance(Client::class, $mock);
$response = $this->post('/register', [
'name' => 'John Doe',
'email' => 'temp@mailinator.com',
'password' => 'password123',
'password_confirmation' => 'password123',
]);
$response->assertSessionHasErrors('email');
}
}