Test Failed
Push — dev ( e79643...3785bc )
by 世昌
04:25
created

Application::run()   A

Complexity

Conditions 4
Paths 29

Size

Total Lines 31
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 25
nc 29
nop 2
dl 0
loc 31
rs 9.52
c 0
b 0
f 0
1
<?php
2
3
namespace suda\application;
4
5
use Exception;
6
use Throwable;
7
use suda\framework\Request;
8
use suda\framework\Response;
9
use suda\framework\http\Status;
10
use suda\framework\loader\Loader;
11
use suda\framework\route\MatchResult;
12
use suda\application\template\Template;
13
use suda\application\debug\RequestDumper;
14
use suda\application\loader\ModuleLoader;
15
use suda\database\exception\SQLException;
16
use suda\application\template\RawTemplate;
17
use suda\application\debug\ExceptionCatcher;
18
use suda\application\wrapper\TemplateWrapper;
19
use suda\application\loader\ApplicationLoader;
20
use suda\application\processor\FileRequestProcessor;
21
use suda\framework\http\Request as RequestInterface;
22
use suda\application\wrapper\ExceptionContentWrapper;
23
use suda\application\exception\ConfigurationException;
24
use suda\framework\http\Response as ResponseInterface;
25
use suda\application\processor\TemplateAssetProcessor;
26
use suda\application\processor\TemplateRequestProcessor;
27
use suda\application\processor\RunnableRequestProcessor;
28
29
/**
30
 * 应用程序
31
 */
