Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
114 / 114
98.80% covered (success)
98.80%
82 / 83
46.07% covered (danger)
46.07%
41 / 89
93.33% covered (success)
93.33%
14 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
Handler
100.00% covered (success)
100.00%
114 / 114
98.80% covered (success)
98.80%
82 / 83
46.07% covered (danger)
46.07%
41 / 89
100.00% covered (success)
100.00%
15 / 15
318.73
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 debugHandler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 logger
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 process
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
6 / 6
25.00% covered (danger)
25.00%
1 / 4
100.00% covered (success)
100.00%
1 / 1
3.69
 renderer
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 handleError
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 response
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
19 / 19
16.67% covered (danger)
16.67%
8 / 48
100.00% covered (success)
100.00%
1 / 1
55.88
 normalize
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 fallback
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 status
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
8 / 8
80.00% covered (warning)
80.00%
4 / 5
100.00% covered (success)
100.00%
1 / 1
4.13
 log
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 recordServerException
100.00% covered (success)
100.00%
2 / 2
87.50% covered (warning)
87.50%
7 / 8
33.33% covered (danger)
33.33%
2 / 6
100.00% covered (success)
100.00%
1 / 1
5.67
 logUnmatched
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 diagnosticType
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 escape
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Celema\Core\Error;
6
7use Celema\Core\Exception\HttpError;
8use Celema\Core\Exception\HttpMethodNotAllowed;
9use Celema\Core\Exception\HttpNotFound;
10use Celema\Router\Exception\MethodNotAllowedException;
11use Celema\Router\Exception\NotFoundException;
12use Celema\Server\Console as ServerConsole;
13use ErrorException;
14use Override;
15use Psr\Http\Message\ResponseFactoryInterface as ResponseFactory;
16use Psr\Http\Message\ResponseInterface as Response;
17use Psr\Http\Message\ServerRequestInterface as Request;
18use Psr\Http\Server\MiddlewareInterface as Middleware;
19use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
20use Psr\Log\LoggerInterface as Logger;
21use Psr\Log\LogLevel;
22use Throwable;
23
24/** @api */
25class Handler implements Middleware
26{
27    protected ?Logger $logger = null;
28    protected ?DebugHandler $debugHandler = null;
29
30    /** @var list<RendererEntry> */
31    protected array $renderers = [];
32
33    protected ?RendererEntry $defaultRenderer = null;
34
35    public function __construct(
36        protected readonly ResponseFactory $responseFactory,
37        protected readonly bool $debug = false,
38        protected readonly int $exceptionLevels = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED,
39    ) {}
40
41    public function debugHandler(DebugHandler $debugHandler): void
42    {
43        $this->debugHandler = $debugHandler;
44    }
45
46    public function logger(?Logger $logger = null): void
47    {
48        $this->logger = $logger;
49    }
50
51    #[Override]
52    public function process(Request $request, RequestHandler $handler): Response
53    {
54        set_error_handler([$this, 'handleError'], E_ALL);
55
56        try {
57            return $handler->handle($request);
58        } catch (Throwable $e) {
59            return $this->response($e, $request);
60        } finally {
61            restore_error_handler();
62        }
63    }
64
65    /**
66     * @param class-string<Throwable>|list<class-string<Throwable>>|null $exceptions
67     */
68    public function renderer(Renderer $renderer, string|array|null $exceptions = null): RendererEntry
69    {
70        if ($exceptions === null) {
71            $entry = new RendererEntry([], $renderer);
72            $this->defaultRenderer = $entry;
73
74            return $entry;
75        }
76
77        $classes = (array) $exceptions;
78        $entry = new RendererEntry($classes, $renderer);
79        $this->renderers[] = $entry;
80
81        return $entry;
82    }
83
84    public function handleError(
85        int $level,
86        string $message,
87        string $file = '',
88        int $line = 0,
89    ): bool {
90        if (($level & error_reporting()) === 0) {
91            return false;
92        }
93
94        $exception = new ErrorException($message, 0, $level, $file, $line);
95
96        if (($level & $this->exceptionLevels) !== 0) {
97            // Converted diagnostics continue through the normal exception flow.
98            throw $exception;
99        }
100
101        if ($this->logger === null) {
102            // Let PHP report diagnostics when Core has no logger.
103            return false;
104        }
105
106        // True keeps PHP from reporting a logged diagnostic again. One the
107        // logger failed on is left to PHP rather than thrown at the line
108        // that raised it.
109        return Log::attempt(
110            $this->logger,
111            LogLevel::NOTICE,
112            'PHP {type}: {diagnostic} in {file} on line {line}',
113            $exception,
114            [
115                'type' => self::diagnosticType($level),
116                'diagnostic' => $message,
117                'file' => $file,
118                'line' => $line,
119            ],
120        );
121    }
122
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
144            $this->recordServerException($exception);
145
146            return $renderer->render(
147                $exception,
148                $this->responseFactory,
149                $request,
150                $this->debug,
151            );
152        }
153
154        if ($this->debug) {
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
158                $this->recordServerException($exception);
159
160                return $debugHandler->handle($exception, $this->responseFactory);
161            }
162
163            throw $exception;
164        }
165
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
172                $this->log($logLevel, $exception, $request);
173            } else {
174                $this->logUnmatched($exception, $request);
175            }
176
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
184            );
185        }
186
187        $this->logUnmatched($exception, $request);
188        $this->recordServerException($exception);
189
190        return $this->fallback($exception);
191    }
192
193    protected function normalize(Throwable $exception, Request $request): Throwable
194    {
195        if ($exception instanceof NotFoundException) {
196            return new HttpNotFound($request, previous: $exception);
197        }
198
199        if ($exception instanceof MethodNotAllowedException) {
200            return new HttpMethodNotAllowed(
201                $request,
202                ['allowed' => $exception->allowedMethods()],
203                previous: $exception,
204            );
205        }
206
207        return $exception;
208    }
209
210    protected function fallback(Throwable $exception): Response
211    {
212        $status = $this->status($exception);
213        $title = $exception instanceof HttpError
214            ? $exception->title()
215            : '500 Internal Server Error';
216        $response = $this->responseFactory
217            ->createResponse($status)
218            ->withHeader('Content-Type', 'text/html; charset=utf-8');
219        $response->getBody()->write('<h1>' . $this->escape($title) . '</h1>');
220
221        return $response;
222    }
223
224    protected function status(Throwable $exception): int
225    {
226        if (!$exception instanceof HttpError) {
227            return 500;
228        }
229
230        $status = $exception->statusCode();
231
232        return $status >= 400 && $status <= 599 ? $status : 500;
233    }
234
235    protected function log(string|int $logLevel, Throwable $exception, Request $request): void
236    {
237        $status = $this->status($exception);
238        $kind = $status >= 500 ? 'Server' : 'Client';
239
240        Log::write($this->logger, $logLevel, $kind . ' error {status} for {method} {path}', $exception, [
241            'status' => $status,
242            ...Log::request($request),
243        ]);
244    }
245
246    protected function recordServerException(Throwable $exception): void
247    {
248        // The celema/server dev server is an optional dependency; report
249        // handled server errors to its request log when it is installed.
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
251            ServerConsole::recordException($exception, trace: $this->debug);
252        }
253    }
254
255    protected function logUnmatched(Throwable $exception, Request $request): void
256    {
257        // A client error is no server failure; a renderer entry's log level
258        // can still record it.
259        if ($this->status($exception) < 500) {
260            return;
261        }
262
263        $this->log(LogLevel::CRITICAL, $exception, $request);
264    }
265
266    private static function diagnosticType(int $level): string
267    {
268        return match ($level) {
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
270            E_NOTICE, E_USER_NOTICE => 'Notice',
271            E_WARNING, E_USER_WARNING => 'Warning',
272            default => 'Error',
273        };
274    }
275
276    private function escape(string $value): string
277    {
278        return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
279    }
280}

