SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

人気のPHPフレームワークLaravelを習得しよう

Laravelのエラーハンドラを理解しよう

人気のPHPフレームワークLaravelを習得しよう 第7回

  • X ポスト
  • このエントリーをはてなブックマークに追加

Laravelのエラーハンドラのカスタマイズ

 前節で、Laravelのエラー表示の切り替えとエラー画面のカスタマイズの方法がわかってもらえたと思います。そのエラーのカスタマイズをもう少し掘り下げていきます。

Handlerクラス

 Laravelのエラーハンドラの中心となるクラスは、\App\Exceptions\Handlerです。このクラスは、プロジェクトを作成した時点で、あらかじめ作成されています。そのコードをリスト2に抜粋します。

[リスト2]app/Exceptions/Handler.php
<?php
namespace App\Exceptions;

use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler  // (1)
{
	protected $dontReport = [  // (2)
	];
	protected $dontFlash = [  // (3)
		〜省略〜
	];
	public function report(Exception $exception)  // (4)
	{
		parent::report($exception);  // (5)
	}
	public function render($request, Exception $exception)  // (6)
	{
		return parent::render($request, $exception);  // (7)
	}
}

 Handlerクラスは、(1)にあるように、\Illuminate\Foundation\Exceptions\Handlerクラスを継承して作られています。

 メソッドとしては、(4)のreport()と(6)のrender()の2個が定義されています。

 先述のように、アプリケーション内で発生したすべての例外は、Laravelが処理します。その際に、このHandlerクラスのreport()メソッドとrender()メソッドが呼び出される仕組みとなっています。そして、その両方とも、プロジェクトが作成された時点でのコード(初期コード)では、親クラスの同名メソッドを呼び出しているにすぎません。ということは、この両メソッドを適切にカスタマイズすることで、エラーハンドラをカスタマイズできるようになります。

 では、この両メソッドにはどのような働きがあり、どのようにカスタマイズすればいいのでしょうか。次はそれを見ていきます。

ログへの書き出し処理を行うreport()メソッド

 リスト2の(4)のreport()メソッドは、例外が発生した際の記録処理を行うメソッドとして呼び出されます。初期コードである(5)では、親クラスのreport()メソッドを呼び出しています。このコードのおかげで、その時点で設定されたログの書き出し方式を利用して、すべての例外内容がログに記録されます。

 なお、Laravelのデフォルトのログの書き出し方式は、プロジェクトディレクトリ内のstorage/logsディレクトリに、laravel-2020-02-10.logのように「laravel-日付.log」のファイルが日次で作られるようになっています。

 このreport()メソッドに処理を記述することで、ある特定の例外が発生した場合に対して、その例外の記録方式をカスタマイズすることができます。例えば、独自に作成した例外クラスであるFatalProcessExceptionがアプリケーション内で発生するとして、この例外が発生した場合は、ログに記録するだけでなくメールも送信する、といったカスタマイズが考えられます。その場合は、次のようなコードを記述します。なお、実際のメール送信コードは割愛し、コメントのみの記載としています。

public function report(Exception $exception)
{
	if($exception instanceof FatalProcessException) {  // *
		// メール送信処理
	}
	parent::report($exception);
}

 report()メソッドの引数であるException型の$exceptionは、発生した例外そのものです。したがって、上記コード中の*の行にあるように、この引数の型を、instanceof演算子を使って特定の例外クラスと比較した条件分岐を記述することで、その例外が発生した際の記録処理を独自にカスタマイズできます。

 上記コードでは、FatalProcessExceptionの場合のみを記述していますが、次のようにelseifブロックを積み重ねることで、様々な例外に対して独自の処理を記述できます。

public function report(Exception $exception)
{
	if($exception instanceof FatalProcessException) {
		// メール送信処理
	}
	elseif($exception instanceof TooManyException) {
		// 何かの記録処理
	}
	elseif($exception instanceof TooMuchException) {
		// 何かの記録処理
	}
	parent::report($exception);
}

 上記コードでは、どの例外の場合でも、最終的に親クラスのreport()が呼び出されます。ということは、デフォルトの記録処理が行われた上で、FatalProcessExceptionやTooManyException、TooMuchExceptionの場合は、追加の記録処理が行われることを意味します。

 一方、次のように、parent::report()をelseブロックに入れることで、デフォルトの記録処理が行われる場合を限定することもできます。

public function report(Exception $exception)
{
	if($exception instanceof FatalProcessException) {
		// メール送信処理
	}
	elseif($exception instanceof TooManyException) {
		// 何かの記録処理
	}
	elseif($exception instanceof TooMuchException) {
		// 何かの記録処理
	}
	else {
		parent::report($exception);
	}
}

 このように、発生する例外それぞれに応じて、様々な処理が記述できるようになります。

