Completed
Push — 6.0 ( 4de6f5...a6720b )
by liu
03:17
created

App   F

Complexity

Total Complexity 60

Size/Duplication

Total Lines 584
Duplicated Lines 0 %

Test Coverage

Coverage 90.6%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 152
c 3
b 0
f 0
dl 0
loc 584
ccs 135
cts 149
cp 0.906
rs 3.6
wmc 60

31 Methods

Rating   Name   Duplication   Size   Complexity  
A runningInConsole() 0 3 2
A debug() 0 4 1
A initialized() 0 3 1
A getBeginMem() 0 3 1
A getConfigExt() 0 3 1
A setRuntimePath() 0 3 1
A getAppPath() 0 3 1
A initialize() 0 38 3
A bootService() 0 4 2
A boot() 0 4 1
A register() 0 21 6
A parseClass() 0 8 2
B load() 0 30 7
A isDebug() 0 3 1
A __construct() 0 15 3
A getNamespace() 0 3 1
A getRuntimePath() 0 3 1
A getConfigPath() 0 3 1
A getThinkPath() 0 3 1
A loadEvent() 0 12 4
A getBasePath() 0 3 1
A setAppPath() 0 3 1
A loadLangPack() 0 15 3
A getDefaultRootPath() 0 3 1
A getBeginTime() 0 3 1
A version() 0 3 1
A getRootPath() 0 3 1
A debugModeInit() 0 16 6
A setNamespace() 0 4 1
A setEnvName() 0 4 1
A getService() 0 6 2

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
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2021 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\AppInit;
16
use think\helper\Str;
17
use think\initializer\BootService;
18
use think\initializer\Error;
19
use think\initializer\RegisterService;
20
21
/**
22
 * App 基础类
23
 * @property Route      $route
24
 * @property Config     $config
25
 * @property Cache      $cache
26
 * @property Request    $request
27
 * @property Http       $http
28
 * @property Console    $console
29
 * @property Env        $env
30
 * @property Event      $event
31
 * @property Middleware $middleware
32
 * @property Log        $log
33
 * @property Lang       $lang
34
 * @property Db         $db
35
 * @property Cookie     $cookie
36
 * @property Session    $session
37
 * @property Validate   $validate
38
 * @property Filesystem $filesystem
39
 */
