Symfony2のエラーページをカスタマイズしてみる

Symfony2でエラーページをカスタマイズしたかったのでやり方を調べてみた。

エラーページのカスタマイズ方法 | Symfony2日本語ドキュメント

エラーページをカスタマイズするには下記の2種類の方法があるみたい。

  1. エラーテンプレートのカスタマイズ
  2. 例外処理のカスタマイズ

1の方法は上記のドキュメントにも載っている通り非常に簡単にできるけど自由度が効かないので、今回は2の方法を試してみた。

例外処理をカスタマイズするには、まず初めに例外処理をするクラスをサービス登録します。
この時、kernel.exceptionイベント発生時に動くようにeventにはkernel.exceptionを設定します。

src/Acme/DemoBundle/Resources/config/services.yml

custom.exception.listener:
    class: Acme\DemoBundle\Listener\CustomExceptionListener
    arguments: ['@templating']
    tags:
        - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }

ここまで設定したら次は実際に例外処理を行うクラスを作成します。
自分は管理画面の時だけこの処理を実行したかったので、URLが/adminの時だけ処理されるように設定しています。
また、Securityコンポーネントでユーザーと権限管理を行なっているので、権限が足りなかった際のページも用意しています。

src/Acme/DemoBundle/Listener/CustomExceptionListener.php

<?php
namespace Acme\DemoBundle\Listener;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Bundle\TwigBundle\TwigEngine;

class CustomExceptionListener
{
    /**
     * @var TwigEngine
     */
    protected $templating;


    /**
     * __construct
     *
     * @param \Symfony\Bundle\TwigBundle\TwigEngine $templating
     */
    public function __construct(TwigEngine $templating)
    {
        $this->templating = $templating;
    }

    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        static $handling;

        $exception = $event->getException();

        if (true === $handling) {
            return;
        }

        if (method_exists($exception, 'getStatusCode')) {
            $code = $exception->getStatusCode();
        }

        // URLが/adminの時だけ実行
        if (0 === strpos($event->getRequest()->getPathInfo(), '/admin')) {
            $handling = true;

            // Securityコンポーネントで権限が足りないときの処理
            if ($exception instanceof AccessDeniedHttpException) {
                $message = $this->templating->render('AcmeDemoBundle:Exception:access_denied.html.twig');
            }

            // HTTP 404 Status
            if (isset($code) && 404 == $code) {
                $message = $this->templating->render('AcmeDemoBundle:404.html.twig', array());
                $response = new Response($message, $code);
                $event->setResponse($response);
            }

            if (isset($message)) {
                $response = new Response($message, $code);
                $event->setResponse($response);
            }
        }

        $handling = false;
    }
}

ここまで出来たら後はテンプレートを用意してあげれば完了。
もっと色々エラー内容を拾って処理したいって人は元の例外処理クラスを参考にしてみるといいかもしれません。
元々の例外処理をしているクラスは下記のファイルです。

src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php