Passed
Push — 5.2 ( 7ec591...329a32 )
by
unknown
02:32
created

App::initialized()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
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\exception\ClassNotFoundException;
17
use think\initializer\BootService;
18
use think\initializer\Error;
19
use think\initializer\RegisterService;
20
21
/**
22
 * App 基础类
23
 */
5 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @package tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
24
class App extends Container
25
{
26
    const VERSION = '5.2.0RC1';
27
28
    /**
29
     * 应用调试模式
30
     * @var bool
31
     */
32
    protected $appDebug = true;
33
34
    /**
35
     * 应用开始时间
36
     * @var float
37
     */
38
    protected $beginTime;
39
40
    /**
41
     * 应用内存初始占用
42
     * @var integer
43
     */
44
    protected $beginMem;
45
46
    /**
47
     * 应用类库顶级命名空间
48
     * @var string
49
     */
50
    protected $rootNamespace = 'app';
51
52
    /**
53
     * 当前应用类库命名空间
54
     * @var string
55
     */
56
    protected $namespace = 'app';
57
58
    /**
59
     * 应用根目录
60
     * @var string
61
     */
62
    protected $rootPath = '';
63
64
    /**
65
     * 框架目录
66
     * @var string
67
     */
68
    protected $thinkPath = '';
69
70
    /**
71
     * 应用目录
72
     * @var string
73
     */
74
    protected $appPath = '';
75
76
    /**
77
     * Runtime目录
78
     * @var string
79
     */
80
    protected $runtimePath = '';
81
82
    /**
83
     * 配置后缀
84
     * @var string
85
     */
86
    protected $configExt = '.php';
87
88
    /**
89
     * 应用初始化器
90
     * @var array
91
     */
92
    protected $initializers = [
93
        Error::class,
94
        RegisterService::class,
95
        BootService::class,
96
    ];
97
98
    /**
99
     * 注册的系统服务
100
     * @var array
101
     */
102
    protected $services = [];
103
104
    /**
105
     * 初始化
106
     * @var bool
107
     */
108
    protected $initialized = false;
109
110
    /**
111
     * 架构方法
112
     * @access public
113
     * @param string $rootPath 应用根目录
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
114
     */
115
    public function __construct(string $rootPath = '')
116
    {
117
        $this->thinkPath   = dirname(__DIR__) . DIRECTORY_SEPARATOR;
118
        $this->rootPath    = $rootPath ? realpath($rootPath) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
119
        $this->appPath     = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
120
        $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
121
122
        static::setInstance($this);
123
124
        $this->instance('app', $this);
125
    }
126
127
    /**
128
     * 注册服务
129
     * @access public
130
     * @param Service|string $service 服务
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
131
     * @param bool           $force   强制重新注册
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
132
     * @return Service|null
133
     */
134
    public function register($service, bool $force = false)
135
    {
136
        $registered = $this->getService($service);
137
138
        if ($registered && !$force) {
139
            return $registered;
140
        }
141
142
        if (is_string($service)) {
143
            $service = new $service($this);
144
        }
145
146
        if (method_exists($service, 'register')) {
147
            $service->register();
148
        }
149
150
        $this->services[] = $service;
151
    }
152
153
    /**
154
     * 执行服务
155
     * @access public
156
     * @param Service $service 服务
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
157
     * @return mixed
158
     */
159
    public function bootService($service)
160
    {
161
        if (method_exists($service, 'boot')) {
162
            return $this->invoke([$service, 'boot']);
163
        }
164
    }
165
166
    /**
167
     * 获取服务
168
     * @param string|Service $service
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
169
     * @return Service|null
170
     */
171
    public function getService($service)
172
    {
173
        $name = is_string($service) ? $service : get_class($service);
174
        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...
175
                return $value instanceof $name;
176
            }, ARRAY_FILTER_USE_BOTH))[0] ?? null;
1 ignored issue
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...
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace indented incorrectly; expected 8 spaces, found 12
Loading history...
177
    }
178
179
    /**
180
     * 开启应用调试模式
181
     * @access public
182
     * @param bool $debug 开启应用调试模式
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
183
     * @return $this
184
     */
185
    public function debug(bool $debug = true)
186
    {
187
        $this->appDebug = $debug;
188
        return $this;
189
    }
190
191
    /**
192
     * 是否为调试模式
193
     * @access public
194
     * @return bool
195
     */
196
    public function isDebug(): bool
197
    {
198
        return $this->appDebug;
199
    }
200
201
    /**
202
     * 设置应用命名空间
203
     * @access public
204
     * @param string $namespace 应用命名空间
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
205
     * @return $this
206
     */
207
    public function setNamespace(string $namespace)
208
    {
209
        $this->namespace = $namespace;
210
        return $this;
211
    }
212
213
    /**
214
     * 设置应用根命名空间
215
     * @access public
216
     * @param string $rootNamespace 应用命名空间
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
217
     * @return $this
218
     */
219
    public function setRootNamespace(string $rootNamespace)
220
    {
221
        $this->rootNamespace = $rootNamespace;
222
        return $this;
223
    }
224
225
    /**
226
     * 获取应用类基础命名空间
227
     * @access public
228
     * @return string
229
     */
