Passed
Push — master ( 1cf0a9...56b73d )
by Ayan
01:37
created

Application::setExceptionDecorator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Phprest;
4
5
use Doctrine\Common\Annotations\AnnotationRegistry;
6
use League\Container\ContainerAwareTrait;
7
use League\Event\EmitterInterface;
8
use League\Event\EmitterTrait;
9
use League\Event\ListenerAcceptorInterface;
10
use Phprest\Middleware\ApiVersion;
11
use Phprest\Router\RouteCollection;
12
use Phprest\Service;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpFoundation\Response;
15
use Symfony\Component\HttpKernel\HttpKernelInterface;
16
use Symfony\Component\HttpKernel\TerminableInterface;
17
use LogicException;
18
use Exception;
19
use Stack;
20
21
class Application implements
22
    HttpKernelInterface,
23
    TerminableInterface,
24
    ListenerAcceptorInterface
25
{
26
    use EmitterTrait;
27
    use ContainerAwareTrait;
28
29
    const CONTAINER_ID_DEBUG = 'debug';
30
    const CONTAINER_ID_VENDOR = 'vendor';
31
    const CONTAINER_ID_API_VERSION = 'api-version';
32
    const CONTAINER_ID_ROUTER = 'router';
33
34
    const API_VERSION_REG_EXP = '((?:[0-9](?:\.[0-9])?){1})';
35
36
    use Service\Hateoas\Getter, Service\Hateoas\Util;
0 ignored issues
show
introduced by
The trait Phprest\Service\Hateoas\Util requires some properties which are not provided by Phprest\Application: $headers, $apiVersion, $vendor
Loading history...
37
    use Service\Logger\Getter;
38
39
    /**
40
     * @var Config
41
     */
42
    protected $configuration;
43
44
    /**
45
     * @var Stack\Builder
46
     */
47
    protected $stackBuilder;
48
49
    /**
50
     * @var RouteCollection
51
     */
52
    protected $router;
53
54
    /**
55
     * @var callable
56
     */
57
    protected $exceptionDecorator;
58
59 8
    public function __construct(Config $configuration)
60
    {
61 8
        $this->configuration = $configuration;
62 8
        $this->container = $configuration->getContainer();
63 8
        $this->router = $configuration->getRouter();
64 8
        $this->emitter = $configuration->getEventEmitter();
65
66 8
        AnnotationRegistry::registerLoader('class_exists');
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\Common\Annotati...istry::registerLoader() has been deprecated: this method is deprecated and will be removed in doctrine/annotations 2.0 autoloading should be deferred to the globally registered autoloader by then. For now, use @example AnnotationRegistry::registerLoader('class_exists') ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

66
        /** @scrutinizer ignore-deprecated */ AnnotationRegistry::registerLoader('class_exists');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
67
68 8
        $this->registerService($configuration->getHateoasService(), $configuration->getHateoasConfig());
69 8
        $this->registerService($configuration->getLoggerService(), $configuration->getLoggerConfig());
70
71 8
        $this->setErrorHandler();
72
73 8
        $this->container->add(self::CONTAINER_ID_VENDOR, $configuration->getVendor());
74 8
        $this->container->add(self::CONTAINER_ID_API_VERSION, $configuration->getApiVersion());
75 8
        $this->container->add(self::CONTAINER_ID_DEBUG, $configuration->isDebug());
76
        $this->container->add(self::CONTAINER_ID_ROUTER, function () {
77 1
            return $this->router;
78 8
        });
79
80 8
        $this->stackBuilder = new Stack\Builder;
81 8
    }
82
83 8
    public function registerService(Service\Serviceable $service, Service\Configurable $config): void
84
    {
85 8
        $service->register($this->container, $config);
86 8
    }
87
88
    /**
89
     * @param string $class Namespaced class name
90
     */
91 1
    public function registerController(string $class): void
92
    {
93 1
        $controller = new $class($this->container);
94
95
        $this->container->add($class, static function () use ($controller) {
96 1
            return $controller;
97 1
        });
98 1
    }
99
100 2
    public function registerMiddleware(string $classPath, array $arguments = [])
101
    {
102 2
        call_user_func_array([$this->stackBuilder, 'push'], array_merge([$classPath], $arguments));
103 2
    }
104
105
    /**
106
     * Run the application
107
     */
108 2
    public function run(Request $request = null)
109
    {
110 2
        if (null === $request) {
111 1
            $request = Request::createFromGlobals();
112
        }
113
114 2
        $this->registerMiddleware(ApiVersion::class);
115
116 2
        $app = $this->stackBuilder->resolve($this);
117
118 2
        $response = $app->handle($request, self::MASTER_REQUEST, false);
119 1
        $response->send();
120
121 1
        $app->terminate($request, $response);
122 1
    }
123
124
    /**
125
     * Handle the request.
126
     *
127
     * @param Request $request
128
     * @param int $type
129
     * @param bool $catch
130
     *
131
     * @return Response
132
     * @throws LogicException
133
     *
134
     * @throws Exception
135
     */
136 2
    public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true): ?Response
