Passed
Push — master ( e71b36...8a2cc4 )
by 世昌
02:31
created

Application::__clone()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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