ExceptionRenderer::class, ]; $this->setConfig($config); } /** * Display an error. * * Template method of BaseErrorHandler. * * @param array $error An array of error data. * @param bool $debug Whether or not the app is in debug mode. * @return void */ protected function _displayError(array $error, bool $debug): void { if (!$debug) { return; } Debugger::getInstance()->outputError($error); } /** * Displays an exception response body. * * @param \Throwable $exception The exception to display. * @return void * @throws \Exception When the chosen exception renderer is invalid. */ protected function _displayException(Throwable $exception): void { try { $renderer = $this->getRenderer( $exception, Router::getRequest() ); $response = $renderer->render(); $this->_sendResponse($response); } catch (Throwable $exception) { $this->_logInternalError($exception); } } /** * Get a renderer instance. * * @param \Throwable $exception The exception being rendered. * @param \Psr\Http\Message\ServerRequestInterface|null $request The request. * @return \Cake\Error\ExceptionRendererInterface The exception renderer. * @throws \RuntimeException When the renderer class cannot be found. */ public function getRenderer( Throwable $exception, ?ServerRequestInterface $request = null ): ExceptionRendererInterface { $renderer = $this->_config['exceptionRenderer']; if (is_string($renderer)) { /** @var class-string<\Cake\Error\ExceptionRendererInterface>|null $class */ $class = App::className($renderer, 'Error'); if (!$class) { throw new RuntimeException(sprintf( "The '%s' renderer class could not be found.", $renderer )); } return new $class($exception, $request); } /** @var callable $factory */ $factory = $renderer; return $factory($exception, $request); } /** * Log internal errors. * * @param \Throwable $exception Exception. * @return void */ protected function _logInternalError(Throwable $exception): void { // Disable trace for internal errors. $this->_config['trace'] = false; $message = sprintf( "[%s] %s (%s:%s)\n%s", // Keeping same message format get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine(), $exception->getTraceAsString() ); trigger_error($message, E_USER_ERROR); } /** * Method that can be easily stubbed in testing. * * @param string|\Cake\Http\Response $response Either the message or response object. * @return void */ protected function _sendResponse($response): void { if (is_string($response)) { echo $response; return; } $emitter = new ResponseEmitter(); $emitter->emit($response); } }