Completed
Push — 6.0 ( d99ed4...2801f0 )
by liu
08:15
created

App::bootService()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

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

427
        date_default_timezone_set(/** @scrutinizer ignore-type */ $this->config->get('app.default_timezone', 'Asia/Shanghai'));
Loading history...
428
429
        // 初始化
430 1
        foreach ($this->initializers as $initializer) {
431 1
            $this->make($initializer)->init($this);
432
        }
433
434 1
        return $this;
435
    }
436
437
    /**
438
     * 是否初始化过
439
     * @return bool
440
     */
441 1
    public function initialized()
442
    {
443 1
        return $this->initialized;
444
    }
445
446
    /**
447
     * 加载语言包
448
     * @param string $langset 语言
449
     * @return void
450
     */
451 6
    public function loadLangPack($langset)
452
    {
453 6
        if (empty($langset)) {
454
            return;
455
        }
456
457
        // 加载系统语言包
458 6
        $files = glob($this->appPath . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*');
459 6
        $this->lang->load($files);
0 ignored issues
show
Bug introduced by
It seems like $files can also be of type false; however, parameter $file of think\Lang::load() does only seem to accept array|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
        $this->lang->load(/** @scrutinizer ignore-type */ $files);
Loading history...
460
461
        // 加载扩展(自定义)语言包
462 6
        $list = $this->config->get('lang.extend_list', []);
463
464 6
        if (isset($list[$langset])) {
465
            $this->lang->load($list[$langset]);
466
        }
467 6
    }
468
469
    /**
470
     * 引导应用
471
     * @access public
472
     * @return void
473
     */
474 1
    public function boot(): void
475
    {
476
        array_walk($this->services, function ($service) {
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...
477 1
            $this->bootService($service);
478 1
        });
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...
479 1
    }
480
481
    /**
482
     * 加载应用文件和配置
483
     * @access protected
484
     * @return void
485
     */
486 1
    protected function load(): void
487
    {
488 1
        $appPath = $this->getAppPath();
489
490 1
        if (is_file($appPath . 'common.php')) {
491 1
            include_once $appPath . 'common.php';
492
        }
493
494 1
        include_once $this->thinkPath . 'helper.php';
495
496 1
        $configPath = $this->getConfigPath();
497
498 1
        $files = [];
499
500 1
        if (is_dir($configPath)) {
501 1
            $files = glob($configPath . '*' . $this->configExt);
502
        }
503
504 1
        foreach ($files as $file) {
505
            $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
506
        }
507
508 1
        if (is_file($appPath . 'event.php')) {
509 1
            $this->loadEvent(include $appPath . 'event.php');
510
        }
511
512 1
        if (is_file($appPath . 'service.php')) {
513
            $services = include $appPath . 'service.php';
514
            foreach ($services as $service) {
515
                $this->register($service);
516
            }
517
        }
518 1
    }
519
520
    /**
521
     * 调试模式设置
522
     * @access protected
523
     * @return void
524
     */
525 1
    protected function debugModeInit(): void
526
    {
527
        // 应用调试模式
528 1
        if (!$this->appDebug) {
529 1
            $this->appDebug = $this->env->get('app_debug') ? true : false;
530 1
            ini_set('display_errors', 'Off');
531
        }
532
533 1
        if (!$this->runningInConsole()) {
534
            //重新申请一块比较大的buffer
535
            if (ob_get_level() > 0) {
536
                $output = ob_get_clean();
537
            }
538
            ob_start();
539
            if (!empty($output)) {
540
                echo $output;
541
            }
542
        }
543 1
    }
544
545
    /**
546
     * 注册应用事件
547
     * @access protected
548
     * @param array $event 事件数据
549
     * @return void
550
     */
551 2
    public function loadEvent(array $event): void
552
    {
553 2
        if (isset($event['bind'])) {
554 2
            $this->event->bind($event['bind']);
555
        }
556
557 2
        if (isset($event['listen'])) {
558 2
            $this->event->listenEvents($event['listen']);
559
        }
560
561 2
        if (isset($event['subscribe'])) {
562 2
            $this->event->subscribe($event['subscribe']);
563
        }
564 2
    }
565
566
    /**
567
     * 解析应用类的类名
568
     * @access public
569
     * @param string $layer 层名 controller model ...
570
     * @param string $name  类名
571
     * @return string
572
     */
573 1
    public function parseClass(string $layer, string $name): string
574
    {
575 1
        $name  = str_replace(['/', '.'], '\\', $name);
576 1
        $array = explode('\\', $name);
577 1
        $class = Str::studly(array_pop($array));
578 1
        $path  = $array ? implode('\\', $array) . '\\' : '';
579
580 1
        return $this->namespace . '\\' . $layer . '\\' . $path . $class;
581
    }
582
583
    /**
584
     * 是否运行在命令行下
585
     * @return bool
586
     */
587 1
    public function runningInConsole()
588
    {
589 1
        return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';
590
    }
591
592
    /**
593
     * 获取应用根目录
594
     * @access protected
595
     * @return string
596
     */
597 17
    protected function getDefaultRootPath(): string
598
    {
599 17
        $path = dirname(dirname(dirname(dirname($this->thinkPath))));
600
601 17
        return $path . DIRECTORY_SEPARATOR;
602
    }
603
604
}
605