Laravel
Email checker for Laravel. PHP email verification in Laravel controllers and forms.
使用自定义验证规则、中间件和服务提供者将电子邮件验证添加到您的 Laravel 应用中。
安装
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' => '请提供有效的电子邮件地址。',
];
}
}自定义验证规则
// 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 必须是有效的电子邮件地址。');
return;
}
if ($this->blockDisposable && $result->result->disposable) {
$fail('不允许使用一次性电子邮件地址。');
return;
}
if ($this->blockRoleBased && $result->result->role) {
$fail('不允许使用基于角色的电子邮件地址。');
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') {
// 电子邮件有效
}
// 检查特定属性
if (EmailVerify::isDisposable('user@example.com')) {
// 阻止一次性电子邮件
}
if (EmailVerify::isValid('user@example.com')) {
// 电子邮件可递送
}中间件
为路由级别验证创建中间件。
创建中间件
// 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' => '无效的电子邮件地址',
], 422);
}
// 将验证结果添加到请求
$request->merge(['email_verification' => $result]);
} catch (\Exception $e) {
\Log::warning('电子邮件验证失败', [
'email' => $email,
'error' => $e->getMessage(),
]);
}
return $next($request);
}
}注册中间件
// 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',
]);
// 验证电子邮件
$verification = $this->emailVerify->verify($validated['email']);
if ($verification->status === 'invalid') {
return back()->withErrors([
'email' => '请提供有效的电子邮件地址。',
]);
}
if ($verification->result->disposable) {
return back()->withErrors([
'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' => '无效的电子邮件']);
}
// 继续处理
}
}批量验证
高效地验证多个电子邮件。
// 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 = '验证所有用户电子邮件地址';
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()} 个电子邮件...");
// 提交批量任务
$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()
);
}
}事件监听器
使用事件响应验证结果。
// 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' => $event->email,
]);
}
if ($event->result['disposable'] ?? false) {
Log::info('阻止的一次性电子邮件', [
'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');
}
}