Completed
Push — 6.0 ( 384317...0d8b86 )
by liu
05:34
created

Http::runWithRequest()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 26
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.25

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 8
nop 1
dl 0
loc 26
ccs 9
cts 12
cp 0.75
crap 4.25
rs 9.8666
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: liu21st <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think;
14
15
use Closure;
16
use think\event\RouteLoaded;
17
use think\exception\Handle;
18
use think\exception\HttpException;
19
use Throwable;
20
21
/**
22
 * Web应用管理类
23
 */
24
class Http
25
{
26
27
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
28
     * @var App
29
     */
30
    protected $app;
31
32
    /**
33
     * 应用路径
34
     * @var string
35
     */
36
    protected $path;
37
38
    /**
39
     * 是否多应用模式
40
     * @var bool
41
     */
42
    protected $multi = false;
43
44
    /**
45
     * 是否域名绑定应用
46
     * @var bool
47
     */
48
    protected $bindDomain = false;
49
50
    /**
51
     * 应用名称
52
     * @var string
53
     */
54
    protected $name;
55
56 9
    public function __construct(App $app)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
57
    {
58 9
        $this->app   = $app;
59 9
        $this->multi = is_dir($this->app->getBasePath() . 'controller') ? false : true;
60 9
    }
61
62
    /**
63
     * 是否域名绑定应用
64
     * @access public
65
     * @return bool
66
     */
67
    public function isBindDomain(): bool
68
    {
69
        return $this->bindDomain;
70
    }
71
72
    /**
73
     * 设置应用模式
74
     * @access public
75
     * @param bool $multi
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
76
     * @return $this
77
     */
78 1
    public function multi(bool $multi)
79
    {
80 1
        $this->multi = $multi;
81 1
        return $this;
82
    }
83
84
    /**
85
     * 是否为多应用模式
86
     * @access public
87
     * @return bool
88
     */
89 6
    public function isMulti(): bool
90
    {
91 6
        return $this->multi;
92
    }
93
94
    /**
95
     * 设置应用名称
96
     * @access public
97
     * @param string $name 应用名称
98
     * @return $this
99
     */
100 1
    public function name(string $name)
101
    {
102 1
        $this->name = $name;
103 1
        return $this;
104
    }
105
106
    /**
107
     * 获取应用名称
108
     * @access public
109
     * @return string
110
     */
111
    public function getName(): string
112
    {
113
        return $this->name ?: '';
114
    }
115
116
    /**
117
     * 设置应用目录
118
     * @access public
119
     * @param string $path 应用目录
120
     * @return $this
121
     */
122 1
    public function path(string $path)
123
    {
124 1
        if (substr($path, -1) != DIRECTORY_SEPARATOR) {
125 1
            $path .= DIRECTORY_SEPARATOR;
126
        }
127
128 1
        $this->path = $path;
129 1
        return $this;
130
    }
131
132
    /**
133
     * 执行应用程序
134
     * @access public
135
     * @param Request|null $request
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
136
     * @return Response
137
     */
138 8
    public function run(Request $request = null): Response
139
    {
140
        //自动创建request对象
141 8
        $request = $request ?? $this->app->make('request', [], true);
142 8
        $this->app->instance('request', $request);
143
144
        try {
145 8
            $response = $this->runWithRequest($request);
146 8
        } catch (Throwable $e) {
147 8
            $this->reportException($e);
148
149 2
            $response = $this->renderException($request, $e);
150
        }
151
152 2
        return $response->setCookie($this->app->cookie);
153
    }
154
155
    /**
156
     * 初始化
157
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
158 7
    protected function initialize()
159
    {
160 7
        if (!$this->app->initialized()) {
161 7
            $this->app->initialize();
162
        }
163 7
    }
164
165
    /**
166
     * 执行应用程序
167
     * @param Request $request
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
168
     * @return mixed
169
     */
170 7
    protected function runWithRequest(Request $request)
171
    {
172 7
        $this->initialize();
173
174
        // 加载全局中间件
175 7
        if (is_file($this->app->getBasePath() . 'middleware.php')) {
176 7
            $this->app->middleware->import(include $this->app->getBasePath() . 'middleware.php');
177
        }
178
179 7
        if ($this->multi) {
180 6
            $this->parseMultiApp();
181
        }
182
183
        // 设置开启事件机制
184 6
        $this->app->event->withEvent($this->app->config->get('app.with_event', true));
0 ignored issues
show
Bug introduced by
It seems like $this->app->config->get('app.with_event', true) can also be of type array; however, parameter $event of think\Event::withEvent() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

184
        $this->app->event->withEvent(/** @scrutinizer ignore-type */ $this->app->config->get('app.with_event', true));
Loading history...
185
186
        // 监听HttpRun
187 6
        $this->app->event->trigger('HttpRun');
188
189 6
        $this->app->log->info($request->ip() . ' ' . $request->method() . ' ' . $request->host() . $request->url());
190
191
        $withRoute = $this->app->config->get('app.with_route', true) ? function () {
192
            $this->loadRoutes();
193
        } : null;
194
195
        return $this->app->route->dispatch($request, $withRoute);
196
    }
197
198
    /**
199
     * 加载路由
200
     * @access protected
201
     * @return void
202
     */
203
    protected function loadRoutes(): void
204
    {
205
        // 加载路由定义
206
        if (is_dir($this->getRoutePath())) {
207
            $files = glob($this->getRoutePath() . '*.php');
208
            foreach ($files as $file) {
209
                include $file;
210
            }
211
        }
212
213
        $this->app->event->trigger(RouteLoaded::class);
214
    }
215
216
    /**
217
     * 获取路由目录
218
     * @access protected
219
     * @return string
220
     */
221
    protected function getRoutePath(): string
222
    {
223
        return $this->app->getRootPath() . 'route' . DIRECTORY_SEPARATOR . ($this->isMulti() ? $this->getName() . DIRECTORY_SEPARATOR : '');
224
    }
225
226
    /**
227
     * Report the exception to the exception handler.
228
     *
229
     * @param Throwable $e
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
230
     * @return void
231
     */
232 7
    protected function reportException(Throwable $e)
233
    {
234 7
        $this->app->make(Handle::class)->report($e);
235 1
    }
236
237
    /**
238
     * Render the exception to a response.
239
     *
240
     * @param Request   $request
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
241
     * @param Throwable $e
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
242
     * @return Response
243
     */
244 1
    protected function renderException($request, Throwable $e)
245
    {
246 1
        return $this->app->make(Handle::class)->render($request, $e);
247
    }
248
249
    /**
250
     * 获取当前运行入口名称
251
     * @access protected
252
     * @codeCoverageIgnore
253
     * @return string
254
     */
255
    protected function getScriptName(): string
256
    {
257
        if (isset($_SERVER['SCRIPT_FILENAME'])) {
258
            $file = $_SERVER['SCRIPT_FILENAME'];
259
        } elseif (isset($_SERVER['argv'][0])) {
260
            $file = realpath($_SERVER['argv'][0]);
261
        }
262
263
        return isset($file) ? pathinfo($file, PATHINFO_FILENAME) : '';
264
    }
265
266
    /**
267
     * 解析多应用
268
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
269 6
    protected function parseMultiApp(): void
270
    {
271 6
        if ($this->app->config->get('app.auto_multi_app', false)) {
272
            // 自动多应用识别
273 5
            $this->bindDomain = false;
274
275 5
            $bind = $this->app->config->get('app.domain_bind', []);
276
277 5
            if (!empty($bind)) {
278
                // 获取当前子域名
279 5
                $subDomain = $this->app->request->subDomain();
280 5
                $domain    = $this->app->request->host(true);
281
282 5
                if (isset($bind[$domain])) {
283 1
                    $appName          = $bind[$domain];
284 1
                    $this->bindDomain = true;
285 4
                } elseif (isset($bind[$subDomain])) {
286 1
                    $appName          = $bind[$subDomain];
287 1
                    $this->bindDomain = true;
288 3
                } elseif (isset($bind['*'])) {
289
                    $appName          = $bind['*'];
290
                    $this->bindDomain = true;
291
                }
292
            }
293
294 5
            if (!$this->bindDomain) {
295 3
                $map  = $this->app->config->get('app.app_map', []);
296 3
                $deny = $this->app->config->get('app.deny_app_list', []);
297 3
                $path = $this->app->request->pathinfo();
298 3
                $name = current(explode('/', $path));
299
300 3
                if (isset($map[$name])) {
301 1
                    if ($map[$name] instanceof Closure) {
302
                        $result  = call_user_func_array($map[$name], [$this]);
303
                        $appName = $result ?: $name;
304
                    } else {
305 1
                        $appName = $map[$name];
306
                    }
307 2
                } elseif ($name && (false !== array_search($name, $map) || in_array($name, $deny))) {
308 1
                    throw new HttpException(404, 'app not exists:' . $name);
309 1
                } elseif ($name && isset($map['*'])) {
310
                    $appName = $map['*'];
311
                } else {
312 1
                    $appName = $name;
313
                }
314
315 2
                if ($name) {
316 2
                    $this->app->request->setRoot('/' . $name);
317 4
                    $this->app->request->setPathinfo(strpos($path, '/') ? ltrim(strstr($path, '/'), '/') : '');
318
                }
319
            }
320
        } else {
321 1
            $appName = $this->name ?: $this->getScriptName();
322
        }
323
324 5
        $this->loadApp($appName ?: $this->app->config->get('app.default_app', 'index'));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $appName does not seem to be defined for all execution paths leading up to this point.
Loading history...
Bug introduced by
It seems like $appName ?: $this->app->....default_app', 'index') can also be of type array; however, parameter $appName of think\Http::loadApp() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

324
        $this->loadApp(/** @scrutinizer ignore-type */ $appName ?: $this->app->config->get('app.default_app', 'index'));
