Passed
Push — 5.2 ( 94f706...1b9ee1 )
by liu
03:53
created

App::runningInConsole()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
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 bool
91
     */
92
    protected $withEvent = true;
93
94
    /**
95
     * 应用初始化器
96
     * @var array
97
     */
98
    protected $initializers = [
99
        Error::class,
100
        RegisterService::class,
101
        BootService::class,
102
    ];
103
104
    /**
105
     * 注册的系统服务
106
     * @var array
107
     */
108
    protected $services = [];
109
110
    /**
111
     * 初始化
112
     * @var bool
113
     */
114
    protected $initialized = false;
115
116
    /**
117
     * 架构方法
118
     * @access public
119
     * @param  string $rootPath 应用根目录
120
     */
121
    public function __construct(string $rootPath = '')
122
    {
123
        $this->thinkPath   = dirname(__DIR__) . DIRECTORY_SEPARATOR;
124
        $this->rootPath    = $rootPath ? realpath($rootPath) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
125
        $this->appPath     = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
126
        $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
127
128
        static::setInstance($this);
129
130
        $this->instance('app', $this);
131
    }
132
133
    /**
134
     * 注册服务
135
     * @access public
136
     * @param  Service|string $service 服务
137
     * @param  bool           $force   强制重新注册
138
     * @return Service|null
139
     */
140
    public function register($service, bool $force = false)
141
    {
142
        $registered = $this->getService($service);
143
144
        if ($registered && !$force) {
145
            return $registered;
146
        }
147
148
        if (is_string($service)) {
149
            $service = new $service($this);
150
        }
151
152
        if (method_exists($service, 'register')) {
153
            $service->register();
154
        }
155
156
        $this->services[] = $service;
157
    }
158
159
    /**
160
     * 执行服务
161
     * @access public
162
     * @param  Service $service 服务
163
     * @return mixed
164
     */
165
    public function bootService($service)
166
    {
167
        if (method_exists($service, 'boot')) {
168
            return $this->invoke([$service, 'boot']);
169
        }
170
    }
171
172
    /**
173
     * 获取服务
174
     * @param  string|Service $service
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
175
     * @return Service|null
176
     */
177
    public function getService($service)
178
    {
179
        $name = is_string($service) ? $service : get_class($service);
180
        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...
181
            return $value instanceof $name;
182
        }, 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...
183
    }
184
185
    /**
186
     * 设置是否使用事件机制
187
     * @access public
188
     * @param  bool $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
189
     * @return $this
190
     */
191
    public function withEvent(bool $event)
192
    {
193
        $this->withEvent = $event;
194
        return $this;
195
    }
196
197
    /**
198
     * 开启应用调试模式
199
     * @access public
200
     * @param  bool $debug 开启应用调试模式
201
     * @return $this
202
     */
203
    public function debug(bool $debug = true)
204
    {
205
        $this->appDebug = $debug;
206
        return $this;
207
    }
208
209
    /**
210
     * 是否为调试模式
211
     * @access public
212
     * @return bool
213
     */
214
    public function isDebug(): bool
215
    {
216
        return $this->appDebug;
217
    }
218
219
    /**
220
     * 设置应用命名空间
221
     * @access public
222
     * @param  string $namespace 应用命名空间
223
     * @return $this
224
     */
225
    public function setNamespace(string $namespace)
226
    {
227
        $this->namespace = $namespace;
228
        return $this;
229
    }
230
231
    /**
232
     * 设置应用根命名空间
233
     * @access public
234
     * @param  string $rootNamespace 应用命名空间
235
     * @return $this
236
     */
237
    public function setRootNamespace(string $rootNamespace)
238
    {
239
        $this->rootNamespace = $rootNamespace;
240
        return $this;
241
    }
242
243
    /**
244
     * 获取应用类基础命名空间
245
     * @access public
246
     * @return string
247
     */
