Passed
Push — master ( 6ce97f...1fe2f9 )
by huang
05:13
created

Application::handleException()   B

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