cakephp: Add support for exception chaining

This is a (multiple allowed):

  • bug

  • enhancement

  • feature-discussion (RFC)

  • CakePHP Version: 3.3.9 / 2.9.3

What you did

Chained exceptions:

try {
    throw new \Exception('Foo');
} catch (\Exception $e) {
    throw new \Exception('Bar', 0, $e);
}

What happened

The chained exceptions were not rendered/logged:

2016-12-01 18:27:49 Error: [Exception] Bar
Request URL: /somethings/
Client IP: 127.0.0.1
Stack Trace:
#0 [internal function]: App\Controller\SomethingsController->index()
#1 C:\var\www\cakephp3.dev\vendor\cakephp\cakephp\src\Controller\Controller.php(435): call_user_func_array(Array, Array)
#2 C:\var\www\cakephp3.dev\vendor\cakephp\cakephp\src\Http\ActionDispatcher.php(122): Cake\Controller\Controller->invokeAction()
#3 C:\var\www\cakephp3.dev\vendor\cakephp\cakephp\src\Http\ActionDispatcher.php(96): Cake\Http\ActionDispatcher->_invoke(Object(App\Controller\SomethingsController))
#4 C:\var\www\cakephp3.dev\vendor\cakephp\cakephp\src\Routing\Dispatcher.php(60): Cake\Http\ActionDispatcher->dispatch(Object(Cake\Network\Request), Object(Cake\Network\Response))
#5 C:\var\www\cakephp3.dev\webroot\index.php(37): Cake\Routing\Dispatcher->dispatch(Object(Cake\Network\Request), Object(Cake\Network\Response))
#6 {main}

What you expected to happen

The chained exceptions are rendered/logged, like the PHP built-in exception handler:

Fatal error: Uncaught exception 'Exception' with message 'Foo' in C:\var\sandbox\test.php:3
Stack trace:
#0 {main}

Next exception 'Exception' with message 'Bar' in C:\var\sandbox\test.php:5
Stack trace:
#0 {main}
  thrown in C:\var\sandbox\test.php on line 5

I would like to add the new third argument $previous to all existing core exception classes, and to fix ErrorHandler and several core view templates (such as exception_stack_trace.ctp) to render chained exceptions correctly.

Now I am targeting 3.4.0 and 2.10.0 (Next minor versions).

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 11
  • Comments: 26 (25 by maintainers)

Commits related to this issue

Most upvoted comments

I would like to see them in reverse order (newest one on top), which is what I think intuitively people will expect. But the ordering should maybe be more clear - the 1./2. doesnt say this. Maybe we don’t need the numbering, and instead describe it?

H1 BarException
...

H2 Previous exceptions

H3 FooException
...

H3 InitialException
....

etc?

How does whoops etc handle it?

OK. I’ll learn whoops and implement a nice unpaginated stack trace as soon as possible I can.

@chinpei215 The unpaginated stack trace looks great!

👍 Yes, please do.

This is tagged to “future”, is this still intended to go into 4.x?

@chinpei215 Would be great to have this in 4.0 🙂.

The following is an example of a logged exception in the current version:

2017-10-08 09:24:14 Error: [Cake\Network\Exception\InternalErrorException] Internal Server Error
Request URL: /example/
Referer URL: https://cakephp.org/
Stack Trace:
#0 /path/to/CORE/src/Controller/Controller.php(435): App\Controller\ExamplesController->index()
#1 /path/to/CORE/src/Http/ActionDispatcher.php(119): Cake\Controller\Controller->invokeAction()
#2 /path/to/CORE/src/Http/ActionDispatcher.php(93): Cake\Http\ActionDispatcher->_invoke(Object(App\Controller\ExamplesController))
#3 /path/to/CORE/src/Http/BaseApplication.php(103): Cake\Http\ActionDispatcher->dispatch(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#4 /path/to/CORE/src/Http/Runner.php(65): Cake\Http\BaseApplication->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#5 /path/to/CORE/src/Routing/Middleware/RoutingMiddleware.php(107): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#6 /path/to/CORE/src/Http/Runner.php(65): Cake\Routing\Middleware\RoutingMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#7 /path/to/CORE/src/Routing/Middleware/AssetMiddleware.php(88): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#8 /path/to/CORE/src/Http/Runner.php(65): Cake\Routing\Middleware\AssetMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#9 /path/to/CORE/src/Error/Middleware/ErrorHandlerMiddleware.php(95): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#10 /path/to/CORE/src/Http/Runner.php(65): Cake\Error\Middleware\ErrorHandlerMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#11 /path/to/CORE/src/Http/Runner.php(51): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#12 /path/to/CORE/src/Http/Server.php(81): Cake\Http\Runner->run(Object(Cake\Http\MiddlewareQueue), Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#13 /path/to/APP/webroot/index.php(41): Cake\Http\Server->run()
#14 {main}

