Application::handleException()   B
last analyzed

Complexity

Conditions 5
Paths 16

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.7629

Importance

Changes 0
Metric Value
cc 5
eloc 15
nc 16
nop 1
dl 0
loc 27
rs 8.439
c 0
b 0
f 0
ccs 11
cts 16
cp 0.6875
crap 5.7629
1
<?php
2
/**
3
 * @author    jan huang <[email protected]>
4
 * @copyright 2016
5
 *
6
 * @see      https://www.github.com/janhuang
7
 * @see      https://fastdlabs.com
8
 */
9
10
namespace FastD;
11
12
use ErrorException;
13
use Exception;
14
use FastD\Config\Config;
15
use FastD\Container\Container;
16
use FastD\Container\ServiceProviderInterface;
17
use FastD\Http\HttpException;
18
use FastD\Http\Response;
19
use FastD\Http\ServerRequest;
20
use FastD\Logger\Logger;
21
use FastD\ServiceProvider\ConfigServiceProvider;
22
use FastD\Servitization\Client\Client;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, FastD\Client.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
23
use Psr\Http\Message\ResponseInterface;
24
use Psr\Http\Message\ServerRequestInterface;
25
use Symfony\Component\Debug\Exception\FatalThrowableError;
26
use Throwable;
27
28
/**
29
 * Class Application.
30
 */
31
class Application extends Container
32
{
33
    const VERSION = 'v3.2.0';
34
35
    /**
36
     * @var Application
37
     */
38
    public static $app;
39
40
    /**
41
     * @var string
42
     */
43
    protected $path;
44
45
    /**
46
     * @var string
47
     */
48
    protected $name;
49
50
    /**
51
     * @var bool
52
     */
53
    protected $booted = false;
54
55
    /**
56
     * AppKernel constructor.
57
     *
58
     * @param $path
59
     */
60 32
    public function __construct($path)
61
    {
62 32
        $this->path = $path;
63
64 32
        static::$app = $this;
65
66 32
        $this->add('app', $this);
67
68 32
        $this->bootstrap();
69 32
    }
70
71
    /**
72
     * @return string
73
     */
74 32
    public function getName()
75
    {
76 32
        return $this->name;
77
    }
78
79
    /**
80
     * @return bool
81
     */
82 1
    public function isBooted()
83
    {
84 1
        return $this->booted;
85
    }
86
87
    /**
88
     * @return string
89
     */
90 32
    public function getPath()
91
    {
92 32
        return $this->path;
93
    }
94
95
    /**
96
     * Application bootstrap.
97
     */
98 32
    public function bootstrap()
99
    {
100 32
        if (!$this->booted) {
101 32
            $config = load($this->path.'/config/app.php');
102
103 32
            $this->name = $config['name'];
104
105 32
            $this->add('config', new Config($config));
106 32
            $this->add('logger', new Logger($this->name));
107 32
            $this->add('client', new Client());
108
109 32
            $this->registerExceptionHandler();
110 32
            $this->registerServicesProviders($config['services']);
111 32
            unset($config);
112 32
            $this->booted = true;
113 32
        }
114 32
    }
115
116 32
    protected function registerExceptionHandler()
117
    {
118 32
        error_reporting(-1);
119
120 32
        set_exception_handler([$this, 'handleException']);
121
122 32
        set_error_handler(function ($level, $message, $file = '', $line = 0) {
123
            throw new ErrorException($message, 0, $level, $file, $line);
124 32
        });
125 32
    }
126
127
    /**
128
     * @param ServiceProviderInterface[] $services
129
     */
130 32
    protected function registerServicesProviders(array $services)
131
    {
132 32
        $this->register(new ConfigServiceProvider());
133 32
        foreach ($services as $service) {
134 32
            $this->register(new $service());
135 32
        }
136 32
    }
137
138
    /**
139
     * @param ServerRequestInterface $request
140
     *
141
     * @return Response|\Symfony\Component\HttpFoundation\Response
142
     */
143 8
    public function handleRequest(ServerRequestInterface $request)
144
    {
145 8
        $this->add('request', $request);
146
147
        try {
148 8
            $response = $this->get('dispatcher')->dispatch($request);
149 7
            logger()->log(Logger::INFO, $response->getStatusCode(), [
150 7
                'method' => $request->getMethod(),
151 7
                'path' => $request->getUri()->getPath(),
152 7
            ]);
153 8
        } catch (Exception $exception) {
154 2
            $response = $this->handleException($exception);
155 2
        } catch (Throwable $exception) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
156
            $exception = new FatalThrowableError($exception);
157
            $response = $this->handleException($exception);
158
        }
159
160 8
        $this->add('response', $response);
161
162 8
        return $response;
163
    }
164
165
    /**
166
     * @param Response|\Symfony\Component\HttpFoundation\Response $response
167
     */
168 2
    public function handleResponse($response)
169
    {
170 2
        $response->send();
171 2
    }
172
173
    /**
174
     * @param $e
175
     *
176
     * @return Response
177
     */
178 3
    public function handleException($e)
179
    {
180 3
        if (!$e instanceof Exception) {
181
            $e = new FatalThrowableError($e);
182
        }
183
184
        try {
185 3
            $trace = call_user_func(config()->get('exception.log'), $e);
186 3
        } catch (Exception $exception) {
187
            $trace = [
188
                'original' => explode("\n", $e->getTraceAsString()),
189
                'handler' => explode("\n", $exception->getTraceAsString()),
190
            ];
191
        }
192
193 3
        $this->add('exception', $e);
194
195 3
        logger()->log(Logger::ERROR, $e->getMessage(), $trace);
196
197 3
        $statusCode = ($e instanceof HttpException) ? $e->getStatusCode() : $e->getCode();
198
199 3
        if (!array_key_exists($statusCode, Response::$statusTexts)) {
200 1
            $statusCode = Response::HTTP_INTERNAL_SERVER_ERROR;
201 1
        }
202
203 3
        return json(call_user_func(config()->get('exception.response'), $e), $statusCode);
204
    }
205
206
    /**
207
     * Started application.
208
     *
209
     * @return int
210
     */
211 1
    public function run()
212
    {
213
        $request = ServerRequest::createServerRequestFromGlobals();
214
215
        $response = $this->handleRequest($request);
216
217
        $this->handleResponse($response);
218
219
        return $this->shutdown($request, $response);
220 1
    }
221
222
    /**
223
     * @param ServerRequestInterface                                       $request
224
     * @param ResponseInterface|\Symfony\Component\HttpFoundation\Response $response
225
     *
226
     * @return int
227
     */
228 2
    public function shutdown(ServerRequestInterface $request, $response)
229
    {
230 2
        $this->offsetUnset('request');
231 2
        $this->offsetUnset('response');
232 2
        if ($this->offsetExists('exception')) {
233 1
            $this->offsetUnset('exception');
234 1
        }
235 2
        unset($request, $response);
236
237 2
        return 0;
238
    }
239
}
240