Passed
Push — master ( 5daeed...b817d9 )
by 世昌
06:58 queued 02:48
created

Application::dumpException()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 2
rs 10
1
<?php
2
3
namespace suda\application;
4
5
use Exception;
6
use Throwable;
7
use ReflectionException;
8
use suda\framework\Request;
9
use suda\framework\Response;
10
use suda\database\exception\SQLException;
11
use suda\framework\route\MatchResult;
12
use suda\application\loader\ModuleLoader;
13
use suda\application\template\RawTemplate;
14
use suda\application\loader\LanguageLoader;
15
use suda\application\template\ModuleTemplate;
16
use suda\application\wrapper\TemplateWrapper;
17
use suda\application\loader\ApplicationLoader;
18
use suda\application\processor\FileRequestProcessor;
19
use suda\application\wrapper\ExceptionContentWrapper;
20
use suda\application\exception\ConfigurationException;
21
use suda\application\processor\TemplateAssetProccesser;
22
use suda\application\processor\TemplateRequestProcessor;
23
24
/**
25
 * 应用程序
26
 */
27
class Application extends ApplicationSource
28
{
29
    /**
30
     * @var DebugDumper
31
     */
32
    protected $dumper;
33
34
    /**
35
     * 准备运行环境
36
     *
37
     * @return void
38
     * @throws SQLException
39
     * @throws ReflectionException
40
     */
41
    public function load()
42
    {
43
        $appLoader = new ApplicationLoader($this);
44
        $this->debug->info('===============================');
45
        $this->debug->time('loading application');
46
        $appLoader->load();
47
        $this->event->exec('application:load-config', [$this->config, $this]);
48
        $this->debug->timeEnd('loading application');
49
        $this->debug->time('loading data-source');
50
        $appLoader->loadDataSource();
51
        $this->debug->timeEnd('loading data-source');
52
        $this->event->exec('application:load-environment', [$this->config, $this]);
53
        $this->debug->time('loading route');
54
        $appLoader->loadRoute();
55
        $this->event->exec('application:load-route', [$this->route, $this]);
56
        $this->debug->timeEnd('loading route');
57
        $this->debug->info('-------------------------------');
58
    }
59
60
    /**
61
     * 准备环境
62
     *
63
     * @param Request $request
64
     * @param Response $response
65
     * @throws SQLException
66
     * @throws ReflectionException
67
     */
68
    protected function prepare(Request $request, Response $response)
69
    {
70
        $response->setHeader('x-powered-by', 'suda/' . SUDA_VERSION, true);
71
        $response->getWrapper()->register(ExceptionContentWrapper::class, [Throwable::class]);
72
        $response->getWrapper()->register(TemplateWrapper::class, [RawTemplate::class]);
73
74
        $this->dumper = new DebugDumper($this, $request, $response);
75
        $this->dumper->register();
76
77
        $this->debug->info('{request-time} {remote-ip} {request-method} {request-uri} debug={debug}', [
78
            'remote-ip' => $request->getRemoteAddr(),
79
            'debug' => SUDA_DEBUG,
80
            'request-uri' => $request->getUrl(),
81
            'request-method' => $request->getMethod(),
82
            'request-time' => date('Y-m-d H:i:s', constant('SUDA_START_TIME')),
83
        ]);
84
85
        if ($this->isPrepared === false) {
86
            $this->load();
87
            $this->isPrepared = true;
88
        }
89
    }
90
91
    /**
92
     * @param Throwable $throwable
93
     */
94
    public function dumpException($throwable) {
95
        $this->dumper->dumpThrowable($throwable);
96
    }
97
98
    /**
99
     * 运行程序
100
     *
101
     * @param Request $request
102
     * @param Response $response
103
     */
104
    public function run(Request $request, Response $response)
105
    {
106
        try {
107
            $this->prepare($request, $response);
108
            $this->debug->time('match route');
109
            $result = $this->route->match($request);
110
            $this->debug->timeEnd('match route');
111
            if ($result !== null) {
112
                $this->event->exec('application:route:match::after', [$result, $request]);
113
            }
114
            $this->debug->time('sending response');
115
            $response = $this->createResponse($result, $request, $response);
116
            if (!$response->isSend()) {
117
                $response->end();
118
            }
119
            $this->debug->info('responded with code ' . $response->getStatus());
120
            $this->debug->timeEnd('sending response');
121
        } catch (Throwable $e) {
122
            $this->debug->uncaughtException($e);
123
            $this->dumper->dumpThrowable($e);
124
            $response->sendContent($e);
125
            $response->end();
126
            $this->debug->timeEnd('sending response');
127
        }
128
        $this->debug->info('system shutdown');
129
    }
130
131
    /**
132
     * 添加请求
133
     *
134
     * @param array $method
135
     * @param string $name
136
     * @param string $url
137
     * @param array $attributes
138
     * @return void
139
     */
140
    public function request(array $method, string $name, string $url, array $attributes = [])
141
    {
142
        $route = $attributes['config'] ?? [];
143
        $runnable = null;
144
        if (array_key_exists('class', $route)) {
145
            $runnable = $this->className($route['class']) . '->onRequest';
146
        } elseif (array_key_exists('source', $route)) {
147
            $attributes['source'] = $route['source'];
148
            $runnable = FileRequestProcessor::class . '->onRequest';
149
        } elseif (array_key_exists('template', $route)) {
150
            $attributes['template'] = $route['template'];
151
            $runnable = TemplateRequestProcessor::class . '->onRequest';
152
        } elseif (array_key_exists('runnable', $route)) {
153
            $runnable = $route['runnable'];
154
        } else {
155
            throw new ConfigurationException('request config error', ConfigurationException::ERR_CONFIG_SET);
156
        }
157
        $this->route->request($method, $name, $url, $runnable, $attributes);
158
    }
159
160
    /**
161
     * 运行默认请求
162
     * @param Application $application
163
     * @param Request $request
164
     * @param Response $response
165
     * @return mixed|void
166
     * @throws Exception
167
     */
168
    protected function defaultResponse(Application $application, Request $request, Response $response)
169
    {
170
        if ((new TemplateAssetProccesser)->onRequest($application, $request, $response)) {
171
            return;
172
        }
173
        return $this->route->getDefaultRunnable()->run($request, $response);
174
    }
175
176
    /**
177
     * 运行请求
178
     * @param MatchResult|null $result
179
     * @param Request $request
180
     * @param Response $response
181
     * @return Response
182
     * @throws Exception
183
     */
184
    protected function createResponse(?MatchResult $result, Request $request, Response $response)
185
    {
186
        if (SUDA_DEBUG) {
187
            $response->setHeader('x-route', $result === null ? 'default' : $result->getName());
188
        }
189
        if ($result === null) {
190
            $content = $this->defaultResponse($this, $request, $response);
191
        } else {
192
            $content = $this->runResult($result, $request, $response);
193
        }
194
        if ($content !== null && !$response->isSend()) {
195
            $response->setContent($content);
196
        }
197
        return $response;
198
    }
199
200
    /**
201
     * 运行结果
202
     *
203
     * @param MatchResult $result
204
     * @param Request $request
205
     * @param Response $response
206
     * @return mixed
207
     * @throws ReflectionException
208
     */
209
    protected function runResult(MatchResult $result, Request $request, Response $response)
210
    {
211
        $request->mergeQueries($result->getParameter())->setAttributes($result->getMatcher()->getAttribute());
212
        $request->setAttribute('result', $result);
213
        $module = $request->getAttribute('module');
214
        if ($module && ($running = $this->find($module))) {
215
            $moduleLoader = new ModuleLoader($this, $running);
216
            $moduleLoader->toRunning();
217
        }
218
        LanguageLoader::load($this);
219
        return ($result->getRunnable())($this, $request, $response);
220
    }
221
222
    /**
223
     * 获取模板页面
224
     *
225
     * @param string $name
226
     * @param Request $request
227
     * @param string|null $default
228
     * @return ModuleTemplate
229
     */
230
    public function getTemplate(string $name, Request $request, ?string $default = null): ModuleTemplate
231
    {
232
        if ($default === null && $this->running !== null) {
233
            $default = $this->running->getFullName();
234
        } else {
235
            $default = $default ?? $request->getAttribute('module');
236
        }
237
        return new ModuleTemplate($this->getModuleSourceName($name, $default), $this, $request, $default);
238
    }
239
}
240