Passed
Pull Request — 5.1 (#1748)
by guanguans
09:27
created

App   F

Complexity

Total Complexity 117

Size/Duplication

Total Lines 958
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 291
dl 0
loc 958
rs 2
c 0
b 0
f 0
wmc 117

41 Methods

Rating   Name   Duplication   Size   Complexity  
A db() 0 3 1
B routeCheck() 0 36 10
A create() 0 24 4
A getBeginMem() 0 3 1
B routeInit() 0 26 8
A checkRequestCache() 0 6 2
A getModulePath() 0 3 1
A setModulePath() 0 4 1
A getConfigExt() 0 3 1
A bind() 0 4 1
A getAppPath() 0 7 2
B initialize() 0 97 9
A config() 0 3 1
F init() 0 67 18
A dispatch() 0 4 1
A log() 0 3 2
A parseClass() 0 8 5
A setResponseCache() 0 13 3
A isDebug() 0 3 1
A __construct() 0 4 1
A getNamespace() 0 3 1
A getRuntimePath() 0 3 1
A parseModuleAndClass() 0 16 3
A getRouteCacheKey() 0 10 2
A getConfigPath() 0 3 1
A containerConfigUpdate() 0 28 2
A validate() 0 9 3
B run() 0 66 10
A getThinkPath() 0 3 1
A getSuffix() 0 3 1
A controller() 0 11 4
A routeMust() 0 4 1
A model() 0 3 1
A path() 0 5 2
A loadLangPack() 0 16 2
A getBeginTime() 0 3 1
A getRoutePath() 0 3 1
A version() 0 3 1
A getRootPath() 0 3 1
A setNamespace() 0 4 1
A action() 0 16 4

How to fix   Complexity   

Complex Class

Complex classes like App often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use App, and based on these observations, apply Extract Interface, too.

1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2018 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
12
namespace think;
13
14
use think\exception\ClassNotFoundException;
15
use think\exception\HttpResponseException;
16
use think\route\Dispatch;
17
18
/**
19
 * App 应用管理
20
 */
5 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @package tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
21
class App extends Container
22
{
23
    const VERSION = '5.1.35 LTS';
24
25
    /**
26
     * 当前模块路径
27
     * @var string
28
     */
29
    protected $modulePath;
30
31
    /**
32
     * 应用调试模式
33
     * @var bool
34
     */
35
    protected $appDebug = true;
36
37
    /**
38
     * 应用开始时间
39
     * @var float
40
     */
41
    protected $beginTime;
42
43
    /**
44
     * 应用内存初始占用
45
     * @var integer
46
     */
47
    protected $beginMem;
48
49
    /**
50
     * 应用类库命名空间
51
     * @var string
52
     */
53
    protected $namespace = 'app';
54
55
    /**
56
     * 应用类库后缀
57
     * @var bool
58
     */
59
    protected $suffix = false;
60
61
    /**
62
     * 严格路由检测
63
     * @var bool
64
     */
65
    protected $routeMust;
66
67
    /**
68
     * 应用类库目录
69
     * @var string
70
     */
71
    protected $appPath;
72
73
    /**
74
     * 框架目录
75
     * @var string
76
     */
77
    protected $thinkPath;
78
79
    /**
80
     * 应用根目录
81
     * @var string
82
     */
83
    protected $rootPath;
84
85
    /**
86
     * 运行时目录
87
     * @var string
88
     */
89
    protected $runtimePath;
90
91
    /**
92
     * 配置目录
93
     * @var string
94
     */
95
    protected $configPath;
96
97
    /**
98
     * 路由目录
99
     * @var string
100
     */
101
    protected $routePath;
102
103
    /**
104
     * 配置后缀
105
     * @var string
106
     */
107
    protected $configExt;
108
109
    /**
110
     * 应用调度实例
111
     * @var Dispatch
112
     */
113
    protected $dispatch;
114
115
    /**
116
     * 绑定模块(控制器)
117
     * @var string
118
     */
119
    protected $bindModule;
120
121
    /**
122
     * 初始化
123
     * @var bool
124
     */
125
    protected $initialized = false;
126
127
    public function __construct($appPath = '')
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
128
    {
129
        $this->thinkPath = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
130
        $this->path($appPath);
131
    }
132
133
    /**
134
     * 绑定模块或者控制器
135
     * @access public
136
     * @param  string $bind
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
137
     * @return $this
138
     */
139
    public function bind($bind)
140
    {
141
        $this->bindModule = $bind;
142
        return $this;
143
    }
144
145
    /**
146
     * 设置应用类库目录
147
     * @access public
148
     * @param  string $path 路径
149
     * @return $this
150
     */
151
    public function path($path)
152
    {
153
        $this->appPath = $path ? realpath($path) . DIRECTORY_SEPARATOR : $this->getAppPath();
154
155
        return $this;
156
    }
157
158
    /**
159
     * 初始化应用
160
     * @access public
161
     * @return void
162
     */
163
    public function initialize()
164
    {
165
        if ($this->initialized) {
166
            return;
167
        }
168
169
        $this->initialized = true;
170
        $this->beginTime   = microtime(true);
171
        $this->beginMem    = memory_get_usage();
172
173
        $this->rootPath    = dirname($this->appPath) . DIRECTORY_SEPARATOR;
174
        $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
175
        $this->routePath   = $this->rootPath . 'route' . DIRECTORY_SEPARATOR;
176
        $this->configPath  = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
177
178
        static::setInstance($this);
179
180
        $this->instance('app', $this);
181
182
        // 加载环境变量配置文件
183
        if (is_file($this->rootPath . '.env')) {
184
            $this->env->load($this->rootPath . '.env');
185
        }
186
187
        $this->configExt = $this->env->get('config_ext', '.php');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->env->get('config_ext', '.php') can also be of type boolean. However, the property $configExt is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
188
189
        // 加载惯例配置文件
190
        $this->config->set(include $this->thinkPath . 'convention.php');
191
192
        // 设置路径环境变量
193
        $this->env->set([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
194
            'think_path'   => $this->thinkPath,
195
            'root_path'    => $this->rootPath,
196
            'app_path'     => $this->appPath,
197
            'config_path'  => $this->configPath,
198
            'route_path'   => $this->routePath,
199
            'runtime_path' => $this->runtimePath,
200
            'extend_path'  => $this->rootPath . 'extend' . DIRECTORY_SEPARATOR,
201
            'vendor_path'  => $this->rootPath . 'vendor' . DIRECTORY_SEPARATOR,
202
        ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
203
204
        $this->namespace = $this->env->get('app_namespace', $this->namespace);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->env->get('app_nam...ace', $this->namespace) can also be of type boolean. However, the property $namespace is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
205
        $this->env->set('app_namespace', $this->namespace);
206
207
        // 注册应用命名空间
208
        Loader::addNamespace($this->namespace, $this->appPath);
209
210
        // 初始化应用
211
        $this->init();
212
213
        // 开启类名后缀
214
        $this->suffix = $this->config('app.class_suffix');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->config('app.class_suffix') can also be of type array. However, the property $suffix is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
215
216
        // 应用调试模式
217
        $this->appDebug = $this->env->get('app_debug', $this->config('app.app_debug'));
218
        $this->env->set('app_debug', $this->appDebug);
219
220
        if (!$this->appDebug) {
221
            ini_set('display_errors', 'Off');
222
        } elseif (PHP_SAPI != 'cli') {
223
            //重新申请一块比较大的buffer
224
            if (ob_get_level() > 0) {
225
                $output = ob_get_clean();
226
            }
227
            ob_start();
228
            if (!empty($output)) {
229
                echo $output;
230
            }
231
        }
232
233
        // 注册异常处理类
234
        if ($this->config('app.exception_handle')) {
235
            Error::setExceptionHandler($this->config('app.exception_handle'));
236
        }
237
238
        // 注册根命名空间
239
        if (!empty($this->config('app.root_namespace'))) {
240
            Loader::addNamespace($this->config('app.root_namespace'));
241
        }
242
243
        // 加载composer autofile文件
244
        Loader::loadComposerAutoloadFiles();
245
246
        // 注册类库别名
247
        Loader::addClassAlias($this->config->pull('alias'));
248
249
        // 数据库配置初始化
250
        Db::init($this->config->pull('database'));
251
252
        // 设置系统时区
253
        date_default_timezone_set($this->config('app.default_timezone'));
0 ignored issues
show
Bug introduced by
It seems like $this->config('app.default_timezone') can also be of type array; however, parameter $timezone_identifier of date_default_timezone_set() 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

253
        date_default_timezone_set(/** @scrutinizer ignore-type */ $this->config('app.default_timezone'));
Loading history...
254
255
        // 读取语言包
256
        $this->loadLangPack();
257
258
        // 路由初始化
259
        $this->routeInit();
260
    }
261
262
    /**
263
     * 初始化应用或模块
264
     * @access public
265
     * @param  string $module 模块名
266
     * @return void
267
     */
268
    public function init($module = '')
269
    {
270
        // 定位模块目录
271
        $module = $module ? $module . DIRECTORY_SEPARATOR : '';
272
        $path   = $this->appPath . $module;
273
274
        // 加载初始化文件
275
        if (is_file($path . 'init.php')) {
276
            include $path . 'init.php';
277
        } elseif (is_file($this->runtimePath . $module . 'init.php')) {
278
            include $this->runtimePath . $module . 'init.php';
279
        } else {
280
            // 加载行为扩展文件
281
            if (is_file($path . 'tags.php')) {
282
                $tags = include $path . 'tags.php';
283
                if (is_array($tags)) {
284
                    $this->hook->import($tags);
285
                }
286
            }
287
288
            // 加载公共文件
289
            if (is_file($path . 'common.php')) {
290
                include_once $path . 'common.php';
291
            }
292
293
            if ('' == $module) {
294
                // 加载系统助手函数
295
                include $this->thinkPath . 'helper.php';
296
            }
297
298
            // 加载中间件
299
            if (is_file($path . 'middleware.php')) {
300
                $middleware = include $path . 'middleware.php';
301
                if (is_array($middleware)) {
302
                    $this->middleware->import($middleware);
303
                }
304
            }
305
306
            // 注册服务的容器对象实例
307
            if (is_file($path . 'provider.php')) {
308
                $provider = include $path . 'provider.php';
309
                if (is_array($provider)) {
310
                    $this->bindTo($provider);
311
                }
312
            }
313
314
            // 自动读取配置文件
315
            if (is_dir($path . 'config')) {
316
                $dir = $path . 'config' . DIRECTORY_SEPARATOR;
317
            } elseif (is_dir($this->configPath . $module)) {
318
                $dir = $this->configPath . $module;
319
            }
320
321
            $files = isset($dir) ? scandir($dir) : [];
322
323
            foreach ($files as $file) {
324
                if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {
325
                    $this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dir does not seem to be defined for all execution paths leading up to this point.
Loading history...
326
                }
327
            }
328
        }
329
330
        $this->setModulePath($path);
331
332
        if ($module) {
333
            // 对容器中的对象实例进行配置更新
334
            $this->containerConfigUpdate($module);
335
        }
336
    }
337
338
    protected function containerConfigUpdate($module)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
339
    {
340
        $config = $this->config->get();
341
342
        // 注册异常处理类
343
        if ($config['app']['exception_handle']) {
344
            Error::setExceptionHandler($config['app']['exception_handle']);
345
        }
346
347
        Db::init($config['database']);
348
        $this->middleware->setConfig($config['middleware']);
349
        $this->route->setConfig($config['app']);
350
        $this->request->init($config['app']);
351
        $this->cookie->init($config['cookie']);
352
        $this->view->init($config['template']);
353
        $this->log->init($config['log']);
354
        $this->session->setConfig($config['session']);
355
        $this->debug->setConfig($config['trace']);
356
        $this->cache->init($config['cache'], true);
357
358
        // 加载当前模块语言包
359
        $this->lang->load($this->appPath . $module . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php');
360
361
        // 模块请求缓存检查
362
        $this->checkRequestCache(
363
            $config['app']['request_cache'],
364
            $config['app']['request_cache_expire'],
365
            $config['app']['request_cache_except']
366
        );
367
    }
368
369
    /**
370
     * 执行应用程序
371
     * @access public
372
     * @return Response
373
     * @throws Exception
374
     */
375
    public function run()
376
    {
377
        try {
378
            // 初始化应用
379
            $this->initialize();
380
381
            // 监听app_init
382
            $this->hook->listen('app_init');
383
384
            if ($this->bindModule) {
385
                // 模块/控制器绑定
386
                $this->route->bind($this->bindModule);
387
            } elseif ($this->config('app.auto_bind_module')) {
388
                // 入口自动绑定
389
                $name = pathinfo($this->request->baseFile(), PATHINFO_FILENAME);
390
                if ($name && 'index' != $name && is_dir($this->appPath . $name)) {
391
                    $this->route->bind($name);
392
                }
393
            }
394
395
            // 监听app_dispatch
396
            $this->hook->listen('app_dispatch');
397
398
            $dispatch = $this->dispatch;
399
400
            if (empty($dispatch)) {
401
                // 路由检测
402
                $dispatch = $this->routeCheck()->init();
403
            }
404
405
            // 记录当前调度信息
406
            $this->request->dispatch($dispatch);
407
408
            // 记录路由和请求信息
409
            if ($this->appDebug) {
410
                $this->log('[ ROUTE ] ' . var_export($this->request->routeInfo(), true));
411
                $this->log('[ HEADER ] ' . var_export($this->request->header(), true));
412
                $this->log('[ PARAM ] ' . var_export($this->request->param(), true));
413
            }
414
415
            // 监听app_begin
416
            $this->hook->listen('app_begin');
417
418
            // 请求缓存检查
419
            $this->checkRequestCache(
420
                $this->config('request_cache'),
0 ignored issues
show
Bug introduced by
It seems like $this->config('request_cache') can also be of type array; however, parameter $key of think\App::checkRequestCache() 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

420
                /** @scrutinizer ignore-type */ $this->config('request_cache'),
Loading history...
421
                $this->config('request_cache_expire'),
422
                $this->config('request_cache_except')
423
            );
424
425
            $data = null;
426
        } catch (HttpResponseException $exception) {
427
            $dispatch = null;
428
            $data     = $exception->getResponse();
429
        }
430
431
        $this->middleware->add(function (Request $request, $next) use ($dispatch, $data) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

431
        $this->middleware->add(function (/** @scrutinizer ignore-unused */ Request $request, $next) use ($dispatch, $data) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $next is not used and could be removed. ( Ignorable by Annotation )

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

431
        $this->middleware->add(function (Request $request, /** @scrutinizer ignore-unused */ $next) use ($dispatch, $data) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
432
            return is_null($data) ? $dispatch->run() : $data;
433
        });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
434
435
        $response = $this->middleware->dispatch($this->request);
436
437
        // 监听app_end
438
        $this->hook->listen('app_end', $response);
439
440
        return $response;
441
    }
442
443
    protected function getRouteCacheKey()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
444
    {
445
        if ($this->config->get('route_check_cache_key')) {
446
            $closure  = $this->config->get('route_check_cache_key');
447
            $routeKey = $closure($this->request);
448
        } else {
449
            $routeKey = md5($this->request->baseUrl(true) . ':' . $this->request->method());
450
        }
451
452
        return $routeKey;
453
    }
454
455
    protected function loadLangPack()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
456
    {
457
        // 读取默认语言
458
        $this->lang->range($this->config('app.default_lang'));
459
460
        if ($this->config('app.lang_switch_on')) {
461
            // 开启多语言机制 检测当前语言
462
            $this->lang->detect();
463
        }
464
465
        $this->request->setLangset($this->lang->range());
466
467
        // 加载系统语言包
468
        $this->lang->load([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
469
            $this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',
470
            $this->appPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',
471
        ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
472
    }
473
474
    /**
475
     * 设置当前地址的请求缓存
476
     * @access public
477
     * @param  string $key 缓存标识,支持变量规则 ,例如 item/:name/:id
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
478
     * @param  mixed  $expire 缓存有效期
479
     * @param  array  $except 缓存排除
480
     * @param  string $tag    缓存标签
481
     * @return void
482
     */
483
    public function checkRequestCache($key, $expire = null, $except = [], $tag = null)
484
    {
485
        $cache = $this->request->cache($key, $expire, $except, $tag);
486
487
        if ($cache) {
488
            $this->setResponseCache($cache);
489
        }
490
    }
491
492
    public function setResponseCache($cache)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
493
    {
494
        list($key, $expire, $tag) = $cache;
495
496
        if (strtotime($this->request->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $this->request->server('REQUEST_TIME')) {
497
            // 读取缓存
498
            $response = Response::create()->code(304);
499
            throw new HttpResponseException($response);
500
        } elseif ($this->cache->has($key)) {
0 ignored issues
show
Bug introduced by
The method has() does not exist on think\Cache. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

500
        } elseif ($this->cache->/** @scrutinizer ignore-call */ has($key)) {
Loading history...
501
            list($content, $header) = $this->cache->get($key);
0 ignored issues
show
Bug introduced by
The method get() does not exist on think\Cache. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

501
            /** @scrutinizer ignore-call */ 
502
            list($content, $header) = $this->cache->get($key);
Loading history...
502
503
            $response = Response::create($content)->header($header);
504
            throw new HttpResponseException($response);
505
        }
506
    }
507
508
    /**
509
     * 设置当前请求的调度信息
510
     * @access public
511
     * @param  Dispatch  $dispatch 调度信息
512
     * @return $this
513
     */
514
    public function dispatch(Dispatch $dispatch)
515
    {
516
        $this->dispatch = $dispatch;
517
        return $this;
518
    }
519
520
    /**
521
     * 记录调试信息
522
     * @access public
523
     * @param  mixed  $msg  调试信息
524
     * @param  string $type 信息类型
525
     * @return void
526
     */
527
    public function log($msg, $type = 'info')
528
    {
529
        $this->appDebug && $this->log->record($msg, $type);
530
    }
531
532
    /**
533
     * 获取配置参数 为空则获取所有配置
534
     * @access public
535
     * @param  string    $name 配置参数名(支持二级配置 .号分割)
536
     * @return mixed
537
     */
538
    public function config($name = '')
539
    {
540
        return $this->config->get($name);
541
    }
542
543
    /**
544
     * 路由初始化 导入路由定义规则
545
     * @access public
546
     * @return void
547
     */
548
    public function routeInit()
549
    {
550
        // 路由检测
551
        $files = scandir($this->routePath);
552
        foreach ($files as $file) {
553
            if (strpos($file, '.php')) {
554
                $filename = $this->routePath . $file;
555
                // 导入路由配置
556
                $rules = include $filename;
557
                if (is_array($rules)) {
558
                    $this->route->import($rules);
559
                }
560
            }
561
        }
562
563
        if ($this->route->config('route_annotation')) {
564
            // 自动生成路由定义
565
            if ($this->appDebug) {
566
                $suffix = $this->route->config('controller_suffix') || $this->route->config('class_suffix');
567
                $this->build->buildRoute($suffix);
568
            }
569
570
            $filename = $this->runtimePath . 'build_route.php';
571
572
            if (is_file($filename)) {
573
                include $filename;
574
            }
575
        }
576
    }
577
578
    /**
579
     * URL路由检测(根据PATH_INFO)
580
     * @access public
581
     * @return Dispatch
582
     */
583
    public function routeCheck()
584
    {
585
        // 检测路由缓存
586
        if (!$this->appDebug && $this->config->get('route_check_cache')) {
587
            $routeKey = $this->getRouteCacheKey();
588
            $option   = $this->config->get('route_cache_option');
589
590
            if ($option && $this->cache->connect($option)->has($routeKey)) {
591
                return $this->cache->connect($option)->get($routeKey);
592
            } elseif ($this->cache->has($routeKey)) {
593
                return $this->cache->get($routeKey);
594
            }
595
        }
596
597
        // 获取应用调度信息
598
        $path = $this->request->path();
599
600
        // 是否强制路由模式
601
        $must = !is_null($this->routeMust) ? $this->routeMust : $this->route->config('url_route_must');
0 ignored issues
show
introduced by
The condition is_null($this->routeMust) is always false.
Loading history...
602
603
        // 路由检测 返回一个Dispatch对象
604
        $dispatch = $this->route->check($path, $must);
605
606
        if (!empty($routeKey)) {
607
            try {
608
                if ($option) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $option does not seem to be defined for all execution paths leading up to this point.
Loading history...
609
                    $this->cache->connect($option)->tag('route_cache')->set($routeKey, $dispatch);
610
                } else {
611
                    $this->cache->tag('route_cache')->set($routeKey, $dispatch);
0 ignored issues
show
Bug introduced by
The method tag() does not exist on think\Cache. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

611
                    $this->cache->/** @scrutinizer ignore-call */ 
612
                                  tag('route_cache')->set($routeKey, $dispatch);
Loading history...
612
                }
613
            } catch (\Exception $e) {
614
                // 存在闭包的时候缓存无效
615
            }
616
        }
617
618
        return $dispatch;
619
    }
620
621
    /**
622
     * 设置应用的路由检测机制
623
     * @access public
624
     * @param  bool $must  是否强制检测路由
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
625
     * @return $this
626
     */
627
    public function routeMust($must = false)
628
    {
629
        $this->routeMust = $must;
630
        return $this;
631
    }
632
633
    /**
634
     * 解析模块和类名
635
     * @access protected
636
     * @param  string $name         资源地址
637
     * @param  string $layer        验证层名称
638
     * @param  bool   $appendSuffix 是否添加类名后缀
639
     * @return array
640
     */
641
    protected function parseModuleAndClass($name, $layer, $appendSuffix)
642
    {
643
        if (false !== strpos($name, '\\')) {
644
            $class  = $name;
645
            $module = $this->request->module();
646
        } else {
647
            if (strpos($name, '/')) {
648
                list($module, $name) = explode('/', $name, 2);
649
            } else {
650
                $module = $this->request->module();
651
            }
652
653
            $class = $this->parseClass($module, $layer, $name, $appendSuffix);
654
        }
655
656
        return [$module, $class];
657
    }
658
659
    /**
660
     * 实例化应用类库
661
     * @access public
662
     * @param  string $name         类名称
663
     * @param  string $layer        业务层名称
664
     * @param  bool   $appendSuffix 是否添加类名后缀
665
     * @param  string $common       公共模块名
666
     * @return object
667
     * @throws ClassNotFoundException
668
     */
669
    public function create($name, $layer, $appendSuffix = false, $common = 'common')
670
    {
671
        $guid = $name . $layer;
672
673
        if ($this->__isset($guid)) {
674
            return $this->__get($guid);
675
        }
676
677
        list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
678
679
        if (class_exists($class)) {
680
            $object = $this->__get($class);
681
        } else {
682
            $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
683
            if (class_exists($class)) {
684
                $object = $this->__get($class);
685
            } else {
686
                throw new ClassNotFoundException('class not exists:' . $class, $class);
687
            }
688
        }
689
690
        $this->__set($guid, $class);
691
692
        return $object;
693
    }
694
695
    /**
696
     * 实例化(分层)模型
697
     * @access public
698
     * @param  string $name         Model名称
699
     * @param  string $layer        业务层名称
700
     * @param  bool   $appendSuffix 是否添加类名后缀
701
     * @param  string $common       公共模块名
702
     * @return Model
703
     * @throws ClassNotFoundException
704
     */
705
    public function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
706
    {
707
        return $this->create($name, $layer, $appendSuffix, $common);
708
    }
709
710
    /**
711
     * 实例化(分层)控制器 格式:[模块名/]控制器名
712
     * @access public
713
     * @param  string $name              资源地址
0 ignored issues
show
Coding Style introduced by
Expected 9 spaces after parameter name; 14 found
Loading history...
714
     * @param  string $layer             控制层名称
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 13 found
Loading history...
715
     * @param  bool   $appendSuffix      是否添加类名后缀
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 6 found
Loading history...
716
     * @param  string $empty             空控制器名称
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 13 found
Loading history...
717
     * @return object
718
     * @throws ClassNotFoundException
719
     */
720
    public function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
721
    {
722
        list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);
723
724
        if (class_exists($class)) {
725
            return $this->make($class, true);
726
        } elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) {
727
            return $this->make($emptyClass, true);
728
        }
729
730
        throw new ClassNotFoundException('class not exists:' . $class, $class);
731
    }
732
733
    /**
734
     * 实例化验证类 格式:[模块名/]验证器名
735
     * @access public
736
     * @param  string $name         资源地址
737
     * @param  string $layer        验证层名称
738
     * @param  bool   $appendSuffix 是否添加类名后缀
739
     * @param  string $common       公共模块名
740
     * @return Validate
741
     * @throws ClassNotFoundException
742
     */
743
    public function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')
744
    {
745
        $name = $name ?: $this->config('default_validate');
746
747
        if (empty($name)) {
748
            return new Validate;
749
        }
750
751
        return $this->create($name, $layer, $appendSuffix, $common);
0 ignored issues
show
Bug introduced by
It seems like $name can also be of type array; however, parameter $name of think\App::create() 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

751
        return $this->create(/** @scrutinizer ignore-type */ $name, $layer, $appendSuffix, $common);
Loading history...
752
    }
753
754
    /**
755
     * 数据库初始化
756
     * @access public
757
     * @param  mixed         $config 数据库配置
758
     * @param  bool|string   $name 连接标识 true 强制重新连接
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
759
     * @return \think\db\Query
760
     */
761
    public function db($config = [], $name = false)
762
    {
763
        return Db::connect($config, $name);
764
    }
765
766
    /**
767
     * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
768
     * @access public
769
     * @param  string       $url          调用地址
770
     * @param  string|array $vars         调用参数 支持字符串和数组
771
     * @param  string       $layer        要调用的控制层名称
772
     * @param  bool         $appendSuffix 是否添加类名后缀
773
     * @return mixed
774
     * @throws ClassNotFoundException
775
     */
776
    public function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
777
    {
778
        $info   = pathinfo($url);
779
        $action = $info['basename'];
780
        $module = '.' != $info['dirname'] ? $info['dirname'] : $this->request->controller();
781
        $class  = $this->controller($module, $layer, $appendSuffix);
782
783
        if (is_scalar($vars)) {
784
            if (strpos($vars, '=')) {
785
                parse_str($vars, $vars);
0 ignored issues
show
Bug introduced by
$vars of type string is incompatible with the type array|null expected by parameter $arr of parse_str(). ( Ignorable by Annotation )

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

785
                parse_str($vars, /** @scrutinizer ignore-type */ $vars);
Loading history...
786
            } else {
787
                $vars = [$vars];
788
            }
789
        }
790
791
        return $this->invokeMethod([$class, $action . $this->config('action_suffix')], $vars);
0 ignored issues
show
Bug introduced by
Are you sure $this->config('action_suffix') of type array|mixed|null can be used in concatenation? ( Ignorable by Annotation )

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

791
        return $this->invokeMethod([$class, $action . /** @scrutinizer ignore-type */ $this->config('action_suffix')], $vars);
Loading history...
792
    }
793
794
    /**
795
     * 解析应用类的类名
796
     * @access public
797
     * @param  string $module 模块名
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 1 found
Loading history...
798
     * @param  string $layer  层名 controller model ...
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 2 found
Loading history...
799
     * @param  string $name   类名
0 ignored issues
show
Coding Style introduced by
Expected 9 spaces after parameter name; 3 found
Loading history...
800
     * @param  bool   $appendSuffix
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
801
     * @return string
802
     */
803
    public function parseClass($module, $layer, $name, $appendSuffix = false)
804
    {
805
        $name  = str_replace(['/', '.'], '\\', $name);
806
        $array = explode('\\', $name);
807
        $class = Loader::parseName(array_pop($array), 1) . ($this->suffix || $appendSuffix ? ucfirst($layer) : '');
808
        $path  = $array ? implode('\\', $array) . '\\' : '';
809
810
        return $this->namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class;
811
    }
812
813
    /**
814
     * 获取框架版本
815
     * @access public
816
     * @return string
817
     */
818
    public function version()
819
    {
820
        return static::VERSION;
821
    }
822
823
    /**
824
     * 是否为调试模式
825
     * @access public
826
     * @return bool
827
     */
828
    public function isDebug()
829
    {
830
        return $this->appDebug;
831
    }
832
833
    /**
834
     * 获取模块路径
835
     * @access public
836
     * @return string
837
     */
838
    public function getModulePath()
839
    {
840
        return $this->modulePath;
841
    }
842
843
    /**
844
     * 设置模块路径
845
     * @access public
846
     * @param  string $path 路径
847
     * @return void
848
     */
849
    public function setModulePath($path)
850
    {
851
        $this->modulePath = $path;
852
        $this->env->set('module_path', $path);
853
    }
854
855
    /**
856
     * 获取应用根目录
857
     * @access public
858
     * @return string
859
     */
860
    public function getRootPath()
861
    {
862
        return $this->rootPath;
863
    }
864
865
    /**
866
     * 获取应用类库目录
867
     * @access public
868
     * @return string
869
     */
870
    public function getAppPath()
871
    {
872
        if (is_null($this->appPath)) {
0 ignored issues
show
introduced by
The condition is_null($this->appPath) is always false.
Loading history...
873
            $this->appPath = Loader::getRootPath() . 'application' . DIRECTORY_SEPARATOR;
874
        }
875
876
        return $this->appPath;
877
    }
878
879
    /**
880
     * 获取应用运行时目录
881
     * @access public
882
     * @return string
883
     */
884
    public function getRuntimePath()
885
    {
886
        return $this->runtimePath;
887
    }
888
889
    /**
890
     * 获取核心框架目录
891
     * @access public
892
     * @return string
893
     */
894
    public function getThinkPath()
895
    {
896
        return $this->thinkPath;
897
    }
898
899
    /**
900
     * 获取路由目录
901
     * @access public
902
     * @return string
903
     */
904
    public function getRoutePath()
905
    {
906
        return $this->routePath;
907
    }
908
909
    /**
910
     * 获取应用配置目录
911
     * @access public
912
     * @return string
913
     */
914
    public function getConfigPath()
915
    {
916
        return $this->configPath;
917
    }
918
919
    /**
920
     * 获取配置后缀
921
     * @access public
922
     * @return string
923
     */
924
    public function getConfigExt()
925
    {
926
        return $this->configExt;
927
    }
928
929
    /**
930
     * 获取应用类库命名空间
931
     * @access public
932
     * @return string
933
     */
934
    public function getNamespace()
935
    {
936
        return $this->namespace;
937
    }
938
939
    /**
940
     * 设置应用类库命名空间
941
     * @access public
942
     * @param  string $namespace 命名空间名称
943
     * @return $this
944
     */
945
    public function setNamespace($namespace)
946
    {
947
        $this->namespace = $namespace;
948
        return $this;
949
    }
950
951
    /**
952
     * 是否启用类库后缀
953
     * @access public
954
     * @return bool
955
     */
956
    public function getSuffix()
957
    {
958
        return $this->suffix;
959
    }
960
961
    /**
962
     * 获取应用开启时间
963
     * @access public
964
     * @return float
965
     */
966
    public function getBeginTime()
967
    {
968
        return $this->beginTime;
969
    }
970
971
    /**
972
     * 获取应用初始内存占用
973
     * @access public
974
     * @return integer
975
     */
976
    public function getBeginMem()
977
    {
978
        return $this->beginMem;
979
    }
980
981
}
982