Completed
Push — 6.0 ( 4bea3d...c65c26 )
by liu
05:50
created

Http::renderException()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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 think\event\HttpEnd;
16
use think\event\HttpRun;
17
use think\event\RouteLoaded;
18
use think\exception\Handle;
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
    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
        $this->multi = is_dir($this->app->getBasePath() . 'controller') ? false : true;
61 9
    }
62 9
63 9
    /**
64
     * 是否域名绑定应用
65
     * @access public
66
     * @return bool
67
     */
68
    public function isBindDomain(): bool
69
    {
70 2
        return $this->bindDomain;
71
    }
72 2
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
    public function multi(bool $multi)
80
    {
81 7
        $this->multi = $multi;
82
        return $this;
83 7
    }
84 7
85
    /**
86
     * 是否为多应用模式
87
     * @access public
88
     * @return bool
89
     */
90
    public function isMulti(): bool
91
    {
92 7
        return $this->multi;
93
    }
94 7
95
    /**
96
     * 设置应用名称
97
     * @access public
98
     * @param string $name 应用名称
99
     * @return $this
100
     */
101
    public function name(string $name)
102
    {
103 1
        $this->name = $name;
104
        return $this;
105 1
    }
106 1
107
    /**
108
     * 获取应用名称
109
     * @access public
110
     * @return string
111
     */
112
    public function getName(): string
113
    {
114 5
        return $this->name ?: '';
115
    }
116 5
117
    /**
118
     * 设置应用目录
119
     * @access public
120
     * @param string $path 应用目录
121
     * @return $this
122
     */
123
    public function path(string $path)
124
    {
125 1
        if (substr($path, -1) != DIRECTORY_SEPARATOR) {
126
            $path .= DIRECTORY_SEPARATOR;
127 1
        }
128 1
129
        $this->path = $path;
130
        return $this;
131 1
    }
132 1
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
    public function run(Request $request = null): Response
140
    {
141 8
        //自动创建request对象
142
        $request = $request ?? $this->app->make('request', [], true);
143
        $this->app->instance('request', $request);
144 8
145 8
        try {
146
            $response = $this->runWithRequest($request);
147
        } catch (Throwable $e) {
148 8
            $this->reportException($e);
149 2
150 2
            $response = $this->renderException($request, $e);
151
        }
152 2
153
        return $response->setCookie($this->app->cookie);
154
    }
155 8
156
    /**
157
     * 初始化
158
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
159
    protected function initialize()
160
    {
161 7
        if (!$this->app->initialized()) {
162
            $this->app->initialize();
163 7
        }
164 7
    }
165
166 7
    /**
167
     * 执行应用程序
168
     * @param Request $request
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
169
     * @return mixed
170
     */
171
    protected function runWithRequest(Request $request)
172
    {
173 7
        $this->initialize();
174
175 7
        // 加载全局中间件
176
        $this->loadMiddleware();
177
178 7
        $autoMulti = $this->app->config->get('app.auto_multi_app', false);
179
180 7
        if ($this->multi || $autoMulti) {
181
            $this->multi(true);
182 7
            $this->parseMultiApp($autoMulti);
183 6
        }
184 6
185
        // 设置开启事件机制
186
        $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 6
        // 监听HttpRun
189
        $this->app->event->trigger(HttpRun::class);
190
191 6
        return $this->dispatchToRoute($request);
192
    }
193 6
194
    protected function dispatchToRoute($request)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function dispatchToRoute()
Loading history...
195
    {
196 6
        $withRoute = $this->app->config->get('app.with_route', true) ? function () {
197
            $this->loadRoutes();
198
        } : null;
199 6
200 6
        return $this->app->route->dispatch($request, $withRoute);
201
    }
202 6
203
    /**
204
     * 加载全局中间件
205
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
206
    protected function loadMiddleware(): void
207
    {
208 7
        if (is_file($this->app->getBasePath() . 'middleware.php')) {
209
            $this->app->middleware->import(include $this->app->getBasePath() . 'middleware.php');
210 7
        }
211 7
    }
212
213 7
    /**
214
     * 加载路由
215
     * @access protected
216
     * @return void
217
     */
218
    protected function loadRoutes(): void
219
    {
220 6
        $routePath = $this->app->getRootPath() . 'route' . DIRECTORY_SEPARATOR;
221
222
        if ($this->name) {
223 6
            // 加载多应用模式的具体应用路由定义
224
            $routeFile = $routePath . $this->name . '.php';
225 6
            if (is_file($routeFile)) {
226 1
                include $routeFile;
227 1
            }
228
        } else {
229
            // 单应用或者未识别的多应用路由定义
230
            $files = glob($routePath . '*.php');
231
            foreach ($files as $file) {
232 6
                if ($this->isMulti()) {
233 6
                    $this->app->route->setAppName(pathinfo($file, PATHINFO_FILENAME));
234
                }
235
                include $file;
236
            }
237
        }
238
239
        $this->app->event->trigger(RouteLoaded::class);
240 6
    }