The following is what I am thinking now for logging chained exceptions:

2017-10-08 09:24:14 Error: [Cake\Network\Exception\InternalErrorException] Internal Server Error
Request URL: /example/
Referer URL: https://cakephp.org/

Exception 'Cake\Network\Exception\InternalErrorException' with message 'Internal Server Error' in /path/to/ExamplesController.php:18
Stack Trace:
#0 /path/to/CORE/src/Controller/Controller.php(435): App\Controller\ExamplesController->index()
#1 /path/to/CORE/src/Http/ActionDispatcher.php(119): Cake\Controller\Controller->invokeAction()
#2 /path/to/CORE/src/Http/ActionDispatcher.php(93): Cake\Http\ActionDispatcher->_invoke(Object(App\Controller\ExamplesController))
#3 /path/to/CORE/src/Http/BaseApplication.php(103): Cake\Http\ActionDispatcher->dispatch(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#4 /path/to/CORE/src/Http/Runner.php(65): Cake\Http\BaseApplication->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#5 /path/to/CORE/src/Routing/Middleware/RoutingMiddleware.php(107): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#6 /path/to/CORE/src/Http/Runner.php(65): Cake\Routing\Middleware\RoutingMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#7 /path/to/CORE/src/Routing/Middleware/AssetMiddleware.php(88): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#8 /path/to/CORE/src/Http/Runner.php(65): Cake\Routing\Middleware\AssetMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#9 /path/to/CORE/src/Error/Middleware/ErrorHandlerMiddleware.php(95): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#10 /path/to/CORE/src/Http/Runner.php(65): Cake\Error\Middleware\ErrorHandlerMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#11 /path/to/CORE/src/Http/Runner.php(51): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#12 /path/to/CORE/src/Http/Server.php(81): Cake\Http\Runner->run(Object(Cake\Http\MiddlewareQueue), Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#13 /path/to/APP/webroot/index.php(41): Cake\Http\Server->run()
#14 {main}

Previous exception 'LogicException' with 'Never come here' in /path/to/ExamplesController.php:16
#0 /path/to/CORE/src/Controller/Controller.php(435): App\Controller\ExamplesController->index()
#1 /path/to/CORE/src/Http/ActionDispatcher.php(119): Cake\Controller\Controller->invokeAction()
#2 /path/to/CORE/src/Http/ActionDispatcher.php(93): Cake\Http\ActionDispatcher->_invoke(Object(App\Controller\ExamplesController))
#3 /path/to/CORE/src/Http/BaseApplication.php(103): Cake\Http\ActionDispatcher->dispatch(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#4 /path/to/CORE/src/Http/Runner.php(65): Cake\Http\BaseApplication->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#5 /path/to/CORE/src/Routing/Middleware/RoutingMiddleware.php(107): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#6 /path/to/CORE/src/Http/Runner.php(65): Cake\Routing\Middleware\RoutingMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#7 /path/to/CORE/src/Routing/Middleware/AssetMiddleware.php(88): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#8 /path/to/CORE/src/Http/Runner.php(65): Cake\Routing\Middleware\AssetMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#9 /path/to/CORE/src/Error/Middleware/ErrorHandlerMiddleware.php(95): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#10 /path/to/CORE/src/Http/Runner.php(65): Cake\Error\Middleware\ErrorHandlerMiddleware->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response), Object(Cake\Http\Runner))
#11 /path/to/CORE/src/Http/Runner.php(51): Cake\Http\Runner->__invoke(Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#12 /path/to/CORE/src/Http/Server.php(81): Cake\Http\Runner->run(Object(Cake\Http\MiddlewareQueue), Object(Cake\Http\ServerRequest), Object(Cake\Http\Response))
#13 /path/to/APP/webroot/index.php(41): Cake\Http\Server->run()
#14 {main}

If you have other ideas, please tell me. I am planning to implement this feature on the next weekend.

+1

Not a problem @chinpei215 👍

Sounds like a great idea! Any thoughts on how we could render nested exceptions in the error pages?