Completed
Push — 4.x ( bbb92b...3cece6 )
by Phil
32:45 queued 30:00
created

JsonStrategy::getNotFoundDecorator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php declare(strict_types=1);
2
3
namespace League\Route\Strategy;
4
5
use League\Route\{ContainerAwareInterface, ContainerAwareTrait};
6
use League\Route\Http\Exception as HttpException;
7
use League\Route\Http\Exception\{MethodNotAllowedException, NotFoundException};
8
use League\Route\Route;
9
use Psr\Http\Message\{ResponseFactoryInterface, ResponseInterface, ServerRequestInterface};
10
use Psr\Http\Server\{MiddlewareInterface, RequestHandlerInterface};
11
use Throwable;
12
13
use function is_array;
14
use function is_object;
15
use function json_encode;
16
17
class JsonStrategy extends AbstractStrategy implements ContainerAwareInterface
18
{
19
    use ContainerAwareTrait;
20
21
    /**
22
     * @var ResponseFactoryInterface
23
     */
24
    protected $responseFactory;
25
26
    /**
27
     * @var int
28
     */
29
    protected $jsonFlags;
30
31
    /**
32
     * Construct.
33
     *
34
     * @param ResponseFactoryInterface $responseFactory
35
     * @param int $jsonFlags
36
     */
37 32
    public function __construct(ResponseFactoryInterface $responseFactory, int $jsonFlags = 0)
38
    {
39 32
        $this->responseFactory = $responseFactory;
40 32
        $this->jsonFlags = $jsonFlags;
41
42 32
        $this->addDefaultResponseHeader('content-type', 'application/json');
43 32
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48 14
    public function invokeRouteCallable(Route $route, ServerRequestInterface $request): ResponseInterface
49
    {
50 14
        $controller = $route->getCallable($this->getContainer());
51 14
        $response = $controller($request, $route->getVars());
52
53 10
        if ($this->isJsonEncodable($response)) {
54 8
            $body     = json_encode($response, $this->jsonFlags);
55 8
            $response = $this->responseFactory->createResponse();
56 8
            $response->getBody()->write($body);
57
        }
58
59 10
        $response = $this->applyDefaultResponseHeaders($response);
60
61 10
        return $response;
62
    }
63
64
    /**
65
     * Check if the response can be converted to JSON
66
     *
67
     * Arrays can always be converted, objects can be converted if they're not a response already
68
     *
69
     * @param mixed $response
70
     *
71
     * @return bool
72
     */
73 10
    protected function isJsonEncodable($response): bool
74
    {
75 10
        if ($response instanceof ResponseInterface) {
76 2
            return false;
77
        }
78
79 8
        return (is_array($response) || is_object($response));
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85 4
    public function getNotFoundDecorator(NotFoundException $exception): MiddlewareInterface
86
    {
87 4
        return $this->buildJsonResponseMiddleware($exception);
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 4
    public function getMethodNotAllowedDecorator(MethodNotAllowedException $exception): MiddlewareInterface
94
    {
95 4
        return $this->buildJsonResponseMiddleware($exception);
96
    }
97
98
    /**
99
     * Return a middleware the creates a JSON response from an HTTP exception
100
     *
101
     * @param HttpException $exception
102
     *
103
     * @return MiddlewareInterface
104
     */
105 8
    protected function buildJsonResponseMiddleware(HttpException $exception): MiddlewareInterface
106
    {
107
        return new class($this->responseFactory->createResponse(), $exception) implements MiddlewareInterface
108
        {
109
            protected $response;
110
            protected $exception;
111
112
            public function __construct(ResponseInterface $response, HttpException $exception)
113
            {
114 8
                $this->response  = $response;
115 8
                $this->exception = $exception;
116 8
            }
117
118
            public function process(
119
                ServerRequestInterface $request,
120
                RequestHandlerInterface $requestHandler
121
            ): ResponseInterface {
122 8
                return $this->exception->buildJsonResponse($this->response);
123
            }
124
        };
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130 12
    public function getExceptionHandler(): MiddlewareInterface
131
    {
132 12
        return $this->getThrowableHandler();
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138 18
    public function getThrowableHandler(): MiddlewareInterface
139
    {
140
        return new class($this->responseFactory->createResponse()) implements MiddlewareInterface
141
        {
142
            protected $response;
143
144
            public function __construct(ResponseInterface $response)
145
            {
146 18
                $this->response = $response;
147 18
            }
148
149
            public function process(
150
                ServerRequestInterface $request,
151
                RequestHandlerInterface $requestHandler
152
            ): ResponseInterface {
153
                try {
154 18
                    return $requestHandler->handle($request);
155 12
                } catch (Throwable $exception) {
156 12
                    $response = $this->response;
157
158 12
                    if ($exception instanceof HttpException) {
159 6
                        return $exception->buildJsonResponse($response);
160
                    }
161
162 6
                    $response->getBody()->write(json_encode([
163 6
                        'status_code'   => 500,
164 6
                        'reason_phrase' => $exception->getMessage()
165
                    ]));
166
167 6
                    $response = $response->withAddedHeader('content-type', 'application/json');
168 6
                    return $response->withStatus(500, strtok($exception->getMessage(), "\n"));
169
                }
170
            }
171
        };
172
    }
173
}
174