241
242 6
    /**
243
     * Report the exception to the exception handler.
244
     *
245
     * @param Throwable $e
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
246 6
     * @return void
247
     */
248
    protected function reportException(Throwable $e)
249
    {
250 6
        $this->app->make(Handle::class)->report($e);
251
    }
252
253
    /**
254
     * Render the exception to a response.
255
     *
256
     * @param Request   $request
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
257
     * @param Throwable $e
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
258
     * @return Response
259 1
     */
260
    protected function renderException($request, Throwable $e)
261 1
    {
262 1
        return $this->app->make(Handle::class)->render($request, $e);
263
    }
264
265
    /**
266
     * 获取当前运行入口名称
267
     * @access protected
268
     * @codeCoverageIgnore
269
     * @return string
270
     */
271 1
    protected function getScriptName(): string
272
    {
273 1
        if (isset($_SERVER['SCRIPT_FILENAME'])) {
274
            $file = $_SERVER['SCRIPT_FILENAME'];
275
        } elseif (isset($_SERVER['argv'][0])) {
276
            $file = realpath($_SERVER['argv'][0]);
277
        }
278
279
        return isset($file) ? pathinfo($file, PATHINFO_FILENAME) : '';
280
    }
281
282
    /**
283
     * 解析多应用
284
     * @param bool $autoMulti 自动多应用
285
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
286
    protected function parseMultiApp(bool $autoMulti): void
287
    {
288
        if ($autoMulti) {
289
            // 自动多应用识别
290
            $this->bindDomain = false;
291
            $appName          = null;
292
293
            $bind = $this->app->config->get('app.domain_bind', []);
294
295
            if (!empty($bind)) {
296
                // 获取当前子域名
297 6
                $subDomain = $this->app->request->subDomain();
298
                $domain    = $this->app->request->host(true);
299 6
300
                if (isset($bind[$domain])) {
301 5
                    $appName          = $bind[$domain];
302 5
                    $this->bindDomain = true;
303
                } elseif (isset($bind[$subDomain])) {
304 5
                    $appName          = $bind[$subDomain];
305
                    $this->bindDomain = true;
306 5
                } elseif (isset($bind['*'])) {
307
                    $appName          = $bind['*'];
308 5
                    $this->bindDomain = true;
309 5
                }
310
            }
311 5
312 1
            if (!$this->bindDomain) {
313 1
                return;
314 4
            }
315 1
        } else {
316 1
            $appName = $this->name ?: $this->getScriptName();
317 3
        }
318
319
        $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

319
        $this->setApp(/** @scrutinizer ignore-type */ $appName ?: $this->app->config->get('app.default_app', 'index'));
Loading history...
320
    }
321
322
    /**
323 5
     * 设置应用
324 3
     * @param string $appName
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
325
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
326
    public function setApp(string $appName): void
327
    {
328 3
        $this->name = $appName;
329 3
        $this->app->request->setApp($appName);
330 3
        $this->app->setAppPath($this->path ?: $this->app->getBasePath() . $appName . DIRECTORY_SEPARATOR);
331 3
        $this->app->setRuntimePath($this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . $appName . DIRECTORY_SEPARATOR);
332
333 3
        // 设置应用命名空间
334 1
        $this->app->setNamespace($this->app->config->get('app.app_namespace') ?: 'app\\' . $appName);
335
336
        //加载应用
337
        $this->loadApp($appName);
338 1
    }
339
340 2
    /**
341 1
     * 加载应用文件
342 1
     * @param string $appName 应用名
343
     * @return void
344
     */
345 1
    protected function loadApp(string $appName): void
346
    {
347
        //加载app文件
348 2
        if (is_dir($this->app->getAppPath())) {
349 2
            $appPath = $this->app->getAppPath();
350 4
351
            if (is_file($appPath . 'common.php')) {
352
                include_once $appPath . 'common.php';
353
            }
354 1
355
            $configPath = $this->app->getConfigPath();
356
357 5
            $files = [];
358 5
359
            if (is_dir($appPath . 'config')) {
360
                $files = array_merge($files, glob($appPath . 'config' . DIRECTORY_SEPARATOR . '*' . $this->app->getConfigExt()));
0 ignored issues
show
Bug introduced by
It seems like glob($appPath . 'config'...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

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