EmailVerify LogoEmailVerify

Laravel Email Verification

Email checker for Laravel. PHP email verification in Laravel controllers and forms.

Добавьте верификацию email в ваше Laravel приложение, используя пользовательские правила валидации, middleware и сервис-провайдеры.

Установка

composer require emailverify/laravel

Или установите PHP SDK напрямую:

composer require emailverify/php-sdk

Настройка

Публикация конфигурации

php artisan vendor:publish --tag=emailverify-config

Переменные окружения

# .env
EMAILVERIFY_API_KEY=bv_live_xxx

Конфигурационный файл

// config/emailverify.php
return [
    'api_key' => env('EMAILVERIFY_API_KEY'),
    'timeout' => 10,
    'cache_duration' => 3600, // 1 час
    'block_disposable' => true,
    'block_role_based' => false,
];

Сервис-провайдер

Зарегистрируйте сервис-провайдер, если не используется автообнаружение:

// config/app.php
'providers' => [
    EmailVerify\Laravel\EmailVerifyServiceProvider::class,
],

'aliases' => [
    'EmailVerify' => EmailVerify\Laravel\Facades\EmailVerify::class,
],

Правило валидации

Использование встроенного правила

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' => 'Пожалуйста, укажите корректный email-адрес.',
        ];
    }
}

Пользовательское правило валидации

// 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('Поле :attribute должно содержать корректный email-адрес.');
                return;
            }

            if ($this->blockDisposable && $result->result->disposable) {
                $fail('Одноразовые email-адреса не допускаются.');
                return;
            }

            if ($this->blockRoleBased && $result->result->role) {
                $fail('Ролевые email-адреса не допускаются.');
                return;
            }

        } catch (\Exception $e) {
            // Логируем ошибку, но не блокируем отправку
            \Log::warning('Ошибка EmailVerify API', [
                'email' => $value,
                'error' => $e->getMessage(),
            ]);
        }
    }
}

Использование

use App\Rules\ValidEmail;

$request->validate([
    'email' => ['required', 'email', new ValidEmail(
        blockDisposable: true,
        blockRoleBased: false
    )],
]);

Использование фасада

use EmailVerify\Laravel\Facades\EmailVerify;

// Единичная верификация
$result = EmailVerify::verify('user@example.com');

if ($result->status === 'valid') {
    // Email валиден
}

// Проверка конкретных свойств
if (EmailVerify::isDisposable('user@example.com')) {
    // Блокировка одноразового email
}

if (EmailVerify::isValid('user@example.com')) {
    // Email доставляем
}

Middleware

Создайте middleware для верификации на уровне маршрутов.

Создание 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' => 'Невалидный email-адрес',
                ], 422);
            }

            // Добавление результата верификации к запросу
            $request->merge(['email_verification' => $result]);

        } catch (\Exception $e) {
            \Log::warning('Верификация email не удалась', [
                'email' => $email,
                'error' => $e->getMessage(),
            ]);
        }

        return $next($request);
    }
}

Регистрация Middleware

// bootstrap/app.php (Laravel 11+)
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'verify.email' => \App\Http\Middleware\VerifyEmailAddress::class,
    ]);
})

// Или в app/Http/Kernel.php (Laravel 10)
protected $middlewareAliases = [
    'verify.email' => \App\Http\Middleware\VerifyEmailAddress::class,
];

Использование в маршрутах

Route::post('/register', [AuthController::class, 'register'])
    ->middleware('verify.email');

Route::post('/subscribe', [NewsletterController::class, 'subscribe'])
    ->middleware('verify.email:subscriber_email');

Интеграция с контроллером

// 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',
        ]);

        // Верификация email
        $verification = $this->emailVerify->verify($validated['email']);

        if ($verification->status === 'invalid') {
            return back()->withErrors([
                'email' => 'Пожалуйста, укажите корректный email-адрес.',
            ]);
        }

        if ($verification->result->disposable) {
            return back()->withErrors([
                'email' => 'Одноразовые email-адреса не допускаются.',
            ]);
        }

        // Создание пользователя
        $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');
    }
}

Кэширование

Кэшируйте результаты верификации для сокращения API-вызовов.

// 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));
    }
}

Регистрация сервис-провайдера

// app/Providers/AppServiceProvider.php
public function register(): void
{
    $this->app->singleton(EmailVerificationService::class);
}

Использование

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' => 'Невалидный email']);
        }

        // Продолжение обработки
    }
}

Пакетная верификация

Эффективная верификация нескольких email.

// 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 = 'Верификация всех email-адресов пользователей';

    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("Верификация {$users->count()} email...");

        // Отправка пакетного задания
        $emails = $users->pluck('email')->toArray();
        $job = $client->verifyBulk($emails);

        $this->info("ID задания: {$job->job_id}");

        // Ожидание завершения
        $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();

        // Получение результатов и обновление пользователей
        $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('Верификация завершена!');
        $this->table(
            ['Статус', 'Количество'],
            collect($results->results)
                ->groupBy('status')
                ->map(fn($items, $status) => [$status, $items->count()])
                ->values()
        );
    }
}

Event Listeners

Реагирование на результаты верификации с помощью событий.

// 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('Обнаружен невалидный email', [
                'email' => $event->email,
            ]);
        }

        if ($event->result['disposable'] ?? false) {
            Log::info('Заблокирован одноразовый email', [
                'email' => $event->email,
            ]);
        }
    }
}

Тестирование

Мокирование сервиса

// 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');
    }
}

Связанные ресурсы

On this page