230
    public function getRootNamespace(): string
231
    {
232
        return $this->rootNamespace;
233
    }
234
235
    /**
236
     * 获取应用类库命名空间
237
     * @access public
238
     * @return string
239
     */
240
    public function getNamespace(): string
241
    {
242
        return $this->namespace;
243
    }
244
245
    /**
246
     * 获取框架版本
247
     * @access public
248
     * @return string
249
     */
250
    public function version(): string
251
    {
252
        return static::VERSION;
253
    }
254
255
    /**
256
     * 获取应用根目录
257
     * @access public
258
     * @return string
259
     */
260
    public function getRootPath(): string
261
    {
262
        return $this->rootPath;
263
    }
264
265
    /**
266
     * 获取应用基础目录
267
     * @access public
268
     * @return string
269
     */
270
    public function getBasePath(): string
271
    {
272
        return $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
273
    }
274
275
    /**
276
     * 获取当前应用目录
277
     * @access public
278
     * @return string
279
     */
280
    public function getAppPath(): string
281
    {
282
        return $this->appPath;
283
    }
284
285
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $path should have a doc-comment as per coding-style.
Loading history...
286
     * 设置应用目录
287
     * @param $path
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
288
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
289
    public function setAppPath($path)
290
    {
291
        $this->appPath = $path;
292
    }
293
294
    /**
295
     * 获取应用运行时目录
296
     * @access public
297
     * @return string
298
     */
299
    public function getRuntimePath(): string
300
    {
301
        return $this->runtimePath;
302
    }
303
304
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $path should have a doc-comment as per coding-style.
Loading history...
305
     * 设置runtime目录
306
     * @param $path
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
307
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
308
    public function setRuntimePath($path)
309
    {
310
        $this->runtimePath = $path;
311
    }
312
313
    /**
314
     * 获取核心框架目录
315
     * @access public
316
     * @return string
317
     */
318
    public function getThinkPath(): string
319
    {
320
        return $this->thinkPath;
321
    }
322
323
    /**
324
     * 获取应用配置目录
325
     * @access public
326
     * @return string
327
     */
328
    public function getConfigPath(): string
329
    {
330
        return $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
331
    }
332
333
    /**
334
     * 获取配置后缀
335
     * @access public
336
     * @return string
337
     */
338
    public function getConfigExt(): string
339
    {
340
        return $this->configExt;
341
    }
342
343
    /**
344
     * 获取应用开启时间
345
     * @access public
346
     * @return float
347
     */
348
    public function getBeginTime(): float
349
    {
350
        return $this->beginTime;
351
    }
352
353
    /**
354
     * 获取应用初始内存占用
355
     * @access public
356
     * @return integer
357
     */
358
    public function getBeginMem(): int
359
    {
360
        return $this->beginMem;
361
    }
362
363
    /**
364
     * 初始化应用
365
     * @access public
366
     * @return $this
367
     */
368
    public function initialize()
369
    {
370
        $this->initialized = true;
371
372
        $this->beginTime = microtime(true);
373
        $this->beginMem  = memory_get_usage();
374
375
        // 加载环境变量
376
        if (is_file($this->rootPath . '.env')) {
377
            $this->env->load($this->rootPath . '.env');
378
        }
379
380
        $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...
381
382
        // 加载全局初始化文件
383
        if (is_file($this->getRuntimePath() . 'init.php')) {
384
            //直接加载缓存
385
            include $this->getRuntimePath() . 'init.php';
386
        } else {
387
            $this->load();
388
        }
389
390
        $this->debugModeInit();
391
392
        // 监听AppInit
393
        $this->app->event->trigger('AppInit');
0 ignored issues
show
Bug Best Practice introduced by
The property app does not exist on think\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
394
395
        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 and 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

395
        date_default_timezone_set(/** @scrutinizer ignore-type */ $this->config->get('app.default_timezone', 'Asia/Shanghai'));
Loading history...
396
397
        // 初始化
398
        foreach ($this->initializers as $initializer) {
399
            $this->make($initializer)->init($this);
400
        }
401
402
        return $this;
403
    }
404
405
    /**
406
     * 是否初始化过
407
     * @return bool
408
     */
409
    public function initialized()
410
    {
411
        return $this->initialized;
412
    }
413
414
    /**
415
     * 引导应用
416
     * @access public
417
     * @return void
418
     */
419
    public function boot(): void
420
    {
421
        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...
422
            $this->bootService($service);
423
        });
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...
424
    }
425
426
    /**
427
     * 加载应用文件和配置
428
     * @access protected
429
     * @return void
430
     */
431
    protected function load(): void
432
    {
433
        $appPath = $this->getAppPath();
434
435
        if (is_file($appPath . 'common.php')) {
436
            include_once $appPath . 'common.php';
437
        }
438
439
        include $this->thinkPath . 'helper.php';
440
441
        $configPath = $this->getConfigPath();
442
443
        $files = [];
444
445
        if (is_dir($configPath)) {
446
            $files = glob($configPath . '*' . $this->configExt);
447
        }
448
449
        foreach ($files as $file) {
450
            $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
451
        }
452
453
        if (is_file($appPath . 'event.php')) {
454
            $this->loadEvent(include $appPath . 'event.php');
455
        }
456
457
        if (is_file($appPath . 'middleware.php')) {
458
            $this->middleware->import(include $appPath . 'middleware.php');
459
        }
460
461
        if (is_file($appPath . 'provider.php')) {
462
            $this->bind(include $appPath . 'provider.php');
463
        }
464
    }