記録処理から除外する$dontReportプロパティ

 前節での例は、特定の例外の場合に特別な記録処理を行うカスタマイズでした。これとは逆に、記録処理を全く行わないようにしたい例外クラスもあるでしょう。その場合は、report()メソッド内にコードを記述する必要はなく、$dontReportプロパティを使います。$dontReportプロパティは配列となっていますので、次のコードのように除外する例外クラスの完全修飾名を追加しておけば、その例外が発生した場合は、デフォルトの記録処理、つまり、parent::report()内の処理が実行されず、ログへの記録が行われなくなります。

protected $dontReport = [
	\Illuminate\Auth\AuthenticationException::class,
	\App\Exceptions\DebugProcessException::class
];

 上記コードでは、Laravelで用意されている認証機能によって、未認証の場合に発生する例外であるAuthenticationExceptionと、独自例外クラスであるDebugProcessExceptionが発生した場合は、ログへの記録が行われなくなります。

 なお、リスト2のHandlerクラスには、プロパティとしてもうひとつ$dontFlashというのが記述されています。こちらのプロパティは、Laravelのバリデーション機能で利用されるものです。本稿の内容とは直接関係ないものですので、解説を省略します。

画面表示を行うrender()メソッド

 report()メソッドと同じく、例外発生時に呼び出されるメソッドが、リスト2の(6)のrender()メソッドです。こちらも、report()メソッド同様に、初期コードとしては(7)の親クラスのrender()メソッドの呼び出し処理が記述されているだけです。

 このメソッドでは、例外が発生した時に、そのクライアントに返すHTTPレスポンスを生成する処理を行います。通常は、クライアントはブラウザであり、HTTPレスポンスとしてはそのブラウザに表示するHTMLデータです。実は、前節で紹介した図1や図2のLaravelのエラー画面が表示されたり、独自に用意した500.blade.phpなどのエラー画面テンプレートが表示されたりするのは、リスト2の(7)のparent::render()の処理のおかげなのです。

 そして、特定の例外が発生した場合に、デフォルトのエラー画面表示処理とは別の処理を行いたい場合は、report()メソッド同様に、発生した例外クラスを表す引数$exceptionの型を元に条件分岐を記述すれば可能です。

 例えば、アプリケーション内でデータベース処理に失敗した際に、独自例外としてDataAccessExceptionが発生するとします。しかも、その際にDataAccessExceptionインスタンス内にどのようにデータベース処理に失敗したかのメッセージが格納されているとします。それを表示する独自エラー画面テンプレートとして、resources/views/errorsディレクトリ内にcustom.blade.phpがあるとします。それらを使って、次のようなコードが記述できます。

public function render($request, Exception $exception)
{
	if($exception instanceof DataAccessException) {  // (1)
		$data["errorMsg"] = $exception->getMessage();  // (2)
		return response()->view("errors.custom", $data);  // (3)
	}
	return parent::render($request, $exception);
}

 report()で行なった条件分岐と同じ考え方で、発生した例外の型がDataAccessExceptionかどうかの条件分岐が、上記コードの(1)です。このifブロック内で独自テンプレートであるcustom.blade.phpを表示させる処理を行なっています。それが(3)です。Laravelのヘルパー関数であるresponse()を利用して、レスポンスオブジェクトを生成します。そのレスポンスオブジェクトに対してview()メソッドを実行することで、テンプレートファイルを元にしたレスポンスデータを生成できます。このview()メソッドは、コントローラクラスなどで、テンプレートファイルを元に画面表示を行うview()関数と同じです。その第2引数として渡すテンプレート変数用連想配列を、例外インスタンスに格納されたメッセージを元に生成しているのが(2)です。

 このようなコードを記述することで、特定の例外に対して特定のエラー画面を表示させることが可能となります。

 なお、render()メソッドの第1引数である$requestは、リクエストオブジェクトです。この引数を使うと、現在のリクエストに関係するセッションやパラメータなどのデータを取り出すこともできます。

次のページ
例外クラスを利用したエラーハンドラ

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
人気のPHPフレームワークLaravelを習得しよう連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 齊藤 新三(サイトウ シンゾウ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook<個人紹介>WINGSプロジェクト所属のテクニカルライター。Web系製作会社のシステム部門、SI会社を経てフリーランスとして独立。屋号はSarva(サルヴァ)。HAL大阪の非常勤講師を兼務。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/12130 2020/04/16 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング