エラーと例外の処理

多くの PHP 内部メソッドは失敗を伝えるためにエラーを使用します。 これらのエラーはトラップされ、対処される必要があるでしょう。 CakePHP はエラーが起きるとそれを表示しまたはログに記録する既定のエラートラップを備えています。 この同じエラーハンドラがあなたのアプリケーション中のコントローラや他の部分から キャッチされなかった例外をつかまえるために用いられます。

エラーと例外の設定

エラーの設定はあなたのアプリケーションの config/app.php ファイル中で行われます。 既定では CakePHP はエラーをトラップし、そのエラーを表示/ログに記録するために ErrorHandler または ConsoleErrorHandler クラスを使用します。

エラー処理はあなたのアプリケーション用のエラー処理を調整するためにいくつかのオプションを受け入れます:

  • errorLevel - int - あなたが捕捉したいエラーレベル。 組み込みの PHP エラー定数を使い、捕捉したいエラーレベルを選択するためにビットマスクします。
  • trace - bool - ログファイル中にエラーのスタックトレースを含めます。 スタックトレースはログ中の各エラーの後に含まれるでしょう。 これはどこで/いつそのエラーが引き起こされたかを見つけるために役に立ちます。
  • exceptionRenderer - string - キャッチされなかった例外を描画する役目を担うクラス。 もしもカスタムクラスを選択する場合は src/Error 中にそのクラスのファイルを置くべきです。 このクラスは render() メソッドを実装する必要があります。
  • log - bool - true の時、 Cake\Log\Log によって例外とそのスタックトレースがログに記録されます。
  • skipLog - array - ログに記録されるべきではない例外クラス名の配列。 これは NotFoundException や他のありふれた、でもログにはメッセージを残したくない例外を除外するのに役立ちます。
  • extraFatalErrorMemory - int - 致命的エラーが起きた時にメモリの上限を増加させるためのメガバイト数を設定します。 これはログの記録やエラー処理を完了するために猶予を与えます。

エラーハンドラは既定では、 debugtrue の時にエラーを表示し、 debugfalse の時にエラーをログに記録します。 いずれも捕捉されるエラータイプは errorLevel によって制御されます。 致命的エラーのハンドラは debug レベルや errorLevel とは独立して呼び出されますが、 その結果は debug レベルによって変わるでしょう。 致命的エラーに対する既定のふるまいは内部サーバーエラーページ (debug 無効) またはエラーメッセージ、ファイルおよび行を含むページ (debug 有効) を表示します。

注釈

もしカスタムエラーハンドラを使うなら、サポートされるオプションはあなたのハンドラに依存します。

独自エラーハンドラの作成

あなたはコールバックタイプからエラーハンドラを作り出すことができます。 たとえばあなたのエラーを処理するために AppError というクラスを使うことができます。 BaseErrorHandler を継承することでエラーを処理するためのカスタムロジックを提供できます。 一つ例は:

// config/bootstrap.php の中で
use App\Error\AppError;

$errorHandler = new AppError();
$errorHandler->register();

// src/Error/AppError.php の中で
namespace App\Error;

use Cake\Error\BaseErrorHandler;

class AppError extends BaseErrorHandler
{
    public function _displayError($error, $debug)
    {
        echo 'エラーがありました!';
    }
    public function _displayException($exception)
    {
        echo '例外がありました!';
    }
}

BaseErrorHandler は二つの抽象メソッドを定義しています。 _displayError() はエラーが引き起こされた時に使われます。 _displayException() メソッドはキャッチされなかった例外がある時に呼ばれます。

致命的エラーのふるまい変更

既定のエラーハンドラは致命的エラーを例外に変換し エラーページを描画するための例外処理方法を再利用します。 もし標準のエラーページを表示したくない場合は、あなたはそれをオーバーライドできます。

// config/bootstrap.php の中で
use App\Error\AppError;

$errorHandler = new AppError();
$errorHandler->register();

// src/Error/AppError.php の中で
namespace App\Error;

use Cake\Error\BaseErrorHandler;

class AppError extends BaseErrorHandler
{
    // 他のメソッド

    public function handleFatalError($code, $description, $file, $line)
    {
        return '致命的エラーが発生しました';
    }
}

例外クラス

CakePHP にはいくつかの例外クラスがあります。 組み込みの例外処理ではキャッチされなかったあらゆる例外を捕捉しページを描画するでしょう。 例外は 400 番台のコードは使わず、内部サーバーエラーとして扱われるでしょう。

CakePHP 用の組み込みの例外

HTTP の例外

CakePHP 内部のいくつかの組み込みの例外には、内部的なフレームワークの例外の他に、 HTTP メソッド用のいくつかの例外があります。

exception Cake\Network\Exception\BadRequestException

400 Bad Request エラーに使われます。

exception Cake\Network\Exception\UnauthorizedException

401 Unauthorized エラーに使われます。

exception Cake\Network\Exception\ForbiddenException

403 Forbidden エラーに使われます。

バージョン 3.1 で追加: InvalidCsrfTokenException が追加されました。

exception Cake\Network\Exception\InvalidCsrfTokenException

無効な CSRF トークンによって引き起こされた 403 エラーに使われます。

exception Cake\Network\Exception\NotFoundException

404 Not Found エラーに使われます。

exception Cake\Network\Exception\MethodNotAllowedException

405 Method Not Allowed エラーに使われます。

exception Cake\Network\Exception\NotAcceptableException

406 Not Acceptable エラーに使われます。

バージョン 3.1.7 で追加: NotAcceptableException が追加されました。

exception Cake\Network\Exception\ConflictException

409 Conflict エラーに使われます。

バージョン 3.1.7 で追加: ConflictException が追加されました。

exception Cake\Network\Exception\GoneException

410 Gone エラーに使われます。

バージョン 3.1.7 で追加: GoneException が追加されました。

HTTP 4xx エラーステータスコードの詳細は RFC 2616#section-10.4 をご覧ください。

exception Cake\Network\Exception\InternalErrorException

500 Internal Server Error に使われます。

exception Cake\Network\Exception\NotImplementedException

501 Not Implemented エラーに使われます。

exception Cake\Network\Exception\ServiceUnavailableException

503 Service Unavailable エラーに使われます。

バージョン 3.1.7 で追加: Service Unavailableが追加されました。

HTTP 5xx エラーステータスコードの詳細は RFC 2616#section-10.5 をご覧ください。

失敗の状態や HTTP エラーを示すためにあなたのコントローラからこれらの例外を投げることができます。 HTTP の例外の使用例はアイテムが見つからなかった場合に 404 ページを描画することでしょう。

use Cake\Network\Exception\NotFoundException;

public function view($id = null)
{
    $article = $this->Articles->findById($id)->first();
    if (empty($article)) {
        throw new NotFoundException(__('記事が見つかりません'));
    }
    $this->set('article', $article);
    $this->set('_serialize', ['article']);
}

HTTP エラー用の例外を使うことで、あなたのコードを綺麗にし、 かつ RESTful なレスポンスをアプリケーションのクライアントやユーザーに返すことができます。

その他の組み込みの例外

加えて、以下のフレームワーク層の例外が利用可能で、 そしていくつかの CakePHP のコアコンポーネントから投げられるでしょう。

exception Cake\View\Exception\MissingViewException

選択されたビュークラスが見つかりません。

exception Cake\View\Exception\MissingTemplateException

選択されたテンプレートファイルが見つかりません。

exception Cake\View\Exception\MissingLayoutException

選択されたレイアウトが見つかりません。

exception Cake\View\Exception\MissingHelperException

選択されたヘルパーが見つかりません。

exception Cake\View\Exception\MissingElementException

選択されたエレメントのファイルが見つかりません。

exception Cake\View\Exception\MissingCellException

選択されたセルクラスが見つかりません。

exception Cake\View\Exception\MissingCellViewException

選択されたビューファイルが見つかりません。

exception Cake\Controller\Exception\MissingComponentException

設定されたコンポーネントが見つかりません。

exception Cake\Controller\Exception\MissingActionException

要求されたコントローラのアクションが見つかりません。

exception Cake\Controller\Exception\PrivateActionException

private/protected/_ が前置されたアクションへのアクセス。

exception Cake\Console\Exception\ConsoleException

コンソールライブラリクラスがエラーに遭遇しました。

exception Cake\Console\Exception\MissingTaskException

設定されたタスクが見つかりません。

exception Cake\Console\Exception\MissingShellException

シェルクラスが見つかりません。

exception Cake\Console\Exception\MissingShellMethodException

選択されたシェルクラスが該当の名前のメソッドを持っていません。

exception Cake\Database\Exception\MissingConnectionException

モデルの接続がありません。

exception Cake\Database\Exception\MissingDriverException

データベースドライバが見つかりません。

exception Cake\Database\Exception\MissingExtensionException

データベースドライバのための PHP 拡張がありません。

exception Cake\ORM\Exception\MissingTableException

モデルのテーブルが見つかりません。

exception Cake\ORM\Exception\MissingEntityException

モデルのエンティティが見つかりません。

exception Cake\ORM\Exception\MissingBehaviorException

モデルのビヘイビアが見つかりません。

exception Cake\ORM\Exception\PersistenceFailedException

