Exceptions
Exceptions can be used for a variety of uses in your application. CakePHP uses exceptions internally to indicate logic errors or misuse. All of the exceptions CakePHP raises extend CakeException
, and there are class/task specific exceptions that extend this base class.
CakePHP also provides a number of exception classes that you can use for HTTP errors. See the section on Built In Exceptions for more information.
Exception configuration
There are a few keys available for configuring exceptions:
Configure::write('Exception', array(
'handler' => 'ErrorHandler::handleException',
'renderer' => 'ExceptionRenderer',
'log' => true
));
handler
- callback - The callback to handle exceptions. You can set this to any callback type, including anonymous functions.renderer
- string - The class responsible for rendering uncaught exceptions. If you choose a custom class you should place the file for that class in app/Lib/Error. This class needs to implement arender()
method.log
- boolean - When true, exceptions + their stack traces will be logged to CakeLog.consoleHandler
- callback - The callback used to handle exceptions, in a console context. If undefined, CakePHP's default handler will be used.
Exception rendering by default displays an HTML page, you can customize either the handler or the renderer by changing the settings. Changing the handler, allows you to take full control over the exception handling process, while changing the renderer allows you to easily change the output type/contents, as well as add in application specific exception handling.
Added in version 2.2
The Exception.consoleHandler
option was added in 2.2.
Exception classes
There are a number of exception classes in CakePHP. Each exception replaces a cakeError()
error messages from the past. Exceptions offer additional flexibility in that they can be extended and contain some logic. The built in exception handling will capture any uncaught exceptions and render a useful page. Exceptions that do not specifically use a 400 range code, will be treated as an Internal Server Error.
Built-in Exceptions for CakePHP
There are several built-in exceptions inside CakePHP, outside of the internal framework exceptions, there are several exceptions for HTTP methods
Used for doing 400 Bad Request error.
Used for doing a 401 Unauthorized error.
Used for doing a 403 Forbidden error.
Used for doing a 404 Not found error.
Used for doing a 405 Method Not Allowed error.
Used for doing a 500 Internal Server Error.
Used for doing a 501 Not Implemented Errors.
You can throw these exceptions from your controllers to indicate failure states, or HTTP errors. An example use of the HTTP exceptions could be rendering 404 pages for items that have not been found:
public function view($id) {
$post = $this->Post->findById($id);
if (!$post) {
throw new NotFoundException('Could not find that post');
}
$this->set('post', $post);
}
By using exceptions for HTTP errors, you can keep your code both clean, and give RESTful responses to client applications and users.
In addition, the following framework layer exceptions are available, and will be thrown from a number of CakePHP core components:
Base exception class in CakePHP. All framework layer exceptions thrown by CakePHP will extend this class.
These exception classes all extend CakeException
. By extending CakeException, you can create your own 'framework' errors. All of the standard Exceptions that CakePHP will throw also extend CakeException.
Added in version 2.3
CakeBaseException was addedBase exception class in CakePHP. All CakeExceptions and HttpExceptions above extend this class.
method
Class::responseHeader($header = null, $value = null)
All Http and CakePHP exceptions extend the CakeBaseException class, which has a method to add headers to the response. For instance when throwing a 405 MethodNotAllowedException the rfc2616 says: "The response MUST include an Allow header containing a list of valid methods for the requested resource."
The chosen view file could not be found.
The chosen layout could not be found.
A helper was not found.
A configured behavior could not be found.
A configured component could not be found.
A configured task was not found.
The shell class could not be found.
The chosen shell class has no method of that name.
The configured database is missing.
A model's connection is missing.
A model's table is missing from CakePHP's cache or the datasource. Upon adding a new table to a datasource, the model cache (found in tmp/cache/models by default) must be removed.
The requested controller action could not be found.
The requested controller could not be found.
Private action access. Either accessing private/protected/_ prefixed actions, or trying to access prefixed routes incorrectly.
Using HTTP exceptions in your controllers
You can throw any of the HTTP related exceptions from your controller actions to indicate failure states. For example:
public function view($id) {
$post = $this->Post->findById($id);
if (!$post) {
throw new NotFoundException();
}
$this->set(compact('post'));
}
The above would cause the configured Exception.handler
to catch and process the NotFoundException
. By default this will create an error page, and log the exception.
Exception Renderer
class
ExceptionRenderer(Exception $exception)
The ExceptionRenderer class with the help of CakeErrorController
takes care of rendering the error pages for all the exceptions thrown by you application.
The error page views are located at app/View/Errors/
. For all 4xx and 5xx errors the view files error400.ctp
and error500.ctp
are used respectively. You can customize them as per your needs. By default your app/Layouts/default.ctp
is used for error pages too. If for eg. you want to use another layout app/Layouts/my_error.ctp
for your error pages, then simply edit the error views and add the statement $this->layout = 'my_error';
to the error400.ctp
and error500.ctp
.
Each framework layer exception has its own view file located in the core templates but you really don't need to bother customizing them as they are used only during development. With debug turned off all framework layer exceptions are converted to InternalErrorException
.
application exceptions
Creating your own application exceptions
You can create your own application exceptions using any of the built in SPL exceptions, Exception
itself, or CakeException
. Application exceptions that extend Exception or the SPL exceptions will be treated as 500 error in production mode. CakeException
is special in that all CakeException
objects are coerced into either 500 or 404 errors depending on the code they use. When in development mode CakeException
objects simply need a new template that matches the class name in order to provide useful information. If your application contained the following exception:
class MissingWidgetException extends CakeException {};
You could provide nice development errors, by creating app/View/Errors/missing_widget.ctp
. When in production mode, the above error would be treated as a 500 error. The constructor for CakeException
has been extended, allowing you to pass in hashes of data. These hashes are interpolated into the the messageTemplate, as well as into the view that is used to represent the error in development mode. This allows you to create data rich exceptions, by providing more context for your errors. You can also provide a message template which allows the native __toString()
methods to work as normal:
class MissingWidgetException extends CakeException {
protected $_messageTemplate = 'Seems that %s is missing.';
}
throw new MissingWidgetException(array('widget' => 'Pointy'));
When caught by the built-in exception handler, you would get a $widget
variable in your error view template. In addition if you cast the exception as a string or use its getMessage()
method you will get Seems that Pointy is missing.
. This allows you easily and quickly create your own rich development errors, just like CakePHP uses internally.
Creating custom status codes
You can create custom HTTP status codes by changing the code used when creating an exception:
throw new MissingWidgetHelperException('Its not here', 501);
Will create a 501
response code, you can use any HTTP status code you want. In development, if your exception doesn't have a specific template, and you use a code equal to or greater than 500
you will see the error500
template. For any other error code you'll get the error400
template. If you have defined an error template for your custom exception, that template will be used in development mode. If you'd like your own exception handling logic even in production, see the next section.
Extending and implementing your own Exception handlers
You can implement application specific exception handling in one of a few ways. Each approach gives you different amounts of control over the exception handling process.
- Set
Configure::write('Exception.handler', 'YourClass::yourMethod');
- Create
AppController::appError();
- Set
Configure::write('Exception.renderer', 'YourClass');
In the next few sections, we will detail the various approaches and the benefits each has.
Create your own Exception handler with Exception.handler
Creating your own exception handler gives you full control over the exception handling process. The class you choose should be loaded in your app/Config/bootstrap.php
, so it's available to handle any exceptions. You can define the handler as any callback type. By settings Exception.handler
CakePHP will ignore all other Exception settings. A sample custom exception handling setup could look like:
// in app/Config/core.php
Configure::write('Exception.handler', 'AppExceptionHandler::handle');
// in app/Config/bootstrap.php
App::uses('AppExceptionHandler', 'Lib');
// in app/Lib/AppExceptionHandler.php
class AppExceptionHandler {
public static function handle($error) {
echo 'Oh noes! ' . $error->getMessage();
// ...
}
// ...
}
You can run any code you wish inside handleException
. The code above would simple print 'Oh noes! ' plus the exception message. You can define exception handlers as any type of callback, even an anonymous function if you are using PHP 5.3:
Configure::write('Exception.handler', function ($error) {
echo 'Ruh roh ' . $error->getMessage();
});
By creating a custom exception handler you can provide custom error handling for application exceptions. In the method provided as the exception handler you could do the following:
// in app/Lib/AppErrorHandler.php
class AppErrorHandler {
public static function handleException($error) {
if ($error instanceof MissingWidgetException) {
return self::handleMissingWidget($error);
}
// do other stuff.
}
}
appError
Using AppController::appError()
Implementing this method is an alternative to implementing a custom exception handler. It's primarily provided for backwards compatibility, and is not recommended for new applications. This controller method is called instead of the default exception rendering. It receives the thrown exception as its only argument. You should implement your error handling in that method:
class AppController extends Controller {
public function appError($error) {
// custom logic goes here.
}
}
Using a custom renderer with Exception.renderer to handle application exceptions
If you don't want to take control of the exception handling, but want to change how exceptions are rendered you can use Configure::write('Exception.renderer', 'AppExceptionRenderer');
to choose a class that will render exception pages. By default :php:class`ExceptionRenderer` is used. Your custom exception renderer class should be placed in app/Lib/Error
. Or an Error
directory in any bootstrapped Lib path. In a custom exception rendering class you can provide specialized handling for application specific errors:
// in app/Lib/Error/AppExceptionRenderer.php
App::uses('ExceptionRenderer', 'Error');
class AppExceptionRenderer extends ExceptionRenderer {
public function missingWidget($error) {
echo 'Oops that widget is missing!';
}
}
The above would handle any exceptions of the type MissingWidgetException
, and allow you to provide custom display/handling logic for those application exceptions. Exception handling methods get the exception being handled as their argument.
NOTE
Your custom renderer should expect an exception in its constructor, and implement a render method. Failing to do so will cause additional errors.
NOTE
If you are using a custom Exception.handler
this setting will have no effect. Unless you reference it inside your implementation.
Creating a custom controller to handle exceptions
In your ExceptionRenderer sub-class, you can use the _getController
method to allow you to return a custom controller to handle your errors. By default CakePHP uses CakeErrorController
which omits a few of the normal callbacks to help ensure errors always display. However, you may need a more custom error handling controller in your application. By implementing _getController
in your AppExceptionRenderer
class, you can use any controller you want:
class AppExceptionRenderer extends ExceptionRenderer {
protected function _getController($exception) {
App::uses('SuperCustomErrorController', 'Controller');
return new SuperCustomErrorController();
}
}
Alternatively, you could just override the core CakeErrorController, by including one in app/Controller
. If you are using a custom controller for error handling, make sure you do all the setup you need in your constructor, or the render method. As those are the only methods that the built-in ErrorHandler
class directly call.
Logging exceptions
Using the built-in exception handling, you can log all the exceptions that are dealt with by ErrorHandler by setting Exception.log
to true in your core.php. Enabling this will log every exception to CakeLog
and the configured loggers.
NOTE
If you are using a custom Exception.handler
this setting will have no effect. Unless you reference it inside your implementation.