Completed
Push — 6.0 ( c13f16...4fc9fc )
by liu
05:12
created

App::loadLangPack()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.1406

Importance

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

426
        date_default_timezone_set(/** @scrutinizer ignore-type */ $this->config->get('app.default_timezone', 'Asia/Shanghai'));
Loading history...
427
428
        // 初始化
429 1
        foreach ($this->initializers as $initializer) {
430 1
            $this->make($initializer)->init($this);
431
        }
432
433 1
        return $this;
434
    }
435
436
    /**
437
     * 是否初始化过
438
     * @return bool
439
     */
440 1
    public function initialized()
441
    {
442 1
        return $this->initialized;
443
    }
444
445
    /**
446
     * 加载语言包
447
     * @param string $langset 语言
448
     * @return void
449
     */
450 6
    public function loadLangPack($langset)
451
    {
452 6
        if (empty($langset)) {
453
            return;
454
        }
455
456
        // 加载系统语言包
457 6
        $files = glob($this->appPath . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*');
458 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

458
        $this->lang->load(/** @scrutinizer ignore-type */ $files);
Loading history...
459
460
        // 加载扩展(自定义)语言包
461 6
        $list = $this->config->get('lang.extend_list', []);
462
463 6
        if (isset($list[$langset])) {
464
            $this->lang->load($list[$langset]);
465
        }
466 6
    }
467
468
    /**
469
     * 引导应用
470
     * @access public
471
     * @return void
472
     */
473 1
    public function boot(): void
474
    {
475
        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...
476 1
            $this->bootService($service);
477 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...
478 1
    }
479
480
    /**
481
     * 加载应用文件和配置
482
     * @access protected
483
     * @return void
484
     */
485 1
    protected function load(): void
486
    {
487 1
        $appPath = $this->getAppPath();
488
489 1
        if (is_file($appPath . 'common.php')) {
490 1
            include_once $appPath . 'common.php';
491
        }
492
493 1
        include_once $this->thinkPath . 'helper.php';
494
495 1
        $configPath = $this->getConfigPath();
496
497 1
        $files = [];
498
499 1
        if (is_dir($configPath)) {
500 1
            $files = glob($configPath . '*' . $this->configExt);
501
        }
502
503 1
        foreach ($files as $file) {
504
            $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
505
        }
506
507 1
        if (is_file($appPath . 'event.php')) {
508 1
            $this->loadEvent(include $appPath . 'event.php');
509
        }
510
511 1
        if (is_file($appPath . 'service.php')) {
512
            $services = include $appPath . 'service.php';
513
            foreach ($services as $service) {
514
                $this->register($service);
515
            }
516
        }
517 1
    }
518
519
    /**
520
     * 调试模式设置
521
     * @access protected
522
     * @return void
523
     */
524 1
    protected function debugModeInit(): void
525
    {
526
        // 应用调试模式
527 1
        if (!$this->appDebug) {
528 1
            $this->appDebug = $this->env->get('app_debug') ? true : false;
529 1
            ini_set('display_errors', 'Off');
530
        }
531
532 1
        if (!$this->runningInConsole()) {
533
            //重新申请一块比较大的buffer
534
            if (ob_get_level() > 0) {
535
                $output = ob_get_clean();
536
            }
537
            ob_start();
538
            if (!empty($output)) {
539
                echo $output;
540
            }
541
        }
542 1
    }
543
544
    /**
545
     * 注册应用事件
546
     * @access protected
547
     * @param array $event 事件数据
548
     * @return void
549
     */
550 2
    public function loadEvent(array $event): void
551
    {
552 2
        if (isset($event['bind'])) {
553 2
            $this->event->bind($event['bind']);
554
        }
555
556 2
        if (isset($event['listen'])) {
557 2
            $this->event->listenEvents($event['listen']);
558
        }
559
560 2
        if (isset($event['subscribe'])) {
561 2
            $this->event->subscribe($event['subscribe']);
562
        }
563 2
    }
564
565
    /**
566
     * 解析应用类的类名
567
     * @access public
568
     * @param string $layer 层名 controller model ...
569
     * @param string $name  类名
570
     * @return string
571
     */
572 1
    public function parseClass(string $layer, string $name): string
573
    {
574 1
        $name  = str_replace(['/', '.'], '\\', $name);
575 1
        $array = explode('\\', $name);
576 1
        $class = self::parseName(array_pop($array), 1);
577 1
        $path  = $array ? implode('\\', $array) . '\\' : '';
578
579 1
        return $this->namespace . '\\' . $layer . '\\' . $path . $class;
580
    }
581
582
    /**
583
     * 是否运行在命令行下
584
     * @return bool
585
     */
586 1
    public function runningInConsole()
587
    {
588 1
        return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';
589
    }
590
591
    /**
592
     * 获取应用根目录
593
     * @access protected
594
     * @return string
595
     */
596 18
    protected function getDefaultRootPath(): string
597
    {
598 18
        $path = dirname(dirname(dirname(dirname($this->thinkPath))));
599
600 18
        return $path . DIRECTORY_SEPARATOR;
601
    }
602
603
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $data should have a doc-comment as per coding-style.
Loading history...
604
     * @param $data
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
605
     * @codeCoverageIgnore
606
     * @return string
607
     */
608
    public static function serialize($data): string
609
    {
610
        SerializableClosure::enterContext();
611
        SerializableClosure::wrapClosures($data);
612
        $data = \serialize($data);
613
        SerializableClosure::exitContext();
614
        return $data;
615
    }
616
617
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
618
     * @param string $data
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
619
     * @codeCoverageIgnore
620
     * @return mixed|string
621
     */
622
    public static function unserialize(string $data)
623
    {
624
        SerializableClosure::enterContext();
625
        $data = \unserialize($data);
626
        SerializableClosure::unwrapClosures($data);
627
        SerializableClosure::exitContext();
628
        return $data;
629
    }
630
}
631