alexpts/psr15-middlewares

View on GitHub
src/ErrorToJsonResponse.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
declare(strict_types=1);

namespace PTS\PSR15\Middlewares;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use PTS\Psr7\Response\JsonResponse;
use Throwable;
use function count;

class ErrorToJsonResponse implements MiddlewareInterface
{
    protected const HTTP_ERROR_STATUS_CODE = 500;

    public function __construct(
        protected int $statusCodeDefault = 500,
        protected bool $showError = false,
    ) {
    }

    /**
     * @inheritdoc
     * @throws Throwable
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $targetLevel = count(ob_get_status(true));

        try {
            $response = $handler->handle($request);
        } catch (Throwable $throwable) {
            $response = $this->handleThrowable($throwable, $targetLevel);
        }

        return $response;
    }

    protected function handleThrowable(Throwable $throwable, int $targetLevel): ResponseInterface
    {
        $this->closeOutputBuffers($targetLevel);
        return $this->createResponse($throwable);
    }

    protected function createResponse(Throwable $throwable): ResponseInterface
    {
        $statusCode = $this->getStatusCode($throwable);
        $showError = $this->showError ?? $statusCode < self::HTTP_ERROR_STATUS_CODE;
        $message = $showError ? $throwable->getMessage() : 'Error';

        return new JsonResponse([
            'status' => 'error',
            'code' => $throwable->getCode(),
            'httpStatusCode' => $statusCode,
            'message' => $message
        ], $statusCode);
    }

    protected function getStatusCode(Throwable $throwable): int
    {
        if (method_exists($throwable, 'getStatusCode')) {
            return $throwable->getStatusCode();
        }

        return self::HTTP_ERROR_STATUS_CODE;
    }

    /**
     * Cleans or flushes output buffers up to target level.
     * Resulting level can be greater than target level if a non-removable buffer has been encountered.
     *
     * @param int $targetLevel
     * @param bool $flush
     *
     * @see original Symfony Response::closeOutputBuffers
     */
    protected function closeOutputBuffers(int $targetLevel = 0, bool $flush = false): void
    {
        $status = ob_get_status(true);
        $level = count($status);

        while ($level-- > $targetLevel) {
            $flush ? ob_end_flush() : ob_end_clean();
        }
    }
}