Passed
Pull Request — 6.0 (#2322)
by
unknown
03:02
created

App::getRuntimePath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

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

465
        $this->lang->load(/** @scrutinizer ignore-type */ $files);
Loading history...
466
467
        // 加载扩展(自定义)语言包
468 3
        $list = $this->config->get('lang.extend_list', []);
469
470 3
        if (isset($list[$langset])) {
471
            $this->lang->load($list[$langset]);
472
        }
473 3
    }
474
475
    /**
476
     * 引导应用
477
     * @access public
478
     * @return void
479
     */
480 3
    public function boot(): void
481
    {
482
        array_walk($this->services, function ($service) {
483 3
            $this->bootService($service);
484 3
        });
485 3
    }
486
487
    /**
488
     * 加载应用文件和配置
489
     * @access protected
490
     * @return void
491
     */
492 3
    protected function load(): void
493
    {
494 3
        $appPath = $this->getAppPath();
495
496 3
        if (is_file($appPath . 'common.php')) {
497 3
            include_once $appPath . 'common.php';
498
        }
499
500 3
        include_once $this->thinkPath . 'helper.php';
501
502 3
        $configPath = $this->getConfigPath();
503
504 3
        $files = [];
505
506 3
        if (is_dir($configPath)) {
507 3
            $files = glob($configPath . '*' . $this->configExt);
508
        }
509
510 3
        foreach ($files as $file) {
511
            $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
512
        }
513
514 3
        if (is_file($appPath . 'event.php')) {
515 3
            $this->loadEvent(include $appPath . 'event.php');
516
        }
517
518 3
        if (is_file($appPath . 'service.php')) {
519
            $services = include $appPath . 'service.php';
520
            foreach ($services as $service) {
521
                $this->register($service);
522
            }
523
        }
524 3
    }
525
526
    /**
527
     * 调试模式设置
528
     * @access protected
529
     * @return void
530
     */
531 3
    protected function debugModeInit(): void
532
    {
533
        // 应用调试模式
534 3
        if (!$this->appDebug) {
535 3
            if(defined('APP_DEBUG')){
536
                $this->appDebug = APP_DEBUG;
0 ignored issues
show
Bug introduced by
The constant think\APP_DEBUG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
537
            }else{
538 3
                $this->appDebug = $this->env->get('app_debug') ? true : false;
539
            }
540 3
            ini_set('display_errors', 'Off');
541
        }
542
543 3
        if (!$this->runningInConsole()) {
544
            //重新申请一块比较大的buffer
545
            if (ob_get_level() > 0) {
546
                $output = ob_get_clean();
547
            }
548
            ob_start();
549
            if (!empty($output)) {
550
                echo $output;
551
            }
552
        }
553 3
    }
554
555
    /**
556
     * 注册应用事件
557
     * @access protected
558
     * @param array $event 事件数据
559
     * @return void
560
     */
561 3
    public function loadEvent(array $event): void
562
    {
563 3
        if (isset($event['bind'])) {
564 3
            $this->event->bind($event['bind']);
565
        }
566
567 3
        if (isset($event['listen'])) {
568 3
            $this->event->listenEvents($event['listen']);
569
        }
570
571 3
        if (isset($event['subscribe'])) {
572 3
            $this->event->subscribe($event['subscribe']);
573
        }
574 3
    }
575
576
    /**
577
     * 解析应用类的类名
578
     * @access public
579
     * @param string $layer 层名 controller model ...
580
     * @param string $name  类名
581
     * @return string
582
     */
583 6
    public function parseClass(string $layer, string $name): string
584
    {
585 6
        $name  = str_replace(['/', '.'], '\\', $name);
586 6
        $array = explode('\\', $name);
587 6
        $class = Str::studly(array_pop($array));
588 6
        $path  = $array ? implode('\\', $array) . '\\' : '';
589
590 6
        return $this->namespace . '\\' . $layer . '\\' . $path . $class;
591
    }
592
593
    /**
594
     * 是否运行在命令行下
595
     * @return bool
596
     */
597 3
    public function runningInConsole()
598
    {
599 3
        return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';
600
    }
601
602
    /**
603
     * 获取应用根目录
604
     * @access protected
605
     * @return string
606
     */
607 30
    protected function getDefaultRootPath(): string
608
    {
609 30
        return dirname($this->thinkPath, 4) . DIRECTORY_SEPARATOR;
610
    }
611
612
}
613