Completed
Push — 6.0 ( 487697...a3b118 )
by yun
08:06
created

Http::loadRoutes()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 13
ccs 6
cts 7
cp 0.8571
crap 3.0261
rs 10
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
 * @package think
0 ignored issues
show
Coding Style introduced by
Package name "think" is not valid; consider "Think" instead
Loading history...
24
 */
25
class Http
26
{
27
28
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
29
     * @var App
30
     */
31
    protected $app;
32
33
    /**
34
     * 应用路径
35
     * @var string
36
     */
37
    protected $path;
38
39
    /**
40
     * 是否多应用模式
41
     * @var bool
42
     */
43
    protected $multi = false;
44
45
    /**
46
     * 是否域名绑定应用
47
     * @var bool
48
     */
49
    protected $bindDomain = false;
50
51
    /**
52
     * 应用名称
53
     * @var string
54
     */
55
    protected $name;
56
57 9
    public function __construct(App $app)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
58
    {
59 9
        $this->app   = $app;
60 9
        $this->multi = is_dir($this->app->getBasePath() . 'controller') ? false : true;
61 9
    }
62
63
    /**
64
     * 是否域名绑定应用
65
     * @access public
66
     * @return bool
67
     */
68 2
    public function isBindDomain(): bool
69
    {
70 2
        return $this->bindDomain;
71
    }
72
73
    /**
74
     * 设置应用模式
75
     * @access public
76
     * @param bool $multi
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
77
     * @return $this
78
     */
79 7
    public function multi(bool $multi)
80
    {
81 7
        $this->multi = $multi;
82 7
        return $this;
83
    }
84
85
    /**
86
     * 是否为多应用模式
87
     * @access public
88
     * @return bool
89
     */
90 7
    public function isMulti(): bool
91
    {
92 7
        return $this->multi;
93
    }
94
95
    /**
96
     * 设置应用名称
97
     * @access public
98
     * @param string $name 应用名称
99
     * @return $this
100
     */
101 1
    public function name(string $name)
102
    {
103 1
        $this->name = $name;
104 1
        return $this;
105
    }
106
107
    /**
108
     * 获取应用名称
109
     * @access public
110
     * @return string
111
     */
112 5
    public function getName(): string
113
    {
114 5
        return $this->name ?: '';
115
    }
116
117
    /**
118
     * 设置应用目录
119
     * @access public
120
     * @param string $path 应用目录
121
     * @return $this
122
     */
123 1
    public function path(string $path)
124
    {
125 1
        if (substr($path, -1) != DIRECTORY_SEPARATOR) {
126 1
            $path .= DIRECTORY_SEPARATOR;
127
        }
128
129 1
        $this->path = $path;
130 1
        return $this;
131
    }
132
133
    /**
134
     * 执行应用程序
135
     * @access public
136
     * @param Request|null $request
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
137
     * @return Response
138
     */
139 8
    public function run(Request $request = null): Response
140
    {
141
        //自动创建request对象
142 8
        $request = $request ?? $this->app->make('request', [], true);
143 8
        $this->app->instance('request', $request);
144
145
        try {
146 8
            $response = $this->runWithRequest($request);
147 2
        } catch (Throwable $e) {
148 2
            $this->reportException($e);
149
150 2
            $response = $this->renderException($request, $e);
151
        }
152
153 8
        return $response;
154
    }
155
156
    /**
157
     * 初始化
158
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
159 7
    protected function initialize()
160
    {
161 7
        if (!$this->app->initialized()) {
162 7
            $this->app->initialize();
163
        }
164 7
    }
165
166
    /**
167
     * 执行应用程序
168
     * @param Request $request
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
169
     * @return mixed
170
     */
171 7
    protected function runWithRequest(Request $request)
172
    {
173 7
        $this->initialize();
174
175
        // 加载全局中间件
176 7
        $this->loadMiddleware();
177
178 7
        $autoMulti = $this->app->config->get('app.auto_multi_app', false);
179
180 7
        if ($this->multi || $autoMulti) {
181 6
            $this->multi(true);
182 6
            $this->parseMultiApp($autoMulti);
183
        }
184
185
        // 设置开启事件机制
186 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

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

347
        $this->setApp(/** @scrutinizer ignore-type */ $appName ?: $this->app->config->get('app.default_app', 'index'));
Loading history...
348 5
    }
349
350
    /**
351
     * 设置应用
352
     * @param string $appName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
353
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
354 5
    protected function setApp(string $appName): void
355
    {
356 5
        $this->name = $appName;
357 5
        $this->app->request->setApp($appName);
358 5
        $this->app->setAppPath($this->path ?: $this->app->getBasePath() . $appName . DIRECTORY_SEPARATOR);
359 5
        $this->app->setRuntimePath($this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . $appName . DIRECTORY_SEPARATOR);
360
361
        // 设置应用命名空间
362 5
        $this->app->setNamespace($this->app->config->get('app.app_namespace') ?: 'app\\' . $appName);
363
364
        //加载应用
365 5
        $this->loadApp($appName);
366 5
    }
367
368
    /**
369
     * 加载应用文件
370
     * @param string $appName 应用名
371
     * @return void
372
     */
373 5
    protected function loadApp(string $appName): void
374
    {
375
        //加载app文件
376 5
        if (is_dir($this->app->getAppPath())) {
377 1
            $appPath = $this->app->getAppPath();
378
379 1
            if (is_file($appPath . 'common.php')) {
380 1
                include_once $appPath . 'common.php';
381
            }
382
383 1
            $configPath = $this->app->getConfigPath();
384
385 1
            $files = [];
386
387 1
            if (is_dir($configPath . $appName)) {
388 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

388
                $files = array_merge($files, /** @scrutinizer ignore-type */ glob($configPath . $appName . DIRECTORY_SEPARATOR . '*' . $this->app->getConfigExt()));
Loading history...
389
            } elseif (is_dir($appPath . 'config')) {
390
                $files = array_merge($files, glob($appPath . 'config' . DIRECTORY_SEPARATOR . '*' . $this->app->getConfigExt()));
391
            }
392
393 1
            foreach ($files as $file) {
394
                $this->app->config->load($file, pathinfo($file, PATHINFO_FILENAME));
395
            }
396
397 1
            if (is_file($appPath . 'event.php')) {
398 1
                $this->app->loadEvent(include $appPath . 'event.php');
399
            }
400
401 1
            if (is_file($appPath . 'middleware.php')) {
402 1
                $this->app->middleware->import(include $appPath . 'middleware.php');
403
            }
404
405 1
            if (is_file($appPath . 'provider.php')) {
406 1
                $this->app->bind(include $appPath . 'provider.php');
407
            }
408
        }
409
410
        // 加载应用默认语言包
411 5
        $this->app->loadLangPack($this->app->lang->defaultLangSet());
412 5
    }
413
414
    /**
415
     * HttpEnd
416
     * @param Response $response
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
417
     * @return void
418
     */
419 1
    public function end(Response $response): void
420
    {
421 1
        $this->app->event->trigger('HttpEnd', $response);
422
423 1
        $response->setCookie($this->app->cookie);
424
425
        // 写入日志
426 1
        $this->app->log->save();
427
        // 写入Session
428 1
        $this->app->session->save();
429 1
    }
430
431
    public function __debugInfo()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __debugInfo()
Loading history...
432
    {
433
        return [
434
            'path'       => $this->path,
435
            'multi'      => $this->multi,
436
            'bindDomain' => $this->bindDomain,
437
            'name'       => $this->name,
438
        ];
439
    }
440
}
441