Paths

Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

Handler->__construct
36        protected readonly ResponseFactory $responseFactory,
37        protected readonly bool $debug = false,
38        protected readonly int $exceptionLevels = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED,
39    ) {}
Handler->debugHandler
41    public function debugHandler(DebugHandler $debugHandler): void
42    {
43        $this->debugHandler = $debugHandler;
44    }
Handler->diagnosticType
266    private static function diagnosticType(int $level): string
267    {
268        return match ($level) {
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
270            E_NOTICE, E_USER_NOTICE => 'Notice',
 
270            E_NOTICE, E_USER_NOTICE => 'Notice',
 
271            E_WARNING, E_USER_WARNING => 'Warning',
 
271            E_WARNING, E_USER_WARNING => 'Warning',
 
271            E_WARNING, E_USER_WARNING => 'Warning',
 
272            default => 'Error',
 
272            default => 'Error',
273        };
274    }
266    private static function diagnosticType(int $level): string
267    {
268        return match ($level) {
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
270            E_NOTICE, E_USER_NOTICE => 'Notice',
 
270            E_NOTICE, E_USER_NOTICE => 'Notice',
 
271            E_WARNING, E_USER_WARNING => 'Warning',
 
271            E_WARNING, E_USER_WARNING => 'Warning',
 
271            E_WARNING, E_USER_WARNING => 'Warning',
 
272            default => 'Error',
273        };
274    }
266    private static function diagnosticType(int $level): string
267    {
268        return match ($level) {
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
270            E_NOTICE, E_USER_NOTICE => 'Notice',
 
270            E_NOTICE, E_USER_NOTICE => 'Notice',
 
271            E_WARNING, E_USER_WARNING => 'Warning',
 
271            E_WARNING, E_USER_WARNING => 'Warning',
 
272            default => 'Error',
273        };
274    }
266    private static function diagnosticType(int $level): string
267    {
268        return match ($level) {
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
270            E_NOTICE, E_USER_NOTICE => 'Notice',
 
270            E_NOTICE, E_USER_NOTICE => 'Notice',
 
270            E_NOTICE, E_USER_NOTICE => 'Notice',
 
272            default => 'Error',
273        };
274    }
266    private static function diagnosticType(int $level): string
267    {
268        return match ($level) {
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
270            E_NOTICE, E_USER_NOTICE => 'Notice',
 
270            E_NOTICE, E_USER_NOTICE => 'Notice',
 
272            default => 'Error',
273        };
274    }
266    private static function diagnosticType(int $level): string
267    {
268        return match ($level) {
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
272            default => 'Error',
273        };
274    }
266    private static function diagnosticType(int $level): string
267    {
268        return match ($level) {
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
269            E_DEPRECATED, E_USER_DEPRECATED => 'Deprecated',
 
272            default => 'Error',
273        };
274    }
Handler->escape
276    private function escape(string $value): string
277    {
278        return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
279    }
Handler->fallback
210    protected function fallback(Throwable $exception): Response
211    {
212        $status = $this->status($exception);
213        $title = $exception instanceof HttpError
 
214            ? $exception->title()
 
213        $title = $exception instanceof HttpError
214            ? $exception->title()
215            : '500 Internal Server Error';
216        $response = $this->responseFactory
217            ->createResponse($status)
218            ->withHeader('Content-Type', 'text/html; charset=utf-8');
219        $response->getBody()->write('<h1>' . $this->escape($title) . '</h1>');
220
221        return $response;
222    }
210    protected function fallback(Throwable $exception): Response
211    {
212        $status = $this->status($exception);
213        $title = $exception instanceof HttpError
 
213        $title = $exception instanceof HttpError
214            ? $exception->title()
215            : '500 Internal Server Error';
 
213        $title = $exception instanceof HttpError
214            ? $exception->title()
215            : '500 Internal Server Error';
216        $response = $this->responseFactory
217            ->createResponse($status)
218            ->withHeader('Content-Type', 'text/html; charset=utf-8');
219        $response->getBody()->write('<h1>' . $this->escape($title) . '</h1>');
220
221        return $response;
222    }
Handler->handleError
85        int $level,
86        string $message,
87        string $file = '',
88        int $line = 0,
89    ): bool {
90        if (($level & error_reporting()) === 0) {
 
91            return false;
85        int $level,
86        string $message,
87        string $file = '',
88        int $line = 0,
89    ): bool {
90        if (($level & error_reporting()) === 0) {
 
94        $exception = new ErrorException($message, 0, $level, $file, $line);
95
96        if (($level & $this->exceptionLevels) !== 0) {
 
98            throw $exception;
85        int $level,
86        string $message,
87        string $file = '',
88        int $line = 0,
89    ): bool {
90        if (($level & error_reporting()) === 0) {
 
94        $exception = new ErrorException($message, 0, $level, $file, $line);
95
96        if (($level & $this->exceptionLevels) !== 0) {
 
101        if ($this->logger === null) {
 
103            return false;
85        int $level,
86        string $message,
87        string $file = '',
88        int $line = 0,
89    ): bool {
90        if (($level & error_reporting()) === 0) {
 
94        $exception = new ErrorException($message, 0, $level, $file, $line);
95
96        if (($level & $this->exceptionLevels) !== 0) {
 
101        if ($this->logger === null) {
 
109        return Log::attempt(
110            $this->logger,
111            LogLevel::NOTICE,
112            'PHP {type}: {diagnostic} in {file} on line {line}',
113            $exception,
114            [
115                'type' => self::diagnosticType($level),
116                'diagnostic' => $message,
117                'file' => $file,
118                'line' => $line,
119            ],
120        );
121    }
Handler->log
235    protected function log(string|int $logLevel, Throwable $exception, Request $request): void
236    {
237        $status = $this->status($exception);
238        $kind = $status >= 500 ? 'Server' : 'Client';
 
238        $kind = $status >= 500 ? 'Server' : 'Client';
 
238        $kind = $status >= 500 ? 'Server' : 'Client';
239
240        Log::write($this->logger, $logLevel, $kind . ' error {status} for {method} {path}', $exception, [
241            'status' => $status,
242            ...Log::request($request),
243        ]);
244    }
235    protected function log(string|int $logLevel, Throwable $exception, Request $request): void
236    {
237        $status = $this->status($exception);
238        $kind = $status >= 500 ? 'Server' : 'Client';
 
238        $kind = $status >= 500 ? 'Server' : 'Client';
 
238        $kind = $status >= 500 ? 'Server' : 'Client';
239
240        Log::write($this->logger, $logLevel, $kind . ' error {status} for {method} {path}', $exception, [
241            'status' => $status,
242            ...Log::request($request),
243        ]);
244    }
Handler->logUnmatched
255    protected function logUnmatched(Throwable $exception, Request $request): void
256    {
257        // A client error is no server failure; a renderer entry's log level
258        // can still record it.
259        if ($this->status($exception) < 500) {
 
260            return;
255    protected function logUnmatched(Throwable $exception, Request $request): void
256    {
257        // A client error is no server failure; a renderer entry's log level
258        // can still record it.
259        if ($this->status($exception) < 500) {
 
263        $this->log(LogLevel::CRITICAL, $exception, $request);
264    }
Handler->logger
46    public function logger(?Logger $logger = null): void
47    {
48        $this->logger = $logger;
49    }
Handler->normalize
193    protected function normalize(Throwable $exception, Request $request): Throwable
194    {
195        if ($exception instanceof NotFoundException) {
 
196            return new HttpNotFound($request, previous: $exception);
193    protected function normalize(Throwable $exception, Request $request): Throwable
194    {
195        if ($exception instanceof NotFoundException) {
 
199        if ($exception instanceof MethodNotAllowedException) {
 
200            return new HttpMethodNotAllowed(
201                $request,
193    protected function normalize(Throwable $exception, Request $request): Throwable
194    {
195        if ($exception instanceof NotFoundException) {
 
199        if ($exception instanceof MethodNotAllowedException) {
 
207        return $exception;
208    }
Handler->process
52    public function process(Request $request, RequestHandler $handler): Response
53    {
54        set_error_handler([$this, 'handleError'], E_ALL);
55
56        try {
57            return $handler->handle($request);
 
61            restore_error_handler();
62        }
63    }
52    public function process(Request $request, RequestHandler $handler): Response
53    {
54        set_error_handler([$this, 'handleError'], E_ALL);
55
56        try {
57            return $handler->handle($request);
 
57            return $handler->handle($request);
58        } catch (Throwable $e) {
 
59            return $this->response($e, $request);
 
61            restore_error_handler();
62        }
63    }
58        } catch (Throwable $e) {
 
59            return $this->response($e, $request);
 
59            return $this->response($e, $request);
Handler->recordServerException
246    protected function recordServerException(Throwable $exception): void
247    {
248        // The celema/server dev server is an optional dependency; report
249        // handled server errors to its request log when it is installed.
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
251            ServerConsole::recordException($exception, trace: $this->debug);
252        }
253    }
 
253    }
246    protected function recordServerException(Throwable $exception): void
247    {
248        // The celema/server dev server is an optional dependency; report
249        // handled server errors to its request log when it is installed.
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
253    }
246    protected function recordServerException(Throwable $exception): void
247    {
248        // The celema/server dev server is an optional dependency; report
249        // handled server errors to its request log when it is installed.
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
251            ServerConsole::recordException($exception, trace: $this->debug);
252        }
253    }
 
253    }
246    protected function recordServerException(Throwable $exception): void
247    {
248        // The celema/server dev server is an optional dependency; report
249        // handled server errors to its request log when it is installed.
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
253    }
246    protected function recordServerException(Throwable $exception): void
247    {
248        // The celema/server dev server is an optional dependency; report
249        // handled server errors to its request log when it is installed.
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
251            ServerConsole::recordException($exception, trace: $this->debug);
252        }
253    }
 
253    }
246    protected function recordServerException(Throwable $exception): void
247    {
248        // The celema/server dev server is an optional dependency; report
249        // handled server errors to its request log when it is installed.
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
250        if ($this->status($exception) >= 500 && class_exists(ServerConsole::class)) {
 
253    }
Handler->renderer
68    public function renderer(Renderer $renderer, string|array|null $exceptions = null): RendererEntry
69    {
70        if ($exceptions === null) {
 
71            $entry = new RendererEntry([], $renderer);
72            $this->defaultRenderer = $entry;
73
74            return $entry;
68    public function renderer(Renderer $renderer, string|array|null $exceptions = null): RendererEntry
69    {
70        if ($exceptions === null) {
 
77        $classes = (array) $exceptions;
78        $entry = new RendererEntry($classes, $renderer);
79        $this->renderers[] = $entry;
80
81        return $entry;
82    }
Handler->response
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
144            $this->recordServerException($exception);
145
146            return $renderer->render(
147                $exception,
148                $this->responseFactory,
149                $request,
150                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
158                $this->recordServerException($exception);
159
160                return $debugHandler->handle($exception, $this->responseFactory);
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
163            throw $exception;
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
171            if ($logLevel !== null) {
172                $this->log($logLevel, $exception, $request);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
174                $this->logUnmatched($exception, $request);
175            }
176
177            $this->recordServerException($exception);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
187        $this->logUnmatched($exception, $request);
188        $this->recordServerException($exception);
189
190        return $this->fallback($exception);
191    }
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
144            $this->recordServerException($exception);
145
146            return $renderer->render(
147                $exception,
148                $this->responseFactory,
149                $request,
150                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
158                $this->recordServerException($exception);
159
160                return $debugHandler->handle($exception, $this->responseFactory);
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
163            throw $exception;
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
171            if ($logLevel !== null) {
172                $this->log($logLevel, $exception, $request);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
174                $this->logUnmatched($exception, $request);
175            }
176
177            $this->recordServerException($exception);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
131                continue;
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
187        $this->logUnmatched($exception, $request);
188        $this->recordServerException($exception);
189
190        return $this->fallback($exception);
191    }
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
144            $this->recordServerException($exception);
145
146            return $renderer->render(
147                $exception,
148                $this->responseFactory,
149                $request,
150                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
158                $this->recordServerException($exception);
159
160                return $debugHandler->handle($exception, $this->responseFactory);
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
163            throw $exception;
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
171            if ($logLevel !== null) {
172                $this->log($logLevel, $exception, $request);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
174                $this->logUnmatched($exception, $request);
175            }
176
177            $this->recordServerException($exception);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
187        $this->logUnmatched($exception, $request);
188        $this->recordServerException($exception);
189
190        return $this->fallback($exception);
191    }
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
144            $this->recordServerException($exception);
145
146            return $renderer->render(
147                $exception,
148                $this->responseFactory,
149                $request,
150                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
158                $this->recordServerException($exception);
159
160                return $debugHandler->handle($exception, $this->responseFactory);
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
163            throw $exception;
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
171            if ($logLevel !== null) {
172                $this->log($logLevel, $exception, $request);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
174                $this->logUnmatched($exception, $request);
175            }
176
177            $this->recordServerException($exception);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
130            if (!$entry->matches($exception)) {
 
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
187        $this->logUnmatched($exception, $request);
188        $this->recordServerException($exception);
189
190        return $this->fallback($exception);
191    }
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
144            $this->recordServerException($exception);
145
146            return $renderer->render(
147                $exception,
148                $this->responseFactory,
149                $request,
150                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
158                $this->recordServerException($exception);
159
160                return $debugHandler->handle($exception, $this->responseFactory);
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
163            throw $exception;
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
171            if ($logLevel !== null) {
172                $this->log($logLevel, $exception, $request);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
174                $this->logUnmatched($exception, $request);
175            }
176
177            $this->recordServerException($exception);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
187        $this->logUnmatched($exception, $request);
188        $this->recordServerException($exception);
189
190        return $this->fallback($exception);
191    }
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
144            $this->recordServerException($exception);
145
146            return $renderer->render(
147                $exception,
148                $this->responseFactory,
149                $request,
150                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
158                $this->recordServerException($exception);
159
160                return $debugHandler->handle($exception, $this->responseFactory);
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
163            throw $exception;
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
171            if ($logLevel !== null) {
172                $this->log($logLevel, $exception, $request);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
174                $this->logUnmatched($exception, $request);
175            }
176
177            $this->recordServerException($exception);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
187        $this->logUnmatched($exception, $request);
188        $this->recordServerException($exception);
189
190        return $this->fallback($exception);
191    }
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
144            $this->recordServerException($exception);
145
146            return $renderer->render(
147                $exception,
148                $this->responseFactory,
149                $request,
150                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
158                $this->recordServerException($exception);
159
160                return $debugHandler->handle($exception, $this->responseFactory);
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
163            throw $exception;
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
171            if ($logLevel !== null) {
172                $this->log($logLevel, $exception, $request);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
174                $this->logUnmatched($exception, $request);
175            }
176
177            $this->recordServerException($exception);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
140            $this->log($logLevel, $exception, $request);
141        }
142
143        if ($renderer) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
187        $this->logUnmatched($exception, $request);
188        $this->recordServerException($exception);
189
190        return $this->fallback($exception);
191    }
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
144            $this->recordServerException($exception);
145
146            return $renderer->render(
147                $exception,
148                $this->responseFactory,
149                $request,
150                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
158                $this->recordServerException($exception);
159
160                return $debugHandler->handle($exception, $this->responseFactory);
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
155            $debugHandler = $this->debugHandler;
156
157            if ($debugHandler) {
 
163            throw $exception;
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
171            if ($logLevel !== null) {
172                $this->log($logLevel, $exception, $request);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
169            $logLevel = $defaultRenderer->logLevel();
170
171            if ($logLevel !== null) {
 
174                $this->logUnmatched($exception, $request);
175            }
176
177            $this->recordServerException($exception);
 
177            $this->recordServerException($exception);
178
179            return $defaultRenderer->renderer->render(
180                $exception,
181                $this->responseFactory,
182                $request,
183                $this->debug,
123    public function response(Throwable $exception, Request $request): Response
124    {
125        $exception = $this->normalize($exception, $request);
126        $renderer = null;
127        $logLevel = null;
128
129        foreach ($this->renderers as $entry) {
 
129        foreach ($this->renderers as $entry) {
130            if (!$entry->matches($exception)) {
131                continue;
132            }
133
134            $renderer = $entry->renderer;
135            $logLevel = $entry->logLevel();
136            break;
137        }
138
139        if ($logLevel !== null) {
 
143        if ($renderer) {
 
154        if ($this->debug) {
 
166        $defaultRenderer = $this->defaultRenderer;
167
168        if ($defaultRenderer) {
 
187        $this->logUnmatched($exception, $request);
188        $this->recordServerException($exception);
189
190        return $this->fallback($exception);
191    }
Handler->status
224    protected function status(Throwable $exception): int
225    {
226        if (!$exception instanceof HttpError) {
 
227            return 500;
224    protected function status(Throwable $exception): int
225    {
226        if (!$exception instanceof HttpError) {
 
230        $status = $exception->statusCode();
231
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
233    }
224    protected function status(Throwable $exception): int
225    {
226        if (!$exception instanceof HttpError) {
 
230        $status = $exception->statusCode();
231
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
233    }
224    protected function status(Throwable $exception): int
225    {
226        if (!$exception instanceof HttpError) {
 
230        $status = $exception->statusCode();
231
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
233    }
224    protected function status(Throwable $exception): int
225    {
226        if (!$exception instanceof HttpError) {
 
230        $status = $exception->statusCode();
231
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
 
232        return $status >= 400 && $status <= 599 ? $status : 500;
233    }