Passed
Pull Request — 8.0 (#3048)
by
unknown
02:16
created

App::boot()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 3
cts 3
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~2023 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 Composer\InstalledVersions;
16
use think\event\AppInit;
17
use think\helper\Str;
18
use think\initializer\BootService;
19
use think\initializer\Error;
20
use think\initializer\RegisterService;
21
22
/**
23
 * App 基础类
24
 * @property Route      $route
25
 * @property Config     $config
26
 * @property Cache      $cache
27
 * @property Request    $request
28
 * @property Http       $http
29
 * @property Console    $console
30
 * @property Env        $env
31
 * @property Event      $event
32
 * @property Middleware $middleware
33
 * @property Log        $log
34
 * @property Lang       $lang
35
 * @property Db         $db
36
 * @property Cookie     $cookie
37
 * @property Session    $session
38
 * @property Validate   $validate
39
 */
40
class App extends Container
41
{
42
    const VERSION = '8.0.0';
43
44
    /**
45
     * 应用调试模式
46
     * @var bool
47
     */
48
    protected bool $appDebug = false;
49
50
    /**
51
     * 公共环境变量标识
52
     * @var string
53
     */
54
    protected string $baseEnvName = '';
55
56
    /**
57
     * 环境变量标识
58
     * @var string
59
     */
60
    protected string $envName = '';
61
62
    /**
63
     * 应用开始时间
64
     * @var float
65
     */
66
    protected float $beginTime;
67
68
    /**
69
     * 应用内存初始占用
70
     * @var integer
71
     */
72
    protected int $beginMem;
73
74
    /**
75
     * 当前应用类库命名空间
76
     * @var string
77
     */
78
    protected string $namespace = 'app';
79
80
    /**
81
     * 应用根目录
82
     * @var string
83
     */
84
    protected string $rootPath = '';
85
86
    /**
87
     * 框架目录
88
     * @var string
89
     */
90
    protected string $thinkPath = '';
91
92
    /**
93
     * 应用目录
94
     * @var string
95
     */
96
    protected string $appPath = '';
97
98
    /**
99
     * Runtime目录
100
     * @var string
101
     */
102
    protected string $runtimePath = '';
103
104
    /**
105
     * 路由定义目录
106
     * @var string
107
     */
108
    protected string $routePath = '';
109
110
    /**
111
     * 配置后缀
112
     * @var string
113
     */
114
    protected string $configExt = '.php';
115
116
    /**
117
     * 应用初始化器
118
     * @var array
119
     */
120
    protected array $initializers = [
121
        Error::class,
122
        RegisterService::class,
123
        BootService::class,
124
    ];
125
126
    /**
127
     * 注册的系统服务
128
     * @var array
129
     */
130
    protected array $services = [];
131
132
    /**
133
     * 初始化
134
     * @var bool
135
     */
136
    protected bool $initialized = false;
137
138
    /**
139
     * 容器绑定标识
140
     * @var array
141
     */
142
    protected $bind = [
143
        'app'                     => App::class,
144
        'cache'                   => Cache::class,
145
        'config'                  => Config::class,
146
        'console'                 => Console::class,
147
        'cookie'                  => Cookie::class,
148
        'db'                      => Db::class,
149
        'env'                     => Env::class,
150
        'event'                   => Event::class,
151
        'http'                    => Http::class,
152
        'lang'                    => Lang::class,
153
        'log'                     => Log::class,
154
        'middleware'              => Middleware::class,
155
        'request'                 => Request::class,
156
        'response'                => Response::class,
157
        'route'                   => Route::class,
158
        'session'                 => Session::class,
159
        'validate'                => Validate::class,
160
        'view'                    => View::class,
161
        'think\DbManager'         => Db::class,
162
        'think\LogManager'        => Log::class,
163
        'think\CacheManager'      => Cache::class,
164
165
        // 接口依赖注入
166
        'Psr\Log\LoggerInterface' => Log::class,
167
    ];
168
169
    /**
170
     * 架构方法
171
     * @access public
172
     * @param string $rootPath 应用根目录
173
     */
174 39
    public function __construct(string $rootPath = '')
175
    {
176 39
        $this->thinkPath   = realpath(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
177 39
        $this->rootPath    = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
178 39
        $this->appPath     = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
179 39
        $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
180
181 39
        if (is_file($this->appPath . 'provider.php')) {
182 3
            $this->bind(include $this->appPath . 'provider.php');
183
        }
184
185 39
        static::setInstance($this);
186
187 39
        $this->instance('app', $this);
188 39
        $this->instance('think\Container', $this);
189
    }
190
191
    /**
192
     * 注册服务
193
     * @access public
194
     * @param Service|string $service 服务
195
     * @param bool           $force   强制重新注册
196
     * @return Service|null
197
     */
198 3
    public function register(Service | string $service, bool $force = false): ?Service
199
    {
200 3
        $registered = $this->getService($service);
201
202 3
        if ($registered && !$force) {
203 3
            return $registered;
204
        }
205
206 3
        if (is_string($service)) {
207
            $service = new $service($this);
208
        }
209
210 3
        if (method_exists($service, 'register')) {
211 3
            $service->register();
212
        }
213
214 3
        if (property_exists($service, 'bind')) {
215 3
            $this->bind($service->bind);
216
        }
217
218 3
        $this->services[] = $service;
219
220 3
        return null;
221
    }
222
223
    /**
224
     * 执行服务
225
     * @access public
226
     * @param Service $service 服务
227
     */
228 3
    public function bootService(Service $service): ?Service
229
    {
230 3
        if (method_exists($service, 'boot')) {
231 3
            return $this->invoke([$service, 'boot']);
232
        }
233
234
        return null;
235
    }
236
237
    /**
238
     * 获取服务
239
     * @param string|Service $service
240
     * @return Service|null
241
     */
242 3
    public function getService(Service | string $service): ?Service
243
    {
244 3
        $name = is_string($service) ? $service : $service::class;
245 3
        return array_values(array_filter($this->services, function ($value) use ($name) {
246 3
            return $value instanceof $name;
247 3
        }, ARRAY_FILTER_USE_BOTH))[0] ?? null;
248
    }
249
250
    /**
251
     * 开启应用调试模式
252
     * @access public
253
     * @param bool $debug 开启应用调试模式
254
     * @return $this
255
     */
256 6
    public function debug(bool $debug = true): static
257
    {
258 6
        $this->appDebug = $debug;
259 6
        return $this;
260
    }
261
262
    /**
263
     * 是否为调试模式
264
     * @access public
265
     * @return bool
266
     */
267 3
    public function isDebug(): bool
268
    {
269 3
        return $this->appDebug;
270
    }
271
272
    /**
273
     * 设置应用命名空间
274
     * @access public
275
     * @param string $namespace 应用命名空间
276
     * @return $this
277
     */
278 6
    public function setNamespace(string $namespace): static
279
    {
280 6
        $this->namespace = $namespace;
281 6
        return $this;
282
    }
283
284
    /**
285
     * 获取应用类库命名空间
286
     * @access public
287
     * @return string
288
     */
289 3
    public function getNamespace(): string
290
    {
291 3
        return $this->namespace;
292
    }
293
294
    /**
295
     * 设置公共环境变量标识
296
     * @access public
297
     * @param string $name 环境标识
298
     * @return $this
299
     */
300 3
    public function setBaseEnvName(string $name): static
301
    {
302 3
        $this->baseEnvName = $name;
303 3
        return $this;
304
    }
305
306
    /**
307
     * 获取公共环境变量标识
308
     * @access public
309
     * @return string
310
     */
311 6
    public function getBaseEnvName(): string
312
    {
313 6
        return $this->baseEnvName;
314
    }
315
316
    /**
317
     * 设置环境变量标识
318
     * @access public
319
     * @param string $name 环境标识
320
     * @return $this
321
     */
322 3
    public function setEnvName(string $name): static
323
    {
324 3
        $this->envName = $name;
325 3
        return $this;
326
    }
327
328
    /**
329
     * 获取环境变量标识
330
     * @access public
331
     * @return string
332
     */
333 6
    public function getEnvName(): string
334
    {
335 6
        $envName = $this->env->get('env_name', '');
336 6
        return $this->envName ?? (string) $envName;
337
    }
338
339
    /**
340
     * 获取框架版本
341
     * @access public
342
     * @return string
343
     */
344
    public function version(): string
345
    {
346
        return ltrim(InstalledVersions::getPrettyVersion('topthink/framework'), 'v');
0 ignored issues
show
Bug introduced by
It seems like Composer\InstalledVersio...n('topthink/framework') can also be of type null; however, parameter $string of ltrim() 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

346
        return ltrim(/** @scrutinizer ignore-type */ InstalledVersions::getPrettyVersion('topthink/framework'), 'v');
Loading history...
347
    }
348
349
    /**
350
     * 获取应用根目录
351
     * @access public
352
     * @return string
353
     */
354 12
    public function getRootPath(): string
355
    {
356 12
        return $this->rootPath;
357
    }
358
359
    /**
360
     * 获取应用基础目录
361
     * @access public
362
     * @return string
363
     */
364 3
    public function getBasePath(): string
365
    {
366 3
        return $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
367
    }
368
369
    /**
370
     * 获取当前应用目录
371
     * @access public
372
     * @return string
373
     */
374 6
    public function getAppPath(): string
375
    {
376 6
        return $this->appPath;
377
    }
378
379
    /**
380
     * 设置应用目录
381
     * @param string $path 应用目录
382
     */
383 3
    public function setAppPath(string $path): void
384
    {
385 3
        $this->appPath = $path;
386
    }
387
388
    /**
389
     * 获取应用运行时目录
390
     * @access public
391
     * @return string
392
     */
393 36
    public function getRuntimePath(): string
394
    {
395 36
        return $this->runtimePath;
396
    }
397
398
    /**
399
     * 设置runtime目录
400
     * @param string $path 定义目录
401
     */
402 3
    public function setRuntimePath(string $path): void
403
    {
404 3
        $this->runtimePath = $path;
405
    }
406
407
    /**
408
     * 获取核心框架目录
409
     * @access public
410
     * @return string
411
     */
412 6
    public function getThinkPath(): string
413
    {
414 6
        return $this->thinkPath;
415
    }
416
417
    /**
418
     * 获取应用配置目录
419
     * @access public
420
     * @return string
421
     */
422 39
    public function getConfigPath(): string
423
    {
424 39
        return $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
425
    }
426
427
    /**
428
     * 获取配置后缀
429
     * @access public
430
     * @return string
431
     */
432 36
    public function getConfigExt(): string
433
    {
434 36
        return $this->configExt;
435
    }
436
437
    /**
438
     * 获取应用开启时间
439
     * @access public
440
     * @return float
441
     */
442 6
    public function getBeginTime(): float
443
    {
444 6
        return $this->beginTime;
445
    }
446
447
    /**
448
     * 设置应用开启时间
449
     * @access public
450
     * @param float $beginTime
451
     * @return $this
452
     */
453 6
    public function setBeginTime(float $beginTime): static
454
    {
455 6
        $this->beginTime = $beginTime;
456 6
        return $this;
457
    }
458
459
    /**
460
     * 获取应用初始内存占用
461
     * @access public
462
     * @return integer
463
     */
464 3
    public function getBeginMem(): int
465
    {
466 3
        return $this->beginMem;
467
    }
468
469
    /**
470
     * 设置应用初始内存占用
471
     * @access public
472
     * @return $this
473
     */
474 3
    public function setBeginMem(int $beginMem): static
475
    {
476 3
        $this->beginMem = $beginMem;
477 3
        return $this;
478
    }
479
480
    /**
481
     * 加载环境变量定义
482
     * @access public
483
     * @param string $envName 环境标识
484
     * @return void
485
     */
486 3
    public function loadEnv(string $envName = ''): void
487
    {
488
        // 加载环境变量
489 3
        $envFile = $envName ? $this->rootPath . '.env.' . $envName : $this->rootPath . '.env';
490
491 3
        if (is_file($envFile)) {
492 3
            $this->env->load($envFile);
493
        }
494
    }
495
496
    /**
497
     * 初始化应用
498
     * @access public
499
     * @return $this
500
     */
501 3
    public function initialize(): static
502
    {
503 3
        $this->initialized = true;
504
505
        // 设置应用开启时间
506 3
        $this->setBeginTime(microtime(true))
0 ignored issues
show
Bug introduced by
It seems like microtime(true) can also be of type string; however, parameter $beginTime of think\App::setBeginTime() does only seem to accept double, 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

506
        $this->setBeginTime(/** @scrutinizer ignore-type */ microtime(true))
Loading history...
507 3
            ->setBeginMem(memory_get_usage());
508
509
        // 加载环境变量
510 3
        $baseEnvName = $this->getBaseEnvName();
511 3
        if ($baseEnvName) {
512
            $this->loadEnv($baseEnvName);
513
        }
514
515 3
        $envName = $this->getEnvName();
516 3
        $this->loadEnv($envName);
517
518 3
        $this->configExt = $this->env->get('config_ext', '.php');
519
520 3
        $this->debugModeInit();
521
522
        // 加载全局初始化文件
523 3
        $this->load();
524
525
        // 加载应用默认语言包
526 3
        $this->loadLangPack();
527
528
        // 监听AppInit
529 3
        $this->event->trigger(AppInit::class);
530
531 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

531
        date_default_timezone_set(/** @scrutinizer ignore-type */ $this->config->get('app.default_timezone', 'Asia/Shanghai'));
Loading history...
532
533
        // 初始化
534 3
        foreach ($this->initializers as $initializer) {
535 3
            $this->make($initializer)->init($this);
536
        }
537
538 3
        return $this;
539
    }
540
541
    /**
542
     * 是否初始化过
543
     * @return bool
544
     */
545 6
    public function initialized(): bool
546
    {
547 6
        return $this->initialized;
548
    }
549
550
    /**
551
     * 加载语言包
552
     * @return void
553
     */
554 3
    public function loadLangPack(): void
555
    {
556
        // 加载默认语言包
557 3
        $langSet = $this->lang->defaultLangSet();
558 3
        $this->lang->switchLangSet($langSet);
559
    }
560
561
    /**
562
     * 引导应用
563
     * @access public
564
     * @return void
565
     */
566 3
    public function boot(): void
567
    {
568 3
        array_walk($this->services, function ($service) {
569 3
            $this->bootService($service);
570 3
        });
571
    }
572
573
    /**
574
     * 加载应用文件和配置
575
     * @access protected
576
     * @return void
577
     */
578 3
    protected function load(): void
579
    {
580 3
        $appPath = $this->getAppPath();
581
582 3
        if (is_file($appPath . 'common.php')) {
583 3
            include_once $appPath . 'common.php';
584
        }
585
586 3
        include_once $this->thinkPath . 'helper.php';
587
588 3
        $configPath = $this->getConfigPath();
589
590 3
        $files = [];
591
592 3
        if (is_dir($configPath)) {
593 3
            $files = glob($configPath . '*' . $this->configExt);
594
        }
595
596 3
        foreach ($files as $file) {
597
            $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

597
            $this->config->load($file, /** @scrutinizer ignore-type */ pathinfo($file, PATHINFO_FILENAME));
Loading history...
598
        }
599
600 3
        if (is_file($appPath . 'event.php')) {
601 3
            $this->loadEvent(include $appPath . 'event.php');
602
        }
603
604 3
        if (is_file($appPath . 'service.php')) {
605
            $services = include $appPath . 'service.php';
606
            foreach ($services as $service) {
607
                $this->register($service);
608
            }
609
        }
610
    }
611
612
    /**
613
     * 调试模式设置
614
     * @access protected
615
     * @return void
616
     */
617 3
    protected function debugModeInit(): void
618
    {
619
        // 应用调试模式
620 3
        if (!$this->appDebug) {
621 3
            $this->appDebug = (bool) $this->env->get('app_debug');
622
        }
623
624 3
        if (!$this->appDebug) {
625
            ini_set('display_errors', 'Off');
626
        }
627
628 3
        if (!$this->runningInConsole()) {
629
            //重新申请一块比较大的buffer
630
            if (ob_get_level() > 0) {
631
                $output = ob_get_clean();
632
            }
633
            ob_start();
634
            if (!empty($output)) {
635
                echo $output;
636
            }
637
        }
638
    }
639
640
    /**
641
     * 注册应用事件
642
     * @access protected
643
     * @param array $event 事件数据
644
     * @return void
645
     */
646 3
    public function loadEvent(array $event): void
647
    {
648 3
        if (isset($event['bind'])) {
649 3
            $this->event->bind($event['bind']);
650
        }
651
652 3
        if (isset($event['listen'])) {
653 3
            $this->event->listenEvents($event['listen']);
654
        }
655
656 3
        if (isset($event['subscribe'])) {
657 3
            $this->event->subscribe($event['subscribe']);
658
        }
659
    }
660
661
    /**
662
     * 解析应用类的类名
663
     * @access public
664
     * @param string $layer 层名 controller model ...
665
     * @param string $name  类名
666
     * @return string
667
     */
668 6
    public function parseClass(string $layer, string $name): string
669
    {
670 6
        $name  = str_replace(['/', '.'], '\\', $name);
671 6
        $array = explode('\\', $name);
672 6
        $class = Str::studly(array_pop($array));
673 6
        $path  = $array ? implode('\\', $array) . '\\' : '';
674
675 6
        return $this->namespace . '\\' . $layer . '\\' . $path . $class;
676
    }
677
678
    /**
679
     * 是否运行在命令行下
680
     * @return bool
681
     */
682 3
    public function runningInConsole(): bool
683
    {
684 3
        return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';
685
    }
686
687
    /**
688
     * 获取应用根目录
689
     * @access protected
690
     * @return string
691
     */
692 39
    protected function getDefaultRootPath(): string
693
    {
694 39
        return dirname($this->thinkPath, 4) . DIRECTORY_SEPARATOR;
695
    }
696
}
697