1
/
5

【Laravel11】RFC違反メールアドレス使用時の例外スローを抑制する

経緯

数年前にLaravel5で構築したシステムを、Laravel11にアップデートすることになりました。

既存の仕様はそのままにフレームワークと関連ライブラリのみアップデートが必要になったのですが、 仕様の一つに「RFC違反のメールアドレス宛に送信時、エラーが発生しないこと」がありました。

「Laravel RFC違反 メール送信」といったキーワードでGoogle検索をすると実装例がいくつかヒットしますが、Laravel9以降はSymfony Mailerが採用されたことで、Laravel11ではそれらは使用できなくなっています。

Gmail等、一般的なメールサーバではRFC違反のメールアドレスへの送受信が禁止されていたり、キャリア側でメールアドレス変更を推奨しているケースもあり、使用者が減ってきている印象はありますが、もし対応が必要となった場合にどのような修正が必要か調査しました。

メールアドレスの対応以外にも、既存仕様と最新版フレームワークとの調整が必要になるケースがあるかもしれませんので、対応方法のひとつとして本記事で残したいと思います。

環境

下記構成にてLaravel11が動く環境を作成済みであることが前提です。

  • Ubuntu22.04.2 LTS(Windows11のWSL2上に構築)
  • PHP 8.3.4
  • Laravel Framework 11.0.8
  • Mailpit、MailCatcher
  • 適当なMailableを作成済み(こちらの手順と同様にOrderShippedを作成しました)

RFC違反のメールアドレスを使用時のエラーを確認

まず、構築直後のLaravel11でRFC違反のメールアドレス宛にメール送信時の結果を確認します。

適当なController等を用意して下記のコードを実行すると例外がスローされます。

// RFC違反のメールアドレスに対してメールを送信する。
\Illuminate\Support\Facades\Mail::to('.sample@example.com')
->send(new \App\Mail\OrderShipped);

どこで例外がスローされているか

エラー内容を参考に例外のスロー元を調査すると下記クラスに到達しました。

13行目のself::$validator->isValid()で、falseが返っているためRfcComplianceExceptionがスローされます。

// vendor/symfony/mime/Address.php
public function __construct(string $address, string $name = '')
{
if (!class_exists(EmailValidator::class)) {
throw new LogicException(sprintf('The "%s" class cannot be used as it needs "%s". Try running "composer require egulias/email-validator".', __CLASS__, EmailValidator::class));
}

self::$validator ??= new EmailValidator();

$this->address = trim($address);
$this->name = trim(str_replace(["\n", "\r"], '', $name));

if (!self::$validator->isValid($this->address, class_exists(MessageIDValidation::class) ? new MessageIDValidation() : new RFCValidation())) {
throw new RfcComplianceException(sprintf('Email "%s" does not comply with addr-spec of RFC 2822.', $address));
}
}

self::$validator->isValid()の中身は下記のようになっています。

6行目で$parser->parse()でメールアドレス形式をチェックしているようです。

// vendor/egulias/email-validator/src/Validation/MessageIDValidation.php
public function isValid(string $email, EmailLexer $emailLexer): bool
{
$parser = new MessageIDParser($emailLexer);
try {
$result = $parser->parse($email);
$this->warnings = $parser->getWarnings();
if ($result->isInvalid()) {
/** @psalm-suppress PropertyTypeCoercion */
$this->error = $result;
return false;
}
} catch (\Exception $invalid) {
$this->error = new InvalidEmail(new ExceptionFound($invalid), '');
return false;
}

return true;
}

メールアドレス自体を加工する案

例外がスローされている処理は判明しましたが、修正に着手する前に他の案が無いか考えてみます。

メールアドレスのローカルパート部分をダブルクォーテーションで囲むと、例外はスローされずメール送信は成功しました。

ただ、案件やシステムによっては、メールアドレスの加工を避けたい場合もありそうな気はします。

// RFC違反のメールアドレスに対してメールを送信する。
\Illuminate\Support\Facades\Mail::to('".sample"@example.com')
->send(new \App\Mail\OrderShipped);

