| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- <?php
- /**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link https://cakephp.org CakePHP(tm) Project
- * @since 3.3.0
- * @license https://opensource.org/licenses/mit-license.php MIT License
- */
- namespace Cake\Error\Middleware;
- use Cake\Core\App;
- use Cake\Core\Configure;
- use Cake\Core\Exception\Exception as CakeException;
- use Cake\Core\InstanceConfigTrait;
- use Cake\Error\ExceptionRenderer;
- use Cake\Error\PHP7ErrorException;
- use Cake\Log\Log;
- use Error;
- use Exception;
- use Throwable;
- /**
- * Error handling middleware.
- *
- * Traps exceptions and converts them into HTML or content-type appropriate
- * error pages using the CakePHP ExceptionRenderer.
- *
- * @mixin \Cake\Core\InstanceConfigTrait
- */
- class ErrorHandlerMiddleware
- {
- use InstanceConfigTrait;
- /**
- * Default configuration values.
- *
- * - `log` Enable logging of exceptions.
- * - `skipLog` List of exceptions to skip logging. Exceptions that
- * extend one of the listed exceptions will also not be logged. Example:
- *
- * ```
- * 'skipLog' => ['Cake\Error\NotFoundException', 'Cake\Error\UnauthorizedException']
- * ```
- *
- * - `trace` Should error logs include stack traces?
- *
- * @var array
- */
- protected $_defaultConfig = [
- 'skipLog' => [],
- 'log' => true,
- 'trace' => false,
- ];
- /**
- * Exception render.
- *
- * @var \Cake\Error\ExceptionRendererInterface|callable|string|null
- */
- protected $exceptionRenderer;
- /**
- * Constructor
- *
- * @param string|callable|null $exceptionRenderer The renderer or class name
- * to use or a callable factory. If null, Configure::read('Error.exceptionRenderer')
- * will be used.
- * @param array $config Configuration options to use. If empty, `Configure::read('Error')`
- * will be used.
- */
- public function __construct($exceptionRenderer = null, array $config = [])
- {
- if ($exceptionRenderer) {
- $this->exceptionRenderer = $exceptionRenderer;
- }
- $config = $config ?: Configure::read('Error');
- $this->setConfig($config);
- }
- /**
- * Wrap the remaining middleware with error handling.
- *
- * @param \Psr\Http\Message\ServerRequestInterface $request The request.
- * @param \Psr\Http\Message\ResponseInterface $response The response.
- * @param callable $next Callback to invoke the next middleware.
- * @return \Psr\Http\Message\ResponseInterface A response
- */
- public function __invoke($request, $response, $next)
- {
- try {
- return $next($request, $response);
- } catch (Throwable $exception) {
- return $this->handleException($exception, $request, $response);
- } catch (Exception $exception) {
- return $this->handleException($exception, $request, $response);
- }
- }
- /**
- * Handle an exception and generate an error response
- *
- * @param \Exception $exception The exception to handle.
- * @param \Psr\Http\Message\ServerRequestInterface $request The request.
- * @param \Psr\Http\Message\ResponseInterface $response The response.
- * @return \Psr\Http\Message\ResponseInterface A response
- */
- public function handleException($exception, $request, $response)
- {
- $renderer = $this->getRenderer($exception, $request);
- try {
- $res = $renderer->render();
- $this->logException($request, $exception);
- return $res;
- } catch (Throwable $exception) {
- $this->logException($request, $exception);
- $response = $this->handleInternalError($response);
- } catch (Exception $exception) {
- $this->logException($request, $exception);
- $response = $this->handleInternalError($response);
- }
- return $response;
- }
- /**
- * @param \Psr\Http\Message\ResponseInterface $response The response
- *
- * @return \Psr\Http\Message\ResponseInterface A response
- */
- protected function handleInternalError($response)
- {
- $body = $response->getBody();
- $body->write('An Internal Server Error Occurred');
- return $response->withStatus(500)
- ->withBody($body);
- }
- /**
- * Get a renderer instance
- *
- * @param \Exception $exception The exception being rendered.
- * @param \Psr\Http\Message\ServerRequestInterface $request The request.
- * @return \Cake\Error\ExceptionRendererInterface The exception renderer.
- * @throws \Exception When the renderer class cannot be found.
- */
- protected function getRenderer($exception, $request)
- {
- if (!$this->exceptionRenderer) {
- $this->exceptionRenderer = $this->getConfig('exceptionRenderer') ?: ExceptionRenderer::class;
- }
- // For PHP5 backwards compatibility
- if ($exception instanceof Error) {
- $exception = new PHP7ErrorException($exception);
- }
- if (is_string($this->exceptionRenderer)) {
- $class = App::className($this->exceptionRenderer, 'Error');
- if (!$class) {
- throw new Exception(sprintf(
- "The '%s' renderer class could not be found.",
- $this->exceptionRenderer
- ));
- }
- return new $class($exception, $request);
- }
- $factory = $this->exceptionRenderer;
- return $factory($exception, $request);
- }
- /**
- * Log an error for the exception if applicable.
- *
- * @param \Psr\Http\Message\ServerRequestInterface $request The current request.
- * @param \Exception $exception The exception to log a message for.
- * @return void
- */
- protected function logException($request, $exception)
- {
- if (!$this->getConfig('log')) {
- return;
- }
- foreach ((array)$this->getConfig('skipLog') as $class) {
- if ($exception instanceof $class) {
- return;
- }
- }
- Log::error($this->getMessage($request, $exception));
- }
- /**
- * Generate the error log message.
- *
- * @param \Psr\Http\Message\ServerRequestInterface $request The current request.
- * @param \Exception $exception The exception to log a message for.
- * @return string Error message
- */
- protected function getMessage($request, $exception)
- {
- $message = sprintf(
- '[%s] %s',
- get_class($exception),
- $exception->getMessage()
- );
- $debug = Configure::read('debug');
- if ($debug && $exception instanceof CakeException) {
- $attributes = $exception->getAttributes();
- if ($attributes) {
- $message .= "\nException Attributes: " . var_export($exception->getAttributes(), true);
- }
- }
- $message .= "\nRequest URL: " . $request->getRequestTarget();
- $referer = $request->getHeaderLine('Referer');
- if ($referer) {
- $message .= "\nReferer URL: " . $referer;
- }
- if ($this->getConfig('trace')) {
- $message .= "\nStack Trace:\n" . $exception->getTraceAsString() . "\n\n";
- }
- return $message;
- }
- }
|