Passed
Push — master ( 72c61b...6af077 )
by 世昌
07:29
created

Application::prepare()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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