137
    {
138
        // Passes the request to the container
139 2
        $this->getContainer()->add(Request::class, $request);
140
141
        try {
142 2
            $this->emit('request.received', $request);
143
144 2
            $dispatcher = $this->getRouter()->getDispatcher();
145 2
            $response = $dispatcher->dispatch(
146 2
                $request->getMethod(),
147 2
                $request->getPathInfo()
148
            );
149
150 1
            $this->emit('response.created', $request, $response);
151
152 1
            return $response;
153 1
        } catch (Exception $e) {
154 1
            if (!$catch) {
155 1
                throw $e;
156
            }
157
158
            $response = call_user_func($this->exceptionDecorator, $e);
159
            if (!$response instanceof Response) {
160
                throw new LogicException(
161
                    'Exception decorator did not return an instance of Symfony\Component\HttpFoundation\Response'
162
                );
163
            }
164
165
            $this->emit('response.created', $request, $response);
166
167
            return $response;
168
        }
169
    }
170
171
    /**
172
     * Add a HEAD route
173
     *
174
     * @param mixed $action
175
     */
176 1
    public function head(string $route, $action): self
177
    {
178 1
        $this->router->addRoute('HEAD', $route, $action);
179
180 1
        return $this;
181
    }
182
183
    /**
184
     * Add a OPTIONS route
185
     *
186
     * @param mixed $action
187
     */
188 1
    public function options(string $route, $action): self
189
    {
190 1
        $this->router->addRoute('OPTIONS', $route, $action);
191
192 1
        return $this;
193
    }
194
195
    /**
196
     * Add a GET route.
197
     *
198
     * @param mixed $action
199
     */
200 2
    public function get(string $route, $action): self
201
    {
202 2
        $this->getRouter()->addRoute('GET', $route, $action);
203
204 2
        return $this;
205
    }
206
207
    /**
208
     * Add a POST route.
209
     *
210
     * @param mixed $action
211
     */
212
    public function post(string $route, $action): self
213
    {
214
        $this->getRouter()->addRoute('POST', $route, $action);
215
216
        return $this;
217
    }
218
219
    /**
220
     * Add a PUT route.
221
     *
222
     * @param mixed $action
223
     */
224
    public function put(string $route, $action): self
225
    {
226
        $this->getRouter()->addRoute('PUT', $route, $action);
227
228
        return $this;
229
    }
230
231
    /**
232
     * Add a DELETE route.
233
     *
234
     * @param mixed $action
235
     */
236
    public function delete(string $route, $action): self
237
    {
238
        $this->getRouter()->addRoute('DELETE', $route, $action);
239
240
        return $this;
241
    }
242
243
    /**
244
     * Add a PATCH route.
245
     *
246
     * @param mixed $action
247
     */
248
    public function patch(string $route, $action): self
249
    {
250
        $this->getRouter()->addRoute('PATCH', $route, $action);
251
252
        return $this;
253
    }
254
255
    /**
256
     * @return Config
257
     */
258 3
    public function getConfiguration(): Config
259
    {
260 3
        return $this->configuration;
261
    }
262
263 6
    public function getRouter(): RouteCollection
264
    {
265 6
        return $this->router;
266
    }
267
268
    /**
269
     * Return the event emitter.
270
     */
271
    public function getEventEmitter(): EmitterInterface
272
    {
273
        return $this->getEmitter();
274
    }
275
276
    /**
277
     * Terminates a request/response cycle.
278
     */
279 1
    public function terminate(Request $request, Response $response): void
280
    {
281 1
        $this->emit('response.sent', $request, $response);
282 1
    }
283
284
    /**
285
     * Subscribe to an event.
286
     *
287
     * @param string $event
288
     * @param callable $listener
289
     * @param int $priority
290
     */
291
    public function subscribe($event, $listener, $priority = ListenerAcceptorInterface::P_NORMAL)
292
    {
293
        $this->addListener($event, $listener, $priority);
294
    }
295
296
    /**
297
     * Set the exception decorator.
298
     */
299 8
    public function setExceptionDecorator(callable $func): void
300
    {
301 8
        $this->exceptionDecorator = $func;
302 8
    }
303
304 8
    protected function setErrorHandler(): void
305
    {
306 8
        $app = $this;
307
308 8
        $this->configuration->getLogHandler()->setLogger($this->serviceLogger());
309
310 8
        $this->configuration->getErrorHandler()->pushHandler($this->configuration->getLogHandler());
311 8
        $this->configuration->getErrorHandler()->register();
312
313
        $this->setExceptionDecorator(static function (Exception $e) use ($app) {
314
            $formatter = new ErrorHandler\Formatter\JsonXml($app->configuration);
315
316
            return new Response($formatter->format($e), http_response_code());
317 8
        });
318 8
    }
319
}
320