Passed
Push — master ( c76517...75329d )
by Runner
02:47
created

Application::handleRequest()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3.1406

Importance

Changes 0
Metric Value
cc 3
eloc 13
nc 3
nop 1
dl 0
loc 19
ccs 9
cts 12
cp 0.75
crap 3.1406
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author    jan huang <[email protected]>
4
 * @copyright 2016
5
 *
6
 * @see      https://www.github.com/janhuang
7
 * @see      http://www.fast-d.cn/
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
    /**
33
     * The FastD version.
34
     *
35
     * @const string
36
     */
37
    const VERSION = '3.1.0';
38
39
    /**
40
     * @var Application
41
     */
42
    public static $app;
43
44
    /**
45
     * @var string
46
     */
47
    protected $path;
48
49
    /**
50
     * @var string
51
     */
52
    protected $name;
53
54
    /**
55
     * @var bool
56
     */
57
    protected $booted = false;
58
59
    /**
60
     * AppKernel constructor.
61
     *
62
     * @param $path
63
     */
64 61
    public function __construct($path)
65
    {
66 61
        $this->path = $path;
67
68 61
        static::$app = $this;
69
70 61
        $this->add('app', $this);
71
72 61
        $this->bootstrap();
73 61
    }
74
75
    /**
76
     * @return string
77
     */
78 61
    public function getName()
79
    {
80 61
        return $this->name;
81
    }
82
83
    /**
84
     * @return bool
85
     */
86 5
    public function isBooted()
87
    {
88 5
        return $this->booted;
89
    }
90
91
    /**
92
     * @return string
93
     */
94 61
    public function getPath()
95
    {
96 61
        return $this->path;
97
    }
98
99 61
    public function bootstrap()
100
    {
101 61
        if (!$this->booted) {
102 61
            $this->registerExceptionHandler();
103
104 61
            $config = load($this->path.'/config/app.php');
105
106 61
            $this->name = $config['name'];
107
108 61
            $this->add('config', new Config($config));
109 61
            $this->add('logger', new Logger(app()->getName()));
110
111 61
            $this->registerServicesProviders($config['services']);
112 61
            unset($config);
113 61
            $this->booted = true;
114 61
        }
115 61
    }
116
117
    /**
118
     * @param ServiceProviderInterface[] $services
119
     */
120 61
    protected function registerServicesProviders(array $services)
121
    {
122 61
        $this->register(new ConfigServiceProvider());
123 61
        foreach ($services as $service) {
124 61
            $this->register(new $service());
125 61
        }
126 61
    }
127
128 61
    protected function registerExceptionHandler()
129
    {
130 61
        error_reporting(-1);
131
132 61
        set_exception_handler([$this, 'handleException']);
133
134 61
        set_error_handler(function ($level, $message, $file = '', $line = 0, $context = []) {
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
135
            throw new ErrorException($message, 0, $level, $file, $line);
136 61
        });
137 61
    }
138
139
    /**
140
     * @param ServerRequestInterface $request
141
     *
142
     * @return Response
143
     */
144 17
    public function handleRequest(ServerRequestInterface $request)
145
    {
146 17
        $this->add('request', $request);
147
148
        try {
149 17
            $response = $this->get('dispatcher')->dispatch($request);
150 17
        } catch (Exception $exception) {
151 5
            $this->handleException($exception);
152 5
            $response = $this->renderException($exception);
153 5
        } 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...
154
            $exception = new FatalThrowableError($exception);
155
            $this->handleException($exception);
156
            $response = $this->renderException($exception);
157
        }
158
159 17
        $this->add('response', $response);
160
161 17
        return $response;
162
    }
163
164
    /**
165
     * @param Response $response
166
     */
167 5
    public function handleResponse(Response $response)
168
    {
169 5
        $response->send();
170 5
    }
171
172
    /**
173
     * @param $e
174
     */
175 9
    public function handleException($e)
176
    {
177 9
        if (!$e instanceof Exception) {
178
            $e = new FatalThrowableError($e);
179
        }
180
        try {
181 9
            $trace = call_user_func(config()->get('exception.log'), $e);
182 9
        } catch (Exception $exception) {
183
            $trace = [
184
                'original' => explode("\n", $e->getTraceAsString()),
185
                'handler' => explode("\n", $exception->getTraceAsString()),
186
            ];
187
        }
188
189
        /*
190
         * TODO 如果在是在 console, 并且在 bootstrap 中发生异常, 将只会保存日志而没有抛出任何异常
191
         */
192 9
        logger()->log(Logger::ERROR, $e->getMessage(), $trace);
193 9
    }
194
195
    /**
196
     * @param Exception $e
197
     *
198
     * @return Response
199
     */
200 10
    public function renderException(Exception $e)
201
    {
202 10
        $statusCode = ($e instanceof HttpException) ? $e->getStatusCode() : $e->getCode();
203
204 10
        if (!array_key_exists($statusCode, Response::$statusTexts)) {
205 5
            $statusCode = 502;
206 5
        }
207
208 10
        return json(call_user_func(config()->get('exception.response'), $e), $statusCode);
209
    }
210
211
    /**
212
     * @return int
213
     */
214 3
    public function run()
215
    {
216
        $request = ServerRequest::createServerRequestFromGlobals();
217
218
        $response = $this->handleRequest($request);
219
220 3
        $this->handleResponse($response);
221
222
        return $this->shutdown($request, $response);
223
    }
224
225
    /**
226
     * @param ServerRequestInterface $request
227
     * @param ResponseInterface      $response
228
     *
229
     * @return int
230
     */
231 8
    public function shutdown(ServerRequestInterface $request, ResponseInterface $response)
232
    {
233 8
        $this->offsetUnset('request');
234 8
        $this->offsetUnset('response');
235 8
        $this->offsetUnset('exception');
236 8
        unset($request, $response);
237
238 8
        return 0;
239
    }
240
}
241