Passed
Push — master ( 56b5e5...cde5ac )
by Ayan
08:19 queued 11s
created

Application   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 281
Duplicated Lines 0 %

Test Coverage

Coverage 76.29%

Importance

Changes 9
Bugs 3 Features 0
Metric Value
wmc 24
eloc 89
c 9
b 3
f 0
dl 0
loc 281
ccs 74
cts 97
cp 0.7629
rs 10

20 Methods

Rating   Name   Duplication   Size   Complexity  
A subscribe() 0 3 1
A getConfiguration() 0 3 1
A put() 0 5 1
A get() 0 5 1
A head() 0 5 1
A setErrorHandler() 0 13 1
A options() 0 5 1
A registerController() 0 6 1
A __construct() 0 22 1
A getEventEmitter() 0 3 1
A patch() 0 5 1
A delete() 0 5 1
A handle() 0 32 4
A terminate() 0 3 1
A run() 0 14 2
A post() 0 5 1
A setExceptionDecorator() 0 3 1
A registerService() 0 3 1
A getRouter() 0 3 1
A registerMiddleware() 0 3 1
1
<?php
2
3
namespace Phprest;
4
5
use Closure;
6
use Doctrine\Common\Annotations\AnnotationRegistry;
7
use League\Container\ContainerAwareTrait;
8
use League\Event\EmitterInterface;
9
use League\Event\EmitterTrait;
10
use League\Event\ListenerAcceptorInterface;
11
use Phprest\Middleware\ApiVersion;
12
use Phprest\Router\RouteCollection;
13
use Phprest\Service;
14
use Symfony\Component\HttpFoundation\Request;
15
use Symfony\Component\HttpFoundation\Response;
16
use Symfony\Component\HttpKernel\HttpKernelInterface;
17
use Symfony\Component\HttpKernel\TerminableInterface;
18
use LogicException;
19
use Exception;
20
use Stack;
21
22
class Application implements
23
    HttpKernelInterface,
24
    TerminableInterface,
25
    ListenerAcceptorInterface
26
{
27
    use EmitterTrait;
28
    use ContainerAwareTrait;
29
    use Service\Hateoas\Getter;
30
    use 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...
31
    use Service\Logger\Getter;
32
33
    public const CONTAINER_ID_DEBUG = 'debug';
34
    public const CONTAINER_ID_VENDOR = 'vendor';
35
    public const CONTAINER_ID_API_VERSION = 'api-version';
36
    public const CONTAINER_ID_ROUTER = 'router';
37
    public const API_VERSION_REG_EXP = '((?:[0-9](?:\.[0-9])?){1})';
38
39
    protected Config $configuration;
40
    protected Stack\Builder $stackBuilder;
41
    protected RouteCollection $router;
42
43
    /**
44
     * @var callable
45
     */
46
    protected $exceptionDecorator;
47
48 8
    public function __construct(Config $configuration)
49
    {
50 8
        $this->configuration = $configuration;
51 8
        $this->container = $configuration->getContainer();
52 8
        $this->router = $configuration->getRouter();
53 8
        $this->emitter = $configuration->getEventEmitter();
54
55 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. Annotations will be autoloaded in 2.0. ( Ignorable by Annotation )

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

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