error handlers per status code
juliangut opened this issue · 1 comments
juliangut commented
interface HttpExceptionHandler
{
public function handle(RequestInterface $request, ResponseInterface $response, HttpException $exception);
}
class HttpExceptionManager
{
/**
* @var HttpExceptionHandler[]
*/
protected $handlers = [];
/**
* @var HttpExceptionHandler
*/
protected $defaultHandler;
public function __construct(HttpExceptionHandler $defaultHandler)
{
$this->defaultHandler = $defaultHandler;
}
public function addHandler(string $httpStatusCode, HttpExceptionHandler $handler)
{
$this->handlers[$httpStatusCode] = $handler;
}
public function getErrorHandler(): callable
{
$errorHandler = [$this, 'handleError'];
return function(ServerRequestInterface $request, ResponseInterface $response, \Throwable $exception) use ($errorHandler) {
return $errorHandler($request, $response, $exception);
};
}
public function getNotAllowedHandler(): callable
{
// Handle OPTIONS request
$errorHandler = [$this, 'handleError'];
return function(ServerRequestInterface $request, ResponseInterface $response, array $methods = []) use ($errorHandler) {
return $errorHandler($request, $response, HttpExceptionFactory::methodNotAllowed('Method "XXX" not allowed. Must be ...'));
};
}
public function getNotFoundHandler(): callable
{
$errorHandler = [$this, 'handleError'];
return function(ServerRequestInterface $request, ResponseInterface $response) use ($errorHandler) {
return $errorHandler($request, $response, HttpExceptionFactory::notFound('Not found'));
};
}
protected function handleError(RequestInterface $request, ResponseInterface $response, HttpException $exception)
{
$handler = $this->defaultHandler;
$statusCode = $exception->getStatusCode();
if (array_key_exists($statusCode, $this->handlers)) {
$handler = $this->handlers[$statusCode];
}
return $handler->handle($request, $response, $exception);
}
}
$exceptionManager = new HttpExceptionManager();
$container['errorHandler'] = $exceptionManager->getErrorHandler();
$container['phpErrorHandler'] = $exceptionManager->getErrorHandler();
$container['notFoundHandler'] = $exceptionManager->getNotFoundHandler();
$container['notAllowedHandler'] = $exceptionManager->getNotAllowedHandler();
$app->run();
juliangut commented
Exception handlers:
abstract class AbstractHttpExceptionHandler implements HttpExceptionHandler
{
public function handle(RequestInterface $request, ResponseInterface $resposne, HttpException $exception): string
{
return $this->getErrorOutput($this->getContentType($request), $exception, $request);
}
protected function getContentType(RequestInterface $request): string
{
$requestedContentTypes = explode(',', $request->getHeaderLine('Accept'));
$selectedContentTypes = array_intersect($requestedContentTypes, $this->getContentTypes());
if (count($selectedContentTypes)) {
return reset($selectedContentTypes);
}
return $requestedContentTypes[0] ?? '';
}
abstract protected function getContentTypes(): array;
abstract protected function getErrorOutput(string $contentType, HttpException $exception, RequestInterface $request): string;
}
class NotFoundHttpExceptionHandler extends AbstractHttpExceptionHandler
{
protected function getContentTypes(): array
{
return [
'text/plain',
'text/json',
//...
];
}
protected function getErrorOutput(string $contentType, HttpException $exception, RequestInterface $request): string
{
switch ($contentType) {
case 'text/plain':
return $this->getTextErrorOutput($exception, $request);
case 'text/json':
return $this->getJsonErrorOutput($exception, $request);
/ ...
case 'text/html':
default:
return $this->getHtmlErrorOutput($exception, $request);
}
}
protected function getTextErrorOutput(httpException $exception, RequestInterface $request): string
{
return ''';
}
/ ...
}