※メールサーバによってはこの対応で送信できないケースもあるようです。

例外スローを回避する方法は無いか

上記以外の方法となるとAddress.phpを継承したクラスを用意して差し替えれないか挑戦しましたが
クラスにfinalキーワードが付いているので、継承やサービスコンテナの結合は厳しそうです。

// vendor/symfony/mime/Address.php
/** * @author Fabien Potencier <fabien@symfony.com> */
final class Address
{

また、MessageIDValidation.phpにはnew MessageIDParserがハードコーディングされているのでこれを置換するのも厳しそうです。

// vendor/egulias/email-validator/src/Validation/MessageIDValidation.php
public function isValid(string $email, EmailLexer $emailLexer): bool
{
$parser = new MessageIDParser($emailLexer);

何とか上記クラスを置換できないか調査や試行錯誤を繰り返しているうちに、composer.jsonでオートロードするクラスを置換する方法に行き着きました。

MessageIDValidation.phpの処理でメールアドレス形式のチェックが通らないため例外スローに繋がっているため、これを置換すれば良いと考えcomposer.jsonの一部分を下記のように編集しました。

// composer.json
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
},
"exclude-from-classmap": [
"vendor/egulias/email-validator/src/Validation/MessageIDValidation.php"
],
"files": [
"vendor-overrides/egulias/email-validator/src/Validation/MessageIDValidation.php"
]
},

filesに指定したファイルは下記を用意しました。

例外スローの原因となっているisValid()で常にtrueを返しています。

※今回は簡易的に対応するため下記ソースとなりましたが、実際に使用する時は不要な処理だけ削除等を検討した方が良さそうです。

// vendor-overrides/egulias/email-validator/src/Validation/MessageIDValidation.php
namespace Egulias\EmailValidator\Validation;

use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Result\InvalidEmail;
use Egulias\EmailValidator\Warning\Warning;

class MessageIDValidation implements EmailValidation
{
private $warnings = [];

private $error;

public function isValid(string $email, EmailLexer $emailLexer): bool
{
return true;
}

public function getWarnings(): array
{
return $this->warnings;
}

public function getError(): ?InvalidEmail
{
return $this->error;
}
}

下記を実行すればオートロードするクラスを変更できます。

$ composer dump-autoload

再度RFC違反のメールアドレス宛にメールを送信する

再度メール送信実行してみますと、今度はエラー画面が表示されずに終了します。

// RFC違反のメールアドレスに対してメールを送信する。
\Illuminate\Support\Facades\Mail::to('.sample@example.com')
->send(new \App\Mail\OrderShipped);

最後に

RFC違反のメールアドレス宛にメール送信時、例外スローを抑制する方法でした。

着手前はクラスの継承やサービスコンテナの結合で対応できると想定していたのですが、メール送信の処理を追って行くうちにそれが無理だと思い、最終的にcomposer.jsonで、クラスを置換することになりました。

今回はメールアドレスを例に説明させていただきましたが「Laravel11が想定していない処理を実装する方法」としても参考にしていただければと思います。

最後までお読みいただき、ありがとうございました。

記事を読んで興味を持った方はぜひコチラから↓

インターンシップ
2025年卒!急成長の会社でスペシャリストを目指すインターン生募集!!
◆『技術のロジカルスタジオ』 システムに強いクリエイティブプロダクション  当社は、ソフトウェア開発会社として培った技術力を強みにディレクションからデザイン、システム開発、コーディング、メンテナンスまでワンストップで請け負うクリエイティブプロダクションです。 イラスト・グラフィック・動画制作などのクリエイティブから、データベースが絡むシステム構築まで幅広くカバーできる強みを生かし、さらなる業務拡大を目指しています。 ◆コーポレートサイト https://logical-studio.com/
株式会社ロジカルスタジオ


株式会社ロジカルスタジオでは一緒に働く仲間を募集しています
9 いいね!
9 いいね!
同じタグの記事
今週のランキング
株式会社ロジカルスタジオからお誘い
この話題に共感したら、メンバーと話してみませんか?