イントロダクション
エラーと例外の処理は、新しいLaravelプロジェクトの開始時に最初から設定されています。App\Exceptions\Handler
クラスは、アプリケーションが投げるすべての例外がログに記録され、ユーザーへレンダーする場所です。このドキュメント全体を通して、このクラスについて詳しく説明します。
設定
config/app.php
設定ファイルのdebug
オプションは、エラーに関する情報が実際にユーザーに表示される量を決定します。デフォルトでは、このオプションは、.env
ファイルに保存されているAPP_DEBUG
環境変数の値を尊重するように設定されています。
ローカル開発中は、APP_DEBUG
環境変数をtrue
に設定する必要があります。実稼働環境では、この値は常にfalse
である必要があります。本番環境で値がtrue
に設定されていると、機密性の高い設定値がアプリケーションのエンドユーザーに公開されるリスクが起きます。
例外ハンドラ
例外のレポート
すべての例外は、App\Exceptions\Handler
クラスが処理します。このクラスは、カスタム例外レポートとレンダリングコールバックを登録できるregister
メソッドを持っています。こうした各概念について詳しく説明します。例外レポートは、例外をログに記録したり、Flare、Bugsnag、Sentryなどの外部サービスへ送信したりするために使用します。デフォルトで例外はログ設定に基づいてログに記録します。ただし、必要に応じて例外を自由に記録できます。
たとえば、さまざまなタイプの例外をさまざまな方法で報告する必要がある場合は、reportable
メソッドを使用して、特定のタイプの例外を報告する必要があるときに実行するクロージャを登録できます。Laravelは、クロージャのタイプヒントを調べることで、クロージャが報告する例外のタイプを推測します。
use App\Exceptions\InvalidOrderException;
/**
* アプリケーションの例外処理コールバックを登録
*
* @return void
*/
public function register()
{
$this->reportable(function (InvalidOrderException $e) {
//
});
}
reportable
メソッドを使用してカスタム例外レポートコールバックを登録した場合でも、Laravelはアプリケーションのデフォルトのログ設定を使用して例外をログに記録します。デフォルトのログスタックへ例外の伝播を停止する場合は、レポートコールバックを定義するときにstop
メソッドを使用するか、コールバックからfalse
を返します。
$this->reportable(function (InvalidOrderException $e) {
//
})->stop();
$this->reportable(function (InvalidOrderException $e) {
return false;
});
Tip!! 特定の例外のレポートをカスタマイズするには、レポート可能な例外を利用することもできます。
グローバルログコンテキスト
利用可能な場合、Laravelは現在のユーザーのIDをコンテキストデータとしてすべての例外のログメッセージに自動的に追加します。アプリケーションのApp\Exceptions\Handler
クラスのcontext
メソッドをオーバーライドすることで、独自のグローバルコンテキストデータを定義できます。この情報は、アプリケーションによって書き込まれるすべての例外のログメッセージに含まれます。
/**
* ログ用のデフォルトのコンテキスト変数を取得
*
* @return array
*/
protected function context()
{
return array_merge(parent::context(), [
'foo' => 'bar',
]);
}
例外ログコンテキスト
すべてのログメッセージにコンテキストを追加することは便利ですが、特定の例外にはログに含めたい固有のコンテキストがある場合もあります。アプリケーションのカスタム例外にcontext
メソッドを定義することで、例外のログエントリに追加すべき、その例外に関連するデータを指定することができます。
<?php
namespace App\Exceptions;
use Exception;
class InvalidOrderException extends Exception
{
// ...
/**
* 例外のコンテキスト情報を取得
*
* @return array
*/
public function context()
{
return ['order_id' => $this->orderId];
}
}
report
ヘルパ
場合により、例外を報告する必要はあるが、現在のリクエストの処理を続行する必要がある場合もあります。report
ヘルパ関数を使用すると、エラーページをユーザーに表示せずに、例外ハンドラを介して例外をすばやく報告できます。
public function isValid($value)
{
try {
// 値のバリデーション…
} catch (Throwable $e) {
report($e);
return false;
}
}
タイプによる例外の無視
アプリケーションを構築するときに、単に無視するだけで報告したくないタイプの例外もいくつかあるでしょう。アプリケーションの例外ハンドラには、空の配列に初期化されている$dontReport
プロパティが含まれています。このプロパティに追加したクラスは報告されません。ただし、カスタムレンダリングロジックがある場合もあります。
use App\Exceptions\InvalidOrderException;
/**
* 報告しない例外タイプのリスト
*
* @var array
*/
protected $dontReport = [
InvalidOrderException::class,
];
Tip!! Laravelは、404 HTTP "not found"エラーや無効なCSRFトークンによって生成された419 HTTPレスポンスに起因する例外など、いくつかのタイプのエラーを皆さんのために裏でこっそり無視しています。
例外のレンダー
デフォルトでは、Laravel例外ハンドラは例外をHTTPレスポンスへ変換します。ただし、特定タイプの例外に対して、カスタムレンダリングクロージャを自由に登録できます。これは、例外ハンドラのrenderable
メソッドを介して実行します。
renderable
メソッドへ渡すクロージャは、Response
ヘルパを介して生成されるIlluminate\Http\Response
のインスタンスを返す必要があります。Laravelは、クロージャのタイプヒントを調べることで、どのタイプの例外をクロージャがレンダーするのか推測します。
use App\Exceptions\InvalidOrderException;
/**
* アプリケーションの例外処理コールバックを登録
*
* @return void
*/
public function register()
{
$this->renderable(function (InvalidOrderException $e, $request) {
return response()->view('errors.invalid-order', [], 500);
});
}
また、renderable
メソッドを使い、NotFoundHttpException
などのLaravelやSymfonyの組み込み例外のレンダー動作をオーバーライドすることもできます。renderable
メソッドに指定したクロージャが値を返さない場合は、Laravelのデフォルト例外レンダーが利用されます。
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* アプリケーションの例外処理コールバックの登録
*
* @return void
*/
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Record not found.'
], 404);
}
});
}
Reportable/Renderable例外
例外ハンドラのregister
メソッドで例外を型チェックする代わりに、カスタム例外に直接report
メソッドとrender
メソッドを定義することもできます。これらのメソッドが存在する場合、フレームワークによって自動的に呼び出されます。
<?php
namespace App\Exceptions;
use Exception;
class InvalidOrderException extends Exception
{
/**
* 例外を報告
*
* @return bool|null
*/
public function report()
{
//
}
/**
* 例外をHTTPレスポンスへレンダリング
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function render($request)
{
return response(...);
}
}
LaravelやSymfonyの組み込み済み例外など、既存のレンダリング可能な例外を拡張している場合は、例外のrender
メソッドからfalse
を返し、例外のデフォルトHTTPレスポンスをレンダーできます。
/**
* Render the exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function render($request)
{
// 例外がカスタムレンダリングを必要とするか判定…
return false;
}
特定の条件が満たされた場合にのみ必要なカスタムレポートロジックが例外に含まれている場合は、デフォルトの例外処理設定を使用して例外をレポートするようにLaravelに指示する必要が起き得ます。これを行うには、例外のreport
メソッドからfalse
を返します。
/**
* 例外を報告
*
* @return bool|null
*/
public function report()
{
// 例外にカスタムレポートが必要かどうかを判定…
return false;
}
Tip!!
report
メソッドで必要な依存関係をタイプヒントすると、Laravelのサービスコンテナがメソッドへ自動的に依存を注入します。
HTTP例外
一部の例外は、サーバからのHTTPエラーコードを表します。たとえば、「ページが見つかりません」エラー(404)、「不正なエラー」(401)、または開発者が500エラーを生成する可能性もあります。アプリケーションのどこからでもこのようなレスポンスを生成したい場合は、abort
ヘルパを使用できます。
abort(404);
カスタムHTTPエラーページ
Laravelを使用すると、さまざまなHTTPステータスコードのカスタムエラーページを簡単に表示できます。たとえば、404
HTTPステータスコードのエラーページをカスタマイズする場合は、resources/views/errors/404.blade.php
ビューテンプレートを作成します。このビューは、アプリケーションが生成するすべての404エラーでレンダーされます。このディレクトリ内のビューには、対応するHTTPステータスコードと一致する名前を付ける必要があります。abort
関数によって生成されたSymfony\Component\HttpKernel\Exception\HttpException
インスタンスは$exception
変数としてビューに渡されます。
<h2>{{ $exception->getMessage() }}</h2>
vendor:publish
Artisanコマンドを使用して、Laravelのデフォルトのエラーページテンプレートをリソース公開できます。テンプレートをリソース公開したら、好みに合わせてカスタマイズしてください。
php artisan vendor:publish --tag=laravel-errors