248
    public function getRootNamespace(): string
249
    {
250
        return $this->rootNamespace;
251
    }
252
253
    /**
254
     * 获取应用类库命名空间
255
     * @access public
256
     * @return string
257
     */
258
    public function getNamespace(): string
259
    {
260
        return $this->namespace;
261
    }
262
263
    /**
264
     * 获取框架版本
265
     * @access public
266
     * @return string
267
     */
268
    public function version(): string
269
    {
270
        return static::VERSION;
271
    }
272
273
    /**
274
     * 获取应用根目录
275
     * @access public
276
     * @return string
277
     */
278
    public function getRootPath(): string
279
    {
280
        return $this->rootPath;
281
    }
282
283
    /**
284
     * 获取应用基础目录
285
     * @access public
286
     * @return string
287
     */
288
    public function getBasePath(): string
289
    {
290
        return $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
291
    }
292
293
    /**
294
     * 获取当前应用目录
295
     * @access public
296
     * @return string
297
     */
298
    public function getAppPath(): string
299
    {
300
        return $this->appPath;
301
    }
302
303
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $path should have a doc-comment as per coding-style.
Loading history...
304
     * 设置应用目录
305
     * @param $path
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
306
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
307
    public function setAppPath($path)
308
    {
309
        $this->appPath = $path;
310
    }
311
312
    /**
313
     * 获取应用运行时目录
314
     * @access public
315
     * @return string
316
     */
317
    public function getRuntimePath(): string
318
    {
319
        return $this->runtimePath;
320
    }
321
322
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $path should have a doc-comment as per coding-style.
Loading history...
323
     * 设置runtime目录
324
     * @param $path
0 ignored issues
show
Coding Style Documentation introduced by
Missing parameter name
Loading history...
325
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
326
    public function setRuntimePath($path)
327
    {
328
        $this->runtimePath = $path;
329
    }
330
331
    /**
332
     * 获取核心框架目录
333
     * @access public
334
     * @return string
335
     */
336
    public function getThinkPath(): string
337
    {
338
        return $this->thinkPath;
339
    }
340
341
    /**
342
     * 获取应用配置目录
343
     * @access public
344
     * @return string
345
     */
346
    public function getConfigPath(): string
347
    {
348
        return $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
349
    }
350
351
    /**
352
     * 获取配置后缀
353
     * @access public
354
     * @return string
355
     */
356
    public function getConfigExt(): string
357
    {
358
        return $this->configExt;
359
    }
360
361
    /**
362
     * 获取应用开启时间
363
     * @access public
364
     * @return float
365
     */
366
    public function getBeginTime(): float
367
    {
368
        return $this->beginTime;
369
    }
370
371
    /**
372
     * 获取应用初始内存占用
373
     * @access public
374
     * @return integer
375
     */
376
    public function getBeginMem(): int
377
    {
378
        return $this->beginMem;
379
    }
380
381
    /**
382
     * 初始化应用
383
     * @access public
384
     * @return $this
385
     */
386
    public function initialize()
387
    {
388
        if ($this->initialized) {
389
            return $this;
390
        }
391
392
        $this->initialized = true;
393
394
        $this->beginTime = microtime(true);
395
        $this->beginMem  = memory_get_usage();
396
397
        //加载环境变量
398
        if (is_file($this->rootPath . '.env')) {
399
            $this->env->load($this->rootPath . '.env');
400
        }
401
402
        $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...
403
404
        // 加载初始化文件
405
        if (is_file($this->getRuntimePath() . 'init.php')) {
406
            //直接加载缓存
407
            include $this->getRuntimePath() . 'init.php';
408
        } else {
409
            $this->load();
410
        }
411
412
        // 设置开启事件机制
413
        $this->event->withEvent($this->withEvent);
414
415
        // 监听AppInit
416
        $this->event->trigger('AppInit');
417
418
        $this->debugModeInit();
419
420
        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

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