Loading history...
325 5
    }
326
327
    /**
328
     * 加载应用文件
329
     * @param string $appName 应用名
330
     * @return void
331
     */
332 5
    protected function loadApp(string $appName): void
333
    {
334 5
        $this->name = $appName;
335 5
        $this->app->request->setApp($appName);
336 5
        $this->app->setAppPath($this->path ?: $this->app->getBasePath() . $appName . DIRECTORY_SEPARATOR);
337 5
        $this->app->setRuntimePath($this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . $appName . DIRECTORY_SEPARATOR);
338
339
        //加载app文件
340 5
        if (is_dir($this->app->getAppPath())) {
341 1
            $appPath = $this->app->getAppPath();
342
343 1
            if (is_file($appPath . 'common.php')) {
344 1
                include_once $appPath . 'common.php';
345
            }
346
347 1
            $configPath = $this->app->getConfigPath();
348
349 1
            $files = [];
350
351 1
            if (is_dir($configPath . $appName)) {
352 1
                $files = array_merge($files, glob($configPath . $appName . DIRECTORY_SEPARATOR . '*' . $this->app->getConfigExt()));
0 ignored issues
show
Bug introduced by
It seems like glob($configPath . $appN...s->app->getConfigExt()) can also be of type false; however, parameter $array2 of array_merge() does only seem to accept array|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

352
                $files = array_merge($files, /** @scrutinizer ignore-type */ glob($configPath . $appName . DIRECTORY_SEPARATOR . '*' . $this->app->getConfigExt()));
Loading history...
353
            } elseif (is_dir($appPath . 'config')) {
354
                $files = array_merge($files, glob($appPath . 'config' . DIRECTORY_SEPARATOR . '*' . $this->app->getConfigExt()));
355
            }
356
357 1
            foreach ($files as $file) {
358
                $this->app->config->load($file, pathinfo($file, PATHINFO_FILENAME));
359
            }
360
361 1
            if (is_file($appPath . 'event.php')) {
362 1
                $this->app->loadEvent(include $appPath . 'event.php');
363
            }
364
365 1
            if (is_file($appPath . 'middleware.php')) {
366 1
                $this->app->middleware->import(include $appPath . 'middleware.php');
367
            }
368
369 1
            if (is_file($appPath . 'provider.php')) {
370 1
                $this->app->bind(include $appPath . 'provider.php');
371
            }
372
        }
373
374
        // 加载应用默认语言包
375 5
        $this->app->loadLangPack($this->app->lang->defaultLangSet());
376
377
        // 设置应用命名空间
378 5
        $this->app->setNamespace($this->app->config->get('app.app_namespace') ?: 'app\\' . $appName);
379 5
    }
380
381
    /**
382
     * HttpEnd
383
     * @param Response $response
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
384
     * @return void
385
     */
386 1
    public function end(Response $response): void
387
    {
388 1
        $this->app->event->trigger('HttpEnd', $response);
389
390
        // 写入日志
391 1
        $this->app->log->save();
392
        // 写入Session
393 1
        $this->app->session->save();
394 1
    }
395
396
    public function __debugInfo()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __debugInfo()
Loading history...
397
    {
398
        return [
399
            'path'       => $this->path,
400
            'multi'      => $this->multi,
401
            'bindDomain' => $this->bindDomain,
402
            'name'       => $this->name,
403
        ];
404
    }
405
}
406