App   F
last analyzed

Complexity

Total Complexity 60

Size/Duplication

Total Lines 580
Duplicated Lines 0 %

Test Coverage

Coverage 90.6%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 147
dl 0
loc 580
ccs 135
cts 149
cp 0.906
rs 3.6
c 4
b 0
f 0
wmc 60

32 Methods

Rating   Name   Duplication   Size   Complexity  
A debug() 0 4 1
A getBeginMem() 0 3 1
A getConfigExt() 0 3 1
A setRuntimePath() 0 3 1
A getAppPath() 0 3 1
A bootService() 0 4 2
A register() 0 21 6
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 getBasePath() 0 3 1
A setAppPath() 0 3 1
A getBeginTime() 0 3 1
A version() 0 3 1
A getRootPath() 0 3 1
A setNamespace() 0 4 1
A setEnvName() 0 4 1
A getService() 0 6 2
A loadEnv() 0 7 3
A runningInConsole() 0 3 2
A initialized() 0 3 1
A initialize() 0 30 2
A boot() 0 4 1
A parseClass() 0 8 2
B load() 0 30 7
A loadEvent() 0 12 4
A loadLangPack() 0 5 1
A getDefaultRootPath() 0 3 1
A debugModeInit() 0 16 6

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.15LTS';
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   = realpath(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
     * @param string $envName 环境标识
420
     * @return void
421
     */
422 3
    public function loadEnv(string $envName = ''): void
423
    {
424
        // 加载环境变量
425 3
        $envFile = $envName ? $this->rootPath . '.env.' . $envName : $this->rootPath . '.env';
426
427 3
        if (is_file($envFile)) {
428 3
            $this->env->load($envFile);
429
        }
430 3
    }
431
432
    /**
433
     * 初始化应用
434
     * @access public
435
     * @return $this
436
     */
437 3
    public function initialize()
438
    {
439 3
        $this->initialized = true;
440
441 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...
442 3
        $this->beginMem  = memory_get_usage();
443
444 3
        $this->loadEnv($this->envName);
445
446 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...
447
448 3
        $this->debugModeInit();
449
450
        // 加载全局初始化文件
451 3
        $this->load();
452
453
        // 加载应用默认语言包
454 3
        $this->loadLangPack();
455
456 3
        // 监听AppInit
457
        $this->event->trigger(AppInit::class);
458
459 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

459
        date_default_timezone_set(/** @scrutinizer ignore-type */ $this->config->get('app.default_timezone', 'Asia/Shanghai'));
Loading history...
460
461
        // 初始化
462 3
        foreach ($this->initializers as $initializer) {
463
            $this->make($initializer)->init($this);
464 3
        }
465
466
        return $this;
467 3
    }
468 3
469
    /**
470
     * 是否初始化过
471 3
     * @return bool
472
     */
473
    public function initialized()
474
    {
475
        return $this->initialized;
476
    }
477
478 6
    /**
479
     * 加载语言包
480 6
     * @return void
481
     */
482
    public function loadLangPack()
483
    {
484
        // 加载默认语言包
485
        $langSet = $this->lang->defaultLangSet();
486
        $this->lang->switchLangSet($langSet);
487
    }
488 3
489
    /**
490 3
     * 引导应用
491
     * @access public
492
     * @return void
493
     */
494
    public function boot(): void
495 3
    {
496 3
        array_walk($this->services, function ($service) {
497
            $this->bootService($service);
498
        });
499 3
    }
500
501 3
    /**
502
     * 加载应用文件和配置
503
     * @access protected
504 3
     * @return void
505
     */
506
    protected function load(): void
507
    {
508
        $appPath = $this->getAppPath();
509
510
        if (is_file($appPath . 'common.php')) {
511 3
            include_once $appPath . 'common.php';
512
        }
513
514 3
        include_once $this->thinkPath . 'helper.php';
515 3
516 3
        $configPath = $this->getConfigPath();
517
518
        $files = [];
519
520
        if (is_dir($configPath)) {
521
            $files = glob($configPath . '*' . $this->configExt);
522
        }
523 3
524
        foreach ($files as $file) {
525 3
            $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

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