465
466
    /**
467
     * 调试模式设置
468
     * @access protected
469
     * @return void
470
     */
471
    protected function debugModeInit(): void
472
    {
473
        // 应用调试模式
474
        if (!$this->appDebug) {
475
            $this->appDebug = $this->env->get('app_debug', false);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->env->get('app_debug', false) can also be of type string. However, the property $appDebug is declared as type boolean. 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...
476
        }
477
478
        if (!$this->appDebug) {
479
            ini_set('display_errors', 'Off');
480
        } elseif (!$this->runningInConsole()) {
481
            //重新申请一块比较大的buffer
482
            if (ob_get_level() > 0) {
483
                $output = ob_get_clean();
484
            }
485
            ob_start();
486
            if (!empty($output)) {
487
                echo $output;
488
            }
489
        }
490
    }
491
492
    /**
493
     * 注册应用事件
494
     * @access protected
495
     * @param array $event 事件数据
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
496
     * @return void
497
     */
498
    public function loadEvent(array $event): void
499
    {
500
        if (isset($event['bind'])) {
501
            $this->event->bind($event['bind']);
502
        }
503
504
        if (isset($event['listen'])) {
505
            $this->event->listenEvents($event['listen']);
506
        }
507
508
        if (isset($event['subscribe'])) {
509
            $this->event->subscribe($event['subscribe']);
510
        }
511
    }
512
513
    /**
514
     * 解析应用类的类名
515
     * @access public
516
     * @param string $layer 层名 controller model ...
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
517
     * @param string $name  类名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
518
     * @return string
519
     */
520
    public function parseClass(string $layer, string $name): string
521
    {
522
        $name  = str_replace(['/', '.'], '\\', $name);
523
        $array = explode('\\', $name);
524
        $class = self::parseName(array_pop($array), 1);
525
        $path  = $array ? implode('\\', $array) . '\\' : '';
526
527
        return $this->namespace . '\\' . $layer . '\\' . $path . $class;
528
    }
529
530
    /**
531
     * 是否运行在命令行下
532
     * @return bool
533
     */
534
    public function runningInConsole()
535
    {
536
        return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';
537
    }
538
539
    /**
540
     * 获取应用根目录
541
     * @access protected
542
     * @return string
543
     */
544
    protected function getDefaultRootPath(): string
545
    {
546
        $path = dirname(dirname(dirname(dirname($this->thinkPath))));
547
548
        return $path . DIRECTORY_SEPARATOR;
549
    }
550
551
    /**
552
     * 字符串命名风格转换
553
     * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
554
     * @access public
555
     * @param string  $name    字符串
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
556
     * @param integer $type    转换类型
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
557
     * @param bool    $ucfirst 首字母是否大写(驼峰规则)
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
558
     * @return string
559
     */
560
    public static function parseName(string $name = null, int $type = 0, bool $ucfirst = true): string
561
    {
562
        if ($type) {
563
            $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
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...
564
                return strtoupper($match[1]);
565
            }, $name);
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...
566
            return $ucfirst ? ucfirst($name) : lcfirst($name);
567
        }
568
569
        return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
570
    }
571
572
    /**
573
     * 获取类名(不包含命名空间)
574
     * @access public
575
     * @param string|object $class
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
576
     * @return string
577
     */
578
    public static function classBaseName($class): string
579
    {
580
        $class = is_object($class) ? get_class($class) : $class;
581
        return basename(str_replace('\\', '/', $class));
582
    }
583
584
    /**
0 ignored issues
show
Coding Style introduced by
Parameter ...$args should have a doc-comment as per coding-style.
Loading history...
585
     * 创建工厂对象实例
586
     * @access public
587
     * @param string $name      工厂类名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
588
     * @param string $namespace 默认命名空间
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
589
     * @return mixed
590
     */
591
    public static function factory(string $name, string $namespace = '', ...$args)
592
    {
593
        $class = false !== strpos($name, '\\') ? $name : $namespace . ucwords($name);
594
595
        if (class_exists($class)) {
596
            return Container::getInstance()->invokeClass($class, $args);
597
        }
598
599
        throw new ClassNotFoundException('class not exists:' . $class, $class);
600
    }
601
602
    public static function serialize($data): string
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
603
    {
604
        SerializableClosure::enterContext();
605
        SerializableClosure::wrapClosures($data);
606
        $data = \serialize($data);
607
        SerializableClosure::exitContext();
608
        return $data;
609
    }
610
611
    public static function unserialize(string $data)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
612
    {
613
        SerializableClosure::enterContext();
614
        $data = \unserialize($data);
615
        SerializableClosure::unwrapClosures($data);
616
        SerializableClosure::exitContext();
617
        return $data;
618
    }
619
}
620