Skip to content

Commit d0ed489

Browse files
committed
Add ErrorHandlerTrait
1 parent 49e493e commit d0ed489

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed

src/ErrorHandlerTrait.php

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace HttpSoft\ErrorHandler;
6+
7+
use ErrorException;
8+
use Psr\Http\Message\ResponseInterface;
9+
use Psr\Http\Message\ServerRequestInterface;
10+
use Psr\Http\Server\RequestHandlerInterface;
11+
use Throwable;
12+
13+
use function error_reporting;
14+
use function get_class;
15+
use function restore_error_handler;
16+
use function set_error_handler;
17+
18+
trait ErrorHandlerTrait
19+
{
20+
/**
21+
* @var ErrorListenerInterface[]
22+
*/
23+
private array $listeners = [];
24+
25+
/**
26+
* @var ErrorResponseGeneratorInterface
27+
*/
28+
private ErrorResponseGeneratorInterface $responseGenerator;
29+
30+
/**
31+
* Adds an error listener to the queue.
32+
*
33+
* @param ErrorListenerInterface $listener
34+
*/
35+
public function addListener(ErrorListenerInterface $listener): void
36+
{
37+
$this->listeners[get_class($listener)] = $listener;
38+
}
39+
40+
/**
41+
* Handles errors and exceptions in the layers it wraps.
42+
*
43+
* When an exception is intercepted, a response with error information is generated and returned;
44+
* otherwise, the response returned by `Psr\Http\Server\RequestHandlerInterface` instance is used.
45+
*
46+
* @param ServerRequestInterface $request
47+
* @param RequestHandlerInterface $handler
48+
* @return ResponseInterface
49+
* @throws ErrorException
50+
*/
51+
private function handleError(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
52+
{
53+
set_error_handler(static function (int $severity, string $message, string $file, int $line): bool {
54+
// https://www.php.net/manual/en/function.error-reporting.php#8866
55+
// Usages the defined levels of `error_reporting()`.
56+
if (!(error_reporting() & $severity)) {
57+
// This error code is not included in `error_reporting()`.
58+
return true;
59+
}
60+
61+
throw new ErrorException($message, 0, $severity, $file, $line);
62+
});
63+
64+
try {
65+
$response = $handler->handle($request);
66+
} catch (Throwable $error) {
67+
$this->triggerListeners($error, $request);
68+
$response = $this->generateResponse($error, $request);
69+
}
70+
71+
restore_error_handler();
72+
return $response;
73+
}
74+
75+
/**
76+
* Trigger all error listeners.
77+
*
78+
* @param Throwable $error
79+
* @param ServerRequestInterface $request
80+
*/
81+
private function triggerListeners(Throwable $error, ServerRequestInterface $request): void
82+
{
83+
foreach ($this->listeners as $listener) {
84+
$listener->trigger($error, $request);
85+
}
86+
}
87+
88+
/**
89+
* Returns a response with a valid status code.
90+
*
91+
* If the generated response has a valid status code, it will be returned unchanged.
92+
*
93+
* If the status code of the generated response is invalid, but the error
94+
* code is valid, the response code will be changed to an error code.
95+
*
96+
* If the status code of the generated response and error code are
97+
* not valid, a response with the status code 500 is returned.
98+
*
99+
* @see isValidResponseCode()
100+
* @param Throwable $error
101+
* @param ServerRequestInterface $request
102+
* @return ResponseInterface
103+
*/
104+
private function generateResponse(Throwable $error, ServerRequestInterface $request): ResponseInterface
105+
{
106+
$response = $this->responseGenerator->generate($error, $request);
107+
108+
if ($this->isValidResponseCode((int) $response->getStatusCode())) {
109+
return $response;
110+
}
111+
112+
if ($this->isValidResponseCode((int) $error->getCode())) {
113+
return $response->withStatus((int) $error->getCode());
114+
}
115+
116+
return $response->withStatus(500);
117+
}
118+
119+
/**
120+
* Checks whether the response status code is valid or not.
121+
*
122+
* The valid response status code must be 4xx (client errors) or 5xx (server errors).
123+
*
124+
* @param int $responseCode
125+
* @return bool
126+
*/
127+
private function isValidResponseCode(int $responseCode): bool
128+
{
129+
return ($responseCode >= 400 && $responseCode < 600);
130+
}
131+
}

0 commit comments

Comments
 (0)