Cake\ORM\Table::saveOrFail()Cake\ORM\Table::deleteOrFail() を使用しましたが、 エンティティは、保存/削除されませんでした。

バージョン 3.4.1 で追加: PersistenceFailedException は追加されました。

exception Cake\Datasource\Exception\RecordNotFoundException

要求されたレコードが見つかりません。 これは HTTP レスポンスヘッダに 404 を設定しもするでしょう。

exception Cake\Routing\Exception\MissingControllerException

要求されたコントローラが見つかりません。

exception Cake\Routing\Exception\MissingRouteException

要求された URL はルーティングの逆引きができないか解析できません。

exception Cake\Routing\Exception\MissingDispatcherFilterException

ディスパッチャフィルタが見つかりません。

exception Cake\Core\Exception\Exception

CakePHP での基底例外クラス。 CakePHP によって投げられるすべてのフレームワーク層の例外はこのクラスを継承するでしょう。

これらの例外クラスはすべて Exception を継承します。 Exception を継承することにより、あなたは独自の‘フレームワーク’エラーを作ることができます。 CakePHP が投げるであろう標準の例外もすべて Exception を継承します。

Cake\Core\Exception\Exception::responseHeader($header = null, $value = null)

Cake\Network\Request::header() 参照

すべての Http と Cake の例外は Exception クラスを継承し、 レスポンスにヘッダを追加するためのメソッドを持っています。 RFC2616 MethodNotAllowedException は言っています。

「レスポンスは要求されたリソースに有効なメソッドの一覧を含むAllowヘッダを含まなければ【ならない】」

コントローラ中での HTTP の例外の使用

失敗の状態を示すためにあたなのコントローラのアクションからあらゆる HTTP 関連の例外を投げることができます。 たとえば:

use Cake\Network\Exception\NotFoundException;

public function view($id = null)
{
    $article = $this->Articles->findById($id)->first();
    if (empty($article)) {
        throw new NotFoundException(__('記事が見つかりません'));
    }
    $this->set('article', 'article');
    $this->set('_serialize', ['article']);
}

上記は NotFoundException をキャッチして処理するための例外ハンドラを設定するでしょう。 既定ではエラーページを作り、例外をログに記録するでしょう。

例外のレンダラ

class Cake\Core\Exception\ExceptionRenderer(Exception $exception)

ErrorController の手助けをする ExceptionRenderer クラスは あなたのアプリケーションによって投げられたすべての例外のためのエラーページを処理します。

エラーページのビューは src/Template/Error/ に配置されます。 すべての 4xx と 5xx エラー用のテンプレートファイル error400.ctperror500.ctp がそれぞれ使われます。 必要に応じてそれらをカスタマイズすることができます。 既定では src/Template/Layout/error.ctp もエラーページに使われます。 たとえばもしも、他のレイアウト src/Template/Layout/my_error.ctp をエラーページに使いたい場合、 単純にエラー用ビューを編集して $this->layout = 'my_error'; という文を error400.ctperror500.ctp に追加してください。

各フレームワーク層の例外はコアのテンプレートに置かれた個別のビューファイル持っていますが それらは開発中の間にのみ使われますのでそれらのカスタマイズに悩む必要はまったくありません。

独自のアプリケーション例外の作成

組み込みの SPL の例外Exception そのもの、 または Cake\Core\Exception\Exception のいずれかを使って あなた独自のアプリケーション例外を作ることができます。 もしあなたのアプリケーションが以下の例外を含んでいたなら:

use Cake\Core\Exception\Exception;

class MissingWidgetException extends Exception
{};

src/Template/Error/missing_widget.ctp を作ることで、素晴らしい開発用エラーを提供できるでしょう。 本番モードでは、上記のエラーは 500 エラーとして扱われるでしょう。 Cake\Core\Exception\Exception のコンストラクタが継承されており、 データの連想配列を渡すことができます。それらの連想配列はメッセージテンプレートに差し込まれ、 開発モードでのエラーを表示するため使われるビューにも同様に差し込まれます。 これにより、エラー用の多くのコンテキスト提供して、データ豊富な例外を作ることができます。 ネイティブの __toString() メソッドを正常に動作させるメッセージテンプレートを提供することもできます。

use Cake\Core\Exception\Exception;

class MissingWidgetException extends Exception
{
    protected $_messageTemplate = '%s が見当たらないようです。';
}

throw new MissingWidgetException(['widget' => 'Pointy']);

組み込みの例外ハンドラにキャッチされた時、あなたはエラービューテンプレート中に $widget 変数を得られるでしょう。 加えてもしその例外を文字列にキャストするかその getMessage() メソッドを使うと %s が見当たらないようです。 を得られるでしょう。 これにより、ちょうど CakePHP が内部的に使っているように、あなた独自の富んだ開発用エラーを手早く作ることができます。