40
class App extends Container
41
{
42
    const VERSION = '6.0.7';
43
44
    /**
45
     * 应用调试模式
46
     * @var bool
47
     */
48
    protected $appDebug = false;
49
50
    /**
51
     * 环境变量标识
52
     * @var string
53
     */
54
    protected $envName = '';
55
56
    /**
57
     * 应用开始时间
58
     * @var float
59
     */
60
    protected $beginTime;
61
62
    /**
63
     * 应用内存初始占用
64
     * @var integer
65
     */
66
    protected $beginMem;
67
68
    /**
69
     * 当前应用类库命名空间
70
     * @var string
71
     */
72
    protected $namespace = 'app';
73
74
    /**
75
     * 应用根目录
76
     * @var string
77
     */
78
    protected $rootPath = '';
79
80
    /**
81
     * 框架目录
82
     * @var string
83
     */
84
    protected $thinkPath = '';
85
86
    /**
87
     * 应用目录
88
     * @var string
89
     */
90
    protected $appPath = '';
91
92
    /**
93
     * Runtime目录
94
     * @var string
95
     */
96
    protected $runtimePath = '';
97
98
    /**
99
     * 路由定义目录
100
     * @var string
101
     */
102
    protected $routePath = '';
103
104
    /**
105
     * 配置后缀
106
     * @var string
107
     */
108
    protected $configExt = '.php';
109
110
    /**
111
     * 应用初始化器
112
     * @var array
113
     */
114
    protected $initializers = [
115
        Error::class,
116
        RegisterService::class,
117
        BootService::class,
118
    ];
119
120
    /**
121
     * 注册的系统服务
122
     * @var array
123
     */
124
    protected $services = [];
125
126
    /**
127
     * 初始化
128
     * @var bool
129
     */
130
    protected $initialized = false;
131
132
    /**
133
     * 容器绑定标识
134
     * @var array
135
     */
136
    protected $bind = [
137
        'app'                     => App::class,
138
        'cache'                   => Cache::class,
139
        'config'                  => Config::class,
140
        'console'                 => Console::class,
141
        'cookie'                  => Cookie::class,
142
        'db'                      => Db::class,
143
        'env'                     => Env::class,
144
        'event'                   => Event::class,
145
        'http'                    => Http::class,
146
        'lang'                    => Lang::class,
147
        'log'                     => Log::class,
148
        'middleware'              => Middleware::class,
149
        'request'                 => Request::class,
150
        'response'                => Response::class,
151
        'route'                   => Route::class,
152
        'session'                 => Session::class,
153
        'validate'                => Validate::class,
154
        'view'                    => View::class,
155
        'filesystem'              => Filesystem::class,
156
        'think\DbManager'         => Db::class,
157
        'think\LogManager'        => Log::class,
158
        'think\CacheManager'      => Cache::class,
159
160
        // 接口依赖注入
161
        'Psr\Log\LoggerInterface' => Log::class,
162
    ];
163
164
    /**
165
     * 架构方法
166
     * @access public
167
     * @param string $rootPath 应用根目录
168
     */
169 30
    public function __construct(string $rootPath = '')
170
    {
171 30
        $this->thinkPath   = dirname(__DIR__) . DIRECTORY_SEPARATOR;
172 30
        $this->rootPath    = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
173 30
        $this->appPath     = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
174 30
        $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
175
176 30
        if (is_file($this->appPath . 'provider.php')) {
177 3
            $this->bind(include $this->appPath . 'provider.php');
178
        }
179
180 30
        static::setInstance($this);
181
182 30
        $this->instance('app', $this);
183 30
        $this->instance('think\Container', $this);
184 30
    }
185
186
    /**
187
     * 注册服务
188
     * @access public
189
     * @param Service|string $service 服务
190
     * @param bool           $force   强制重新注册
191
     * @return Service|null
192
     */
193 3
    public function register($service, bool $force = false)
194
    {
195 3
        $registered = $this->getService($service);
196
197 3
        if ($registered && !$force) {
198 3
            return $registered;
199
        }
200
201 3
        if (is_string($service)) {
202 3
            $service = new $service($this);
203
        }
204
205 3
        if (method_exists($service, 'register')) {
206 3
            $service->register();
207
        }
208
209 3
        if (property_exists($service, 'bind')) {
210 3
            $this->bind($service->bind);
211
        }
212
213 3
        $this->services[] = $service;
214 3
    }
215
216
    /**
217
     * 执行服务
218
     * @access public
219
     * @param Service $service 服务
220
     * @return mixed
221
     */
222 3
    public function bootService($service)
223
    {
224 3
        if (method_exists($service, 'boot')) {
225 3
            return $this->invoke([$service, 'boot']);
226
        }
227 3
    }
228
229
    /**
230
     * 获取服务
231
     * @param string|Service $service
232
     * @return Service|null
233
     */
234 3
    public function getService($service)
235
    {
236 3
        $name = is_string($service) ? $service : get_class($service);
237
        return array_values(array_filter($this->services, function ($value) use ($name) {
238 3
            return $value instanceof $name;
239 3
        }, ARRAY_FILTER_USE_BOTH))[0] ?? null;
240
    }
241
242
    /**
243
     * 开启应用调试模式
244
     * @access public
245
     * @param bool $debug 开启应用调试模式
246
     * @return $this
247
     */
248 6
    public function debug(bool $debug = true)
249
    {
250 6
        $this->appDebug = $debug;
251 6
        return $this;
252
    }
253
254
    /**
255
     * 是否为调试模式
256
     * @access public
257
     * @return bool
258
     */
259 3
    public function isDebug(): bool
260
    {
261 3
        return $this->appDebug;
262
    }
263
264
    /**
265
     * 设置应用命名空间
266
     * @access public
267
     * @param string $namespace 应用命名空间
268
     * @return $this
269
     */
270 6
    public function setNamespace(string $namespace)
271
    {
272 6
        $this->namespace = $namespace;
273 6
        return $this;
274
    }
275
276
    /**
277
     * 获取应用类库命名空间
278
     * @access public
279
     * @return string
280
     */
281 3
    public function getNamespace(): string
282
    {
283 3
        return $this->namespace;
284
    }
285
286
    /**
287
     * 设置环境变量标识
288
     * @access public
289
     * @param string $name 环境标识
290
     * @return $this
291
     */
292
    public function setEnvName(string $name)
293
    {
294
        $this->envName = $name;
295
        return $this;
296
    }
297
298
    /**
299
     * 获取框架版本
300
     * @access public
301
     * @return string
302
     */
303 3
    public function version(): string
304
    {
305 3
        return static::VERSION;
306
    }
307
308
    /**
309
     * 获取应用根目录
310
     * @access public
311
     * @return string
312
     */
313 12
    public function getRootPath(): string
314
    {
315 12
        return $this->rootPath;
316
    }
317
318
    /**
319
     * 获取应用基础目录
320
     * @access public
321
     * @return string
322
     */
323 3
    public function getBasePath(): string
324
    {
325 3
        return $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
326
    }
327
328
    /**
329
     * 获取当前应用目录
330
     * @access public
331
     * @return string
332
     */
333 6
    public function getAppPath(): string
334
    {
335 6
        return $this->appPath;
336
    }
337
338
    /**
339
     * 设置应用目录
340
     * @param string $path 应用目录
341
     */
342 3
    public function setAppPath(string $path)
343
    {
344 3
        $this->appPath = $path;
345 3
    }
346
347
    /**
348
     * 获取应用运行时目录
349
     * @access public
350
     * @return string
351
     */
352 42
    public function getRuntimePath(): string
353
    {
354 42
        return $this->runtimePath;
355
    }
356
357
    /**
358
     * 设置runtime目录
359
     * @param string $path 定义目录
360
     */
361 3
    public function setRuntimePath(string $path): void
362
    {
363 3
        $this->runtimePath = $path;
364 3
    }
365
366
    /**
367
     * 获取核心框架目录
368
     * @access public
369
     * @return string
370
     */
371 3
    public function getThinkPath(): string
372
    {
373 3
        return $this->thinkPath;
374
    }
375
376
    /**
377
     * 获取应用配置目录
378
     * @access public
379
     * @return string
380
     */
381 42
    public function getConfigPath(): string
382
    {
383 42
        return $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
384
    }
385
386
    /**
387
     * 获取配置后缀
388
     * @access public
389
     * @return string
390
     */
391 39
    public function getConfigExt(): string
392
    {
393 39
        return $this->configExt;
394
    }
395
396
    /**
397
     * 获取应用开启时间
398
     * @access public
399
     * @return float
400
     */
401 3
    public function getBeginTime(): float
402
    {
403 3
        return $this->beginTime;
404
    }
405
406
    /**
407
     * 获取应用初始内存占用
408
     * @access public
409
     * @return integer
410
     */
411 3
    public function getBeginMem(): int
412
    {
413 3
        return $this->beginMem;
414
    }
415
416
    /**
417
     * 初始化应用
418
     * @access public
419
     * @return $this
420
     */
421 3
    public function initialize()
422
    {
423 3
        $this->initialized = true;
424
425 3
        $this->beginTime = microtime(true);
0 ignored issues
show
Documentation Bug introduced by
It seems like microtime(true) can also be of type string. However, the property $beginTime is declared as type double. 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...
426 3
        $this->beginMem  = memory_get_usage();
427
428
        // 加载环境变量
429 3
        if (is_file($this->rootPath . $this->envName . '.env')) {
430 3
            $this->env->load($this->rootPath . $this->envName . '.env');
431
        }
432
433 3
        $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...
434
435 3
        $this->debugModeInit();
436
437
        // 加载全局初始化文件
438 3
        $this->load();
439
440
        // 加载框架默认语言包
441 3
        $langSet = $this->lang->defaultLangSet();
442
443 3
        $this->lang->load($this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $langSet . '.php');
444
445
        // 加载应用默认语言包
446 3
        $this->loadLangPack($langSet);
447
448
        // 监听AppInit
449 3
        $this->event->trigger(AppInit::class);
450
451 3
        date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai'));
0 ignored issues
show
Bug introduced by
It seems like $this->config->get('app....zone', 'Asia/Shanghai') can also be of type array; however, parameter $timezoneId 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

451
        date_default_timezone_set(/** @scrutinizer ignore-type */ $this->config->get('app.default_timezone', 'Asia/Shanghai'));
Loading history...
452
453
        // 初始化
454 3
        foreach ($this->initializers as $initializer) {
455 3
            $this->make($initializer)->init($this);
456
        }
457
458 3
        return $this;
459
    }
460
461
    /**
462
     * 是否初始化过
463
     * @return bool
464
     */
465 6
    public function initialized()
466
    {
467 6
        return $this->initialized;
468
    }
469
470
    /**
471
     * 加载语言包
472
     * @param string $langset 语言
473
     * @return void
474
     */
475 3
    public function loadLangPack($langset)
476
    {
477 3
        if (empty($langset)) {
478
            return;
479
        }
480
481
        // 加载系统语言包
482 3
        $files = glob($this->appPath . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*');
483 3
        $this->lang->load($files);
484
485
        // 加载扩展(自定义)语言包
486 3
        $list = $this->config->get('lang.extend_list', []);
487
488 3
        if (isset($list[$langset])) {
489
            $this->lang->load($list[$langset]);
490
        }
491 3
    }
492
493
    /**
494
     * 引导应用
495
     * @access public
496
     * @return void
497
     */
498 3
    public function boot(): void
499
    {
500
        array_walk($this->services, function ($service) {
501 3
            $this->bootService($service);
502 3
        });
503 3
    }
504
505
    /**
506
     * 加载应用文件和配置
507
     * @access protected
508
     * @return void
509
     */
510 3
    protected function load(): void
511
    {
512 3
        $appPath = $this->getAppPath();
513
514 3
        if (is_file($appPath . 'common.php')) {
515 3
            include_once $appPath . 'common.php';
516
        }
517
518 3
        include_once $this->thinkPath . 'helper.php';
519
520 3
        $configPath = $this->getConfigPath();
521
522 3
        $files = [];
523
524 3
        if (is_dir($configPath)) {
525 3
            $files = glob($configPath . '*' . $this->configExt);
526
        }
527
528 3
        foreach ($files as $file) {
529
            $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
0 ignored issues
show
Bug introduced by
It seems like pathinfo($file, think\PATHINFO_FILENAME) can also be of type array; however, parameter $name of think\Config::load() 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

529
            $this->config->load($file, /** @scrutinizer ignore-type */ pathinfo($file, PATHINFO_FILENAME));
Loading history...
530
        }
531
532 3
        if (is_file($appPath . 'event.php')) {
533 3
            $this->loadEvent(include $appPath . 'event.php');
534
        }
535
536 3
        if (is_file($appPath . 'service.php')) {
537
            $services = include $appPath . 'service.php';
538
            foreach ($services as $service) {
539
                $this->register($service);
540
            }
541
        }
542 3
    }
543
544
    /**
545
     * 调试模式设置
546
     * @access protected
547
     * @return void
548
     */
549 3
    protected function debugModeInit(): void
550
    {
551
        // 应用调试模式
552 3
        if (!$this->appDebug) {
553 3
            $this->appDebug = $this->env->get('app_debug') ? true : false;
554 3
            ini_set('display_errors', 'Off');
555
        }
556
557 3
        if (!$this->runningInConsole()) {
558
            //重新申请一块比较大的buffer
559
            if (ob_get_level() > 0) {
560
                $output = ob_get_clean();
561
            }
562
            ob_start();
563
            if (!empty($output)) {
564
                echo $output;
565
            }
566
        }
567 3
    }
568
569
    /**
570
     * 注册应用事件
571
     * @access protected
572
     * @param array $event 事件数据
573
     * @return void
574
     */
575 3
    public function loadEvent(array $event): void
576
    {
577 3
        if (isset($event['bind'])) {
578 3
            $this->event->bind($event['bind']);
579
        }
580
581 3
        if (isset($event['listen'])) {
582 3
            $this->event->listenEvents($event['listen']);
583
        }
584
585 3
        if (isset($event['subscribe'])) {
586 3
            $this->event->subscribe($event['subscribe']);
587
        }
588 3
    }
589
590
    /**
591
     * 解析应用类的类名
592
     * @access public
593
     * @param string $layer 层名 controller model ...
594
     * @param string $name  类名
595
     * @return string
596
     */
597 6
    public function parseClass(string $layer, string $name): string
598
    {
599 6
        $name  = str_replace(['/', '.'], '\\', $name);
600 6
        $array = explode('\\', $name);
601 6
        $class = Str::studly(array_pop($array));
602 6
        $path  = $array ? implode('\\', $array) . '\\' : '';
603
604 6
        return $this->namespace . '\\' . $layer . '\\' . $path . $class;
605
    }
606
607
    /**
608
     * 是否运行在命令行下
609
     * @return bool
610
     */
611 3
    public function runningInConsole()
612
    {
613 3
        return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';
614
    }
615
616
    /**
617
     * 获取应用根目录
618
     * @access protected
619
     * @return string
620
     */
621 30
    protected function getDefaultRootPath(): string
622
    {
623 30
        return dirname($this->thinkPath, 4) . DIRECTORY_SEPARATOR;
624
    }
625
626
}
627