32
class Application extends ApplicationRoute
33
{
34
    /**
35
     * @var RequestDumper
36
     */
37
    protected $catcher;
38
39
    /**
40
     * 准备运行环境
41
     *
42
     * @return void
43
     * @throws SQLException
44
     */
45
    public function load()
46
    {
47
        $this->debug->info('===============================');
48
        parent::load();
49
        $appLoader = new ApplicationLoader($this);
50
        $this->debug->time('loading route');
51
        $appLoader->loadRoute();
52
        $this->event->exec('application:load-route', [$this->route, $this]);
53
        $route = $this->debug->timeEnd('loading route');
54
        $this->debug->recordTiming('route', $route);
55
        $this->debug->info('-------------------------------');
56
    }
57
58
    /**
59
     * 准备环境
60
     *
61
     * @param Request $request
62
     * @param Response $response
63
     * @throws SQLException
64
     */
65
    protected function prepare(Request $request, Response $response)
66
    {
67
        $response->setHeader('x-powered-by', 'suda/' . SUDA_VERSION, true);
68
        $response->getWrapper()->register(ExceptionContentWrapper::class, [Throwable::class]);
69
        $response->getWrapper()->register(TemplateWrapper::class, [RawTemplate::class]);
70
        $this->setCatcher(new RequestDumper($this, $request, $response));
71
72
        $this->debug->info('{request-time} {remote-ip} {request-method} {request-uri} debug={debug}', [
73
            'remote-ip' => $request->getRemoteAddr(),
74
            'debug' => SUDA_DEBUG,
75
            'request-uri' => $request->getUrl(),
76
            'request-method' => $request->getMethod(),
77
            'request-time' => date('Y-m-d H:i:s', constant('SUDA_START_TIME')),
78
        ]);
79
80
        $this->load();
81
    }
82
83
    /**
84
     * @param ExceptionCatcher $catcher
85
     */
86
    public function setCatcher(ExceptionCatcher $catcher) {
87
        ExceptionCatcher::restore();
88
        $this->catcher = $catcher;
0 ignored issues
show
Documentation Bug introduced by
$catcher is of type suda\application\debug\ExceptionCatcher, but the property $catcher was declared to be of type suda\application\debug\RequestDumper. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
89
        $this->catcher->register();
90
    }
91
92
    /**
93
     * @param Throwable $throwable
94
     */
95
    public function dumpException($throwable)
96
    {
97
        $this->catcher->dumpThrowable($throwable);
98
    }
99
100
    /**
101
     * 运行程序
102
     *
103
     * @param RequestInterface $request
104
     * @param ResponseInterface $response
105
     */
106
    public function run(RequestInterface $request, ResponseInterface $response)
107
    {
108
        $appRequest = new Request($request);
109
        $appResponse = new Response($response, $this);
110
111
        try {
112
            $this->debug->time('init');
113
            $this->prepare($appRequest, $appResponse);
114
            $init = $this->debug->timeEnd('init');
115
            $this->debug->recordTiming('init', $init, 'init total');
116
            $this->debug->time('match route');
117
            $result = $this->route->match($appRequest->getMethod(), $appRequest->getUri());
118
            $match = $this->debug->timeEnd('match route');
119
            $this->debug->recordTiming('dispatch', $match);
120
            if ($result !== null) {
121
                $this->event->exec('application:route:match::after', [$result, $appRequest]);
122
            }
123
            $this->debug->time('sending response');
124
            $appResponse = $this->createResponse($result, $appRequest, $appResponse);
125
            if (!$appResponse->isSend()) {
126
                $appResponse->end();
127
            }
128
            $this->debug->info('responded with code ' . $appResponse->getStatus());
129
            $this->debug->timeEnd('sending response');
130
        } catch (Throwable $e) {
131
            $this->debug->uncaughtException($e);
132
            $this->catcher->dumpThrowable($e);
133
            $appResponse->sendContent($e);
134
            $this->debug->timeEnd('sending response');
135
        }
136
        $this->debug->info('system shutdown');
137
    }
138
139
    /**
140
     * 添加请求
141
     *
142
     * @param array $method
143
     * @param string $name
144
     * @param string $url
145
     * @param array $attributes
146
     * @return void
147
     */
148
    public function request(array $method, string $name, string $url, array $attributes = [])
149
    {
150
        $route = $attributes['config'] ?? [];
151
        $runnable = RunnableRequestProcessor::class . '->onRequest';
152
        if (array_key_exists('class', $route)) {
153
            $attributes['class'] = $route['class'];
154
        } elseif (array_key_exists('source', $route)) {
155
            $attributes['class'] = FileRequestProcessor::class;
156
            $attributes['source'] = $route['source'];
157
        } elseif (array_key_exists('template', $route)) {
158
            $attributes['class'] = TemplateRequestProcessor::class;
159
            $attributes['template'] = $route['template'];
160
        } elseif (array_key_exists('runnable', $route)) {
161
            $attributes['runnable'] = $route['runnable'];
162
        } else {
163
            throw new ConfigurationException('request config error', ConfigurationException::ERR_CONFIG_SET);
164
        }
165
        $this->route->request($method, $name, $url, $runnable, $attributes);
166
    }
167
168
    /**
169
     * 运行默认请求
170
     * @param Application $application
171
     * @param Request $request
172
     * @param Response $response
173
     * @return mixed
174
     * @throws Exception
175
     */
176
    protected function defaultResponse(Application $application, Request $request, Response $response)
177
    {
178
        (new TemplateAssetProcessor)->onRequest($application, $request, $response);
179
        if ($response->getStatus() === Status::HTTP_NOT_FOUND) {
180
            return $this->route->getDefaultRunnable()->run($request, $response);
181
        }
182
        return null;
183
    }
184
185
    /**
186
     * 运行请求
187
     * @param MatchResult|null $result
188
     * @param Request $request
189
     * @param Response $response
190
     * @return Response
191
     * @throws Exception
192
     */
193
    protected function createResponse(?MatchResult $result, Request $request, Response $response)
194
    {
195
        if (SUDA_DEBUG) {
196
            $response->setHeader('x-route', $result === null ? 'default' : $result->getName());
197
        }
198
        if ($result === null) {
199
            $response->status(Status::HTTP_NOT_FOUND);
200
            $content = $this->defaultResponse($this, $request, $response);
201
        } else {
202
            $response->status(Status::HTTP_OK);
203
            $content = $this->runResult($result, $request, $response);
204
        }
205
        if ($content instanceof  Response) {
206
            return $response;
207
        }
208
        if ($content !== null && !$response->isSend()) {
209
            $response->setContent($content);
210
        }
211
        return $response;
212
    }
213
214
    /**
215
     * 运行结果
216
     *
217
     * @param MatchResult $result
218
     * @param Request $request
219
     * @param Response $response
220
     * @return mixed
221
     */
222
    protected function runResult(MatchResult $result, Request $request, Response $response)
223
    {
224
        $request
225
            ->setParameter($request->getParameter())
226
            ->mergeQueries($result->getParameter())
227
            ->setAttributes($result->getMatcher()->getAttribute());
228
        $request->setAttribute('result', $result);
229
        $module = $request->getAttribute('module');
230
        if ($module && ($running = $this->find($module))) {
231
            $moduleLoader = new ModuleLoader($this, $running);
232
            $moduleLoader->toRunning();
233
        }
234
        return ($result->getRunnable())($this, $request, $response);
235
    }
236
237
    /**
238
     * 获取模板页面
239
     *
240
     * @param string $name
241
     * @param Request $request
242
     * @param string|null $default
243
     * @return Template
244
     */
245
    public function getTemplate(string $name, Request $request, ?string $default = null): Template
246
    {
247
        if ($default === null && $this->running !== null) {
248
            $default = $this->running->getFullName();
249
        } else {
250
            $default = $default ?? $request->getAttribute('module');
251
        }
252
        return new Template($this->getModuleSourceName($name, $default), $this, $request, $default);
253
    }
254
255
    /**
256
     * @inheritDoc
257
     */
258
    public function __clone()
259
    {
260
        $this->config = clone $this->config;
261
        $this->event = clone $this->event;
262
        $this->loader = clone $this->loader;
263
    }
264
}
265