カスタムステータスコードの作成

例外を生成する際にコードを変えることでカスタム HTTP ステータスコードを作ることができます。

throw new MissingWidgetHelperException('それはここにはありません', 501);

これは 501 のレスポンスコードを作るでしょうが、あなたが望むあらゆる HTTP ステータスコードを使うことができます。 開発中は、もしあなたの例外が特定のテンプレートを持っておらず、かつ 500 番以上のコードを使うと error500.ctp テンプレートが表示されるでしょう。他のあらゆるエラーコードでは error400.ctp テンプレートになるでしょう。 もしカスタムの例外用のエラーテンプレートを定義している場合、そのテンプレートが開発中は使われるでしょう。 もし本番でもあなた独自の例外処理方法が欲しい場合は次の節を参照してください。

独自の例外ハンドラの継承と実装

あなたはいくつかの方法でアプリケーション固有の例外処理を実装することができます。 各方法ごとに、例外処理工程における異なる量の制御権をあなたに与えます。

  • 独自のカスタムエラーハンドラの作成と登録
  • CakePHP によって提供される BaseErrorHandler の継承
  • 既定のエラーハンドラに exceptionRenderer オプションの設定

次の節では、さまざまな方法とそれらが各々持つ利点について詳述します。

独自の例外ハンドラの作成と登録

独自の例外ハンドラの作成は、例外処理工程における全制御権をあなたに与えます。 この場合には、あなたは set_exception_handler を自分で呼ばなければならないでしょう。

BaseErrorHandler の継承

エラーと例外の設定 の節にこの例があります。

既定のハンドラの exceptionRenderer オプションの使用

もし例外処理の制御権を得る必要はないものの、どのように例外が描画されるを変更したい場合は 例外ページを描画するであろうクラスを選択するために config/app.php 中の exceptionRenderer オプションを使うことができます。

// src/Error/AppExceptionRenderer.php の中で
namespace App\Error;

use Cake\Error\ExceptionRenderer;

class AppExceptionRenderer extends ExceptionRenderer
{
    public function missingWidget($error)
    {
        return 'おっとウィジェットが見つからない!';
    }
}


// config/app.php の中で
'Error' => [
    'exceptionRenderer' => 'App\Error\AppExceptionRenderer',
    // ...
],
// ...

上記は MissingWidgetException 型のあらゆる例外を処理し、 それらのアプリケーション例外を表示/処理するためのカスタム処理ができるようにします。 例外処理メソッドは、引数として処理される例外を受け取ります。 あなたのカスタム例外処理は文字列または Response オブジェクトを返すことができます。 Response オブジェクトの返却はそれのレスポンスに対する全制御権をあなたに与えます。

注釈

カスタムレンダラはコンストラクタで例外を受け取るのを期待し、render メソッドを実装します。

もしもカスタム例外処理を使っている場合、レンダラの設定変更は効果がありません。 あなたの実装の中であなたがそれを参照しない限り。

例外処理のためのカスタムコントローラ作成

慣例では CakePHP はもし存在すれば App\Controller\ErrorController を使います。 このクラスを実装することで、エラーページ出力のカスタマイズの設定に依存しない方法を提供します。

もしあなたがカスタム例外レンダラを使っているのであれば、 カスタマイズコントローラを返すために _getController() メソッドを使うことができます。 例外レンダラの中で _getController() を実装することにより あなたが望むあらゆるコントローラを使うことができます。

// src/Error/AppExceptionRenderer の中で
namespace App\Error;

use App\Controller\SuperCustomErrorController;
use Cake\Error\ExceptionRenderer;

class AppExceptionRenderer extends ExceptionRenderer
{
    protected function _getController($exception)
    {
        return new SuperCustomErrorController();
    }
}

// config/app.php の中で
'Error' => [
    'exceptionRenderer' => 'App\Error\AppExceptionRenderer',
    // ...
],
// ...

エラーコントローラは、カスタムであろうと慣例のままであろうと、エラーページの表示に使われ、 すべての標準のリクエストライフサイクルイベントを受け取ります。

例外のログ記録

組み込みの例外処理を使うと、 config/app.php 中で log オプションに true を設定することで ErrorHandler によって対処されるすべての例外をログに記録することができます。 これを有効にすることで Cake\Log\Log と設定済みのロガーに各例外の記録が残るでしょう。

注釈

もしもカスタム例外処理を使っている場合、この設定は効果がないでしょう。 あなたの実装の中であなたがそれを参照しない限り。