# Friendly Error Pages

By default Kohana 3 doesn't have a method to display friendly error pages like that
seen in Kohana 2; In this short guide you will learn how it is done.

## Prerequisites

You will need `'errors' => TRUE` passed to `Kohana::init`. This will convert PHP
errors into exceptions which are easier to handle.

## 1. An Improved Exception Handler

Our custom exception handler is self explanatory.

	class Kohana_Exception extends Kohana_Kohana_Exception {

		public static function handler(Exception $e)
		{
			if (Kohana::DEVELOPMENT === Kohana::$environment)
			{
				parent::handler($e);
			}
			else
			{
				try
				{
					Kohana::$log->add(Log::ERROR, parent::text($e));

					$attributes = array
					(
						'action'  => 500,
						'message' => rawurlencode($e->getMessage())
					);

					if ($e instanceof HTTP_Exception)
					{
						$attributes['action'] = $e->getCode();
					}

					// Error sub-request.
					echo Request::factory(Route::url('error', $attributes))
					->execute()
					->send_headers()
					->body();
				}
				catch (Exception $e)
				{
					// Clean the output buffer if one exists
					ob_get_level() and ob_clean();

					// Display the exception text
					echo parent::text($e);

					// Exit with an error status
					exit(1);
				}
			}
		}
	}

If we are in the development environment then pass it off to Kohana otherwise:

* Log the error
* Set the route action and message attributes.
* If a `HTTP_Exception` was thrown, then override the action with the error code.
* Fire off an internal sub-request.

The action will be used as the HTTP response code. By default this is: 500 (internal
server error) unless a `HTTP_Response_Exception` was thrown.

So this:

	throw new HTTP_Exception_404(':file does not exist', array(':file' => 'Gaia'));

would display a nice 404 error page, where:

	throw new Kohana_Exception('Directory :dir must be writable',
				array(':dir' => Debug::path(Kohana::$cache_dir)));

would display an error 500 page.

**The Route**

	Route::set('error', 'error/<action>(/<message>)', array('action' => '[0-9]++', 'message' => '.+'))
	->defaults(array(
		'controller' => 'error_handler'
	));

## 2. The Error Page Controller

	public function before()
	{
		parent::before();

		$this->template->page = URL::site(rawurldecode(Request::$instance->uri));

		// Internal request only!
		if (Request::$instance !== Request::$current)
		{
			if ($message = rawurldecode($this->request->param('message')))
			{
				$this->template->message = $message;
			}
		}
		else
		{
			$this->request->action(404);
		}

		$this->response->status((int) $this->request->action());
	}

1. Set a template variable "page" so the user can see what they requested. This
   is for display purposes only.
2. If an internal request, then set a template variable "message" to be shown to
   the user.
3. Otherwise use the 404 action. Users could otherwise craft their own error messages, eg:
   `error/404/email%20your%20login%20information%20to%20hacker%40google.com`


		public function action_404()
		{
			$this->template->title = '404 Not Found';

			// Here we check to see if a 404 came from our website. This allows the
			// webmaster to find broken links and update them in a shorter amount of time.
			if (isset ($_SERVER['HTTP_REFERER']) AND strstr($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME']) !== FALSE)
			{
				// Set a local flag so we can display different messages in our template.
				$this->template->local = TRUE;
			}

			// HTTP Status code.
			$this->response->status(404);
		}

		public function action_503()
		{
			$this->template->title = 'Maintenance Mode';
		}

		public function action_500()
		{
			$this->template->title = 'Internal Server Error';
		}

You will notice that each example method is named after the HTTP response code
and sets the request response code.

## 3. Conclusion

So that's it. Now displaying a nice error page is as easy as:

	throw new HTTP_Exception_503('The website is down');
