Passed
Push — 5.2 ( 7a5435...2fd4d8 )
by liu
03:06
created

App::setRootNamespace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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