New in CakePHP - Improved Error and Exception Handling

CakePHP 4.4.0RC1 was released recently and I wanted to go over the new error subsystem that is being added for 4.4. I haven’t ever really loved the interface that CakePHP provided for error and exception handling. I identified a few problems that I wanted to resolve:

  • The way errors were rendered was implemented with inheritance. This meant that code was harder to test, reuse and extend. It also means that application code has to handle selecting the right class to use instead of being able to declaratively configure the framework.
  • The way fatal errors was handled was awkward. Instead of being treated like exception they were part of ErrorHandler.
  • Once an error was handled by the framework, you’re exposed to the basic PHP interface, and I believe we can model PHP Errors as an object and do better.
  • DebugKit relies on some hacks in the error system that no longer work in PHP8. For this reason alone, we needed to make a change.

I wanted to address these problems in a forward facing way giving users lots of time to adopt the new solution before we remove the deprecated implementation in 5.0.

Getting started

The new Error & Exception subsystem starts with two new classes ErrorTrap and ExceptionTrap. These classes act as entry points to handling errors and exceptions. The trap classes provide you pluggable interfaces to control how errors are rendered, and logged. They also expose application level events that your application (and DebugKit) can interact with. To migrate to the new system first remove the code that uses ErrorHandler and ConsoleErrorHandler and add the following:

Show Plain Text
  1. use Cake\Error\ErrorTrap;
  2. use Cake\Error\ExceptionTrap;
  3.  
  4. (new ErrorTrap(Configure::read('Error')))->register();
  5. (new ExceptionTrap(Configure::read('Error')))->register();
  6.  
  7. // In your src/Application.php ensure you have:
  8. $middlewareQueue->add(new ErrorHandlerMiddleware(Configure::read('Error')));

This code attaches the ErrorTrap and ExceptionTrap as PHP’s default handlers for error and exceptions. We also pass the same configuration into the ErrorHandlerMiddleware, which will let the middleware use ExceptionTrap with the same configuration as your default handler does. Beyond changing the entry point, Exception handling is mostly unchanged.

Extending error handling

The new system provides a few extension points and interfaces to allow you to swap in custom behavior if you need it. Error rendering requires you to implement the Cake\Error\ErrorRendererInterface which looks like this interface:

Show Plain Text
  1. interface ErrorRendererInterface
  2. {
  3.     /**
  4.      * Render output for the provided error.
  5.      *
  6.      * @param \Cake\Error\PhpError $error The error to be rendered.
  7.      * @param bool $debug Whether or not the application is in debug mode.
  8.      * @return string The output to be echoed.
  9.      */
  10.     public function render(PhpError $error, bool $debug): string;
  11.  
  12.     /**
  13.      * Write output to the renderer's output stream
  14.      *
  15.      * @param string $out The content to output.
  16.      * @return void
  17.      */
  18.     public function write(string $out): void;
  19. }

You may notice that the parameter to render is PhpError. This is a new addition to 4.4 and it models a PHP errors to implement an interface that closely emulates PHP exceptions.

The constructor for an error renderer gets all of the configuration data passed to the ErrorTrap. You can configure which error renderer is used with the Error.errorRenderer configuration option. Extending error logging works the same as before and you need to implement Cake\Error\ErrorLoggerInterface and configure Error.logger.

Error and Exception events

As mentioned earlier, the new subsystems dispatch events when they handle errors or exceptions. You can listen to the Error.beforeRender and Exception.beforeRender events on the global event manager.

That’s it for today. If you have any feedback on the new error subsystem, please open a GitHub Issue and let us know how it could be better.

Comments

i really appreciate your efforts Mark.
you are my role model.follows you from more than 7+ years

Ashish RB on 3/24/22

Mark I watched you stress over the use of “Trap” in the names but I think it fits perfectly. Like “trapping” rats as soon as they’re discovered in a pantry, then you can do what you want with them. I think the analogy fits. Keep up the great work.

Jamison on 6/15/22

Thank you for the kind words Ashish and Jamison.

mark story on 12/29/22

Have your say: