Passed
Push — 8.0 ( 1629cd...8da4e4 )
by liu
13: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 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
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~2023 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 Composer\InstalledVersions;
16
use think\event\AppInit;
17
use think\helper\Str;
18
use think\initializer\BootService;
19
use think\initializer\Error;
20
use think\initializer\RegisterService;
21
22
/**
23
 * App 基础类
24
 * @property Route      $route
25
 * @property Config     $config
26
 * @property Cache      $cache
27
 * @property Request    $request
28
 * @property Http       $http
29
 * @property Console    $console
30
 * @property Env        $env
31
 * @property Event      $event
32
 * @property Middleware $middleware
33
 * @property Log        $log
34
 * @property Lang       $lang
35
 * @property Db         $db
36
 * @property Cookie     $cookie
37
 * @property Session    $session
38
 * @property Validate   $validate
39
 */
40
class App extends Container
41
{
42
    const VERSION = '8.0.0';
43
44
    /**
45
     * 应用调试模式
46
     * @var bool
47
     */
48
    protected $appDebug = false;
49
50
    /**
51
     * 公共环境变量标识
52
     * @var string
53
     */
54
    protected $baseEnvName = '';
55
56
    /**
57
     * 环境变量标识
58
     * @var string
59
     */
60
    protected $envName = '';
61
62
    /**
63
     * 应用开始时间
64
     * @var float
65
     */
66
    protected $beginTime;
67
68
    /**
69
     * 应用内存初始占用
70
     * @var integer
71
     */
72
    protected $beginMem;
73
74
    /**
75
     * 当前应用类库命名空间
76
     * @var string
77
     */
78
    protected $namespace = 'app';
79
80
    /**
81
     * 应用根目录
82
     * @var string
83
     */
84
    protected $rootPath = '';
85
86
    /**
87
     * 框架目录
88
     * @var string
89
     */
90
    protected $thinkPath = '';
91
92
    /**
93
     * 应用目录
94
     * @var string
95
     */
96
    protected $appPath = '';
97
98
    /**
99
     * Runtime目录
100
     * @var string
101
     */
102
    protected $runtimePath = '';
103
104
    /**
105
     * 路由定义目录
106
     * @var string
107
     */
108
    protected $routePath = '';
109
110
    /**
111
     * 配置后缀
112
     * @var string
113
     */
114
    protected $configExt = '.php';
115
116
    /**
117
     * 应用初始化器
118
     * @var array
119
     */
120
    protected $initializers = [
121
        Error::class,
122
        RegisterService::class,
123
        BootService::class,
124
    ];
125
126
    /**
127
     * 注册的系统服务
128
     * @var array
129
     */
130
    protected $services = [];
131
132
    /**
133
     * 初始化
134
     * @var bool
135
     */
136
    protected $initialized = false;
137
138
    /**
139
     * 容器绑定标识
140
     * @var array
141
     */
142
    protected $bind = [
143
        'app'                     => App::class,
144
        'cache'                   => Cache::class,
145
        'config'                  => Config::class,
146
        'console'                 => Console::class,
147
        'cookie'                  => Cookie::class,
148
        'db'                      => Db::class,
149
        'env'                     => Env::class,
150
        'event'                   => Event::class,
151
        'http'                    => Http::class,
152
        'lang'                    => Lang::class,
153
        'log'                     => Log::class,
154
        'middleware'              => Middleware::class,
155
        'request'                 => Request::class,
156
        'response'                => Response::class,
157
        'route'                   => Route::class,
158
        'session'                 => Session::class,
159
        'validate'                => Validate::class,
160
        'view'                    => View::class,
161
        'think\DbManager'         => Db::class,
162
        'think\LogManager'        => Log::class,
163
        'think\CacheManager'      => Cache::class,
164
165
        // 接口依赖注入
166
        'Psr\Log\LoggerInterface' => Log::class,
167
    ];
168 30
169
    /**
170 30
     * 架构方法
171 30
     * @access public
172 30
     * @param string $rootPath 应用根目录
173 30
     */
174
    public function __construct(string $rootPath = '')
175 30
    {
176 3
        $this->thinkPath   = realpath(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
177
        $this->rootPath    = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
178
        $this->appPath     = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
179 30
        $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
180
181 30
        if (is_file($this->appPath . 'provider.php')) {
182 30
            $this->bind(include $this->appPath . 'provider.php');
183
        }
184
185
        static::setInstance($this);
186
187
        $this->instance('app', $this);
188
        $this->instance('think\Container', $this);
189
    }
190
191
    /**
192 3
     * 注册服务
193
     * @access public
194 3
     * @param Service|string $service 服务
195
     * @param bool           $force   强制重新注册
196 3
     * @return Service|null
197 3
     */
198
    public function register(Service | string $service, bool $force = false)
199
    {
200 3
        $registered = $this->getService($service);
201
202
        if ($registered && !$force) {
203
            return $registered;
204 3
        }
205 3
206
        if (is_string($service)) {
207
            $service = new $service($this);
208 3
        }
209 3
210
        if (method_exists($service, 'register')) {
211
            $service->register();
212 3
        }
213
214
        if (property_exists($service, 'bind')) {
215
            $this->bind($service->bind);
216
        }
217
218
        $this->services[] = $service;
219
    }
220
221 3
    /**
222
     * 执行服务
223 3
     * @access public
224 3
     * @param Service $service 服务
225
     * @return mixed
226
     */
227
    public function bootService(Service $service)
228
    {
229
        if (method_exists($service, 'boot')) {
230
            return $this->invoke([$service, 'boot']);
231
        }
232
    }
233 3
234
    /**
235 3
     * 获取服务
236 3
     * @param string|Service $service
237 3
     * @return Service|null
238 3
     */
239
    public function getService(Service | string $service): ?Service
240
    {
241
        $name = is_string($service) ? $service : $service::class;
242
        return array_values(array_filter($this->services, function ($value) use ($name) {
243
            return $value instanceof $name;
244
        }, ARRAY_FILTER_USE_BOTH))[0] ?? null;
245
    }
246
247 6
    /**
248
     * 开启应用调试模式
249 6
     * @access public
250 6
     * @param bool $debug 开启应用调试模式
251
     * @return $this
252
     */
253
    public function debug(bool $debug = true)
254
    {
255
        $this->appDebug = $debug;
256
        return $this;
257
    }
258 3
259
    /**
260 3
     * 是否为调试模式
261
     * @access public
262
     * @return bool
263
     */
264
    public function isDebug(): bool
265
    {
266
        return $this->appDebug;
267
    }
268
269 6
    /**
270
     * 设置应用命名空间
271 6
     * @access public
272 6
     * @param string $namespace 应用命名空间
273
     * @return $this
274
     */
275
    public function setNamespace(string $namespace)
276
    {
277
        $this->namespace = $namespace;
278
        return $this;
279
    }
280 3
281
    /**
282 3
     * 获取应用类库命名空间
283
     * @access public
284
     * @return string
285
     */
286
    public function getNamespace(): string
287
    {
288
        return $this->namespace;
289
    }
290
291
    /**
292
     * 设置公共环境变量标识
293
     * @access public
294
     * @param string $name 环境标识
295
     * @return $this
296
     */
297
    public function setBaseEnvName(string $name)
298
    {
299
        $this->baseEnvName = $name;
300
        return $this;
301
    }
302
303
    /**
304
     * 设置环境变量标识
305
     * @access public
306
     * @param string $name 环境标识
307
     * @return $this
308
     */
309
    public function setEnvName(string $name)
310
    {
311
        $this->envName = $name;
312 12
        return $this;
313
    }
314 12
315
    /**
316
     * 获取框架版本
317
     * @access public
318
     * @return string
319
     */
320
    public function version(): string
321
    {
322 3
        return ltrim(InstalledVersions::getPrettyVersion('topthink/framework'), 'v');
0 ignored issues
show
Bug introduced by
It seems like Composer\InstalledVersio...n('topthink/framework') can also be of type null; however, parameter $string of ltrim() 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

322
        return ltrim(/** @scrutinizer ignore-type */ InstalledVersions::getPrettyVersion('topthink/framework'), 'v');
Loading history...
323
    }
324 3
325
    /**
326
     * 获取应用根目录
327
     * @access public
328
     * @return string
329
     */
330
    public function getRootPath(): string
331
    {
332 6
        return $this->rootPath;
333
    }
334 6
335
    /**
336
     * 获取应用基础目录
337
     * @access public
338
     * @return string
339
     */
340
    public function getBasePath(): string
341 3
    {
342
        return $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
343 3
    }
344
345
    /**
346
     * 获取当前应用目录
347
     * @access public
348
     * @return string
349
     */
350
    public function getAppPath(): string
351 36
    {
352
        return $this->appPath;
353 36
    }
354
355
    /**
356
     * 设置应用目录
357
     * @param string $path 应用目录
358
     */
359
    public function setAppPath(string $path)
360 3
    {
361
        $this->appPath = $path;
362 3
    }
363
364
    /**
365
     * 获取应用运行时目录
366
     * @access public
367
     * @return string
368
     */
369
    public function getRuntimePath(): string
370 6
    {
371
        return $this->runtimePath;
372 6
    }
373
374
    /**
375
     * 设置runtime目录
376
     * @param string $path 定义目录
377
     */
378
    public function setRuntimePath(string $path): void
379
    {
380 39
        $this->runtimePath = $path;
381
    }
382 39
383
    /**
384
     * 获取核心框架目录
385
     * @access public
386
     * @return string
387
     */
388
    public function getThinkPath(): string
389
    {
390 36
        return $this->thinkPath;
391
    }
392 36
393
    /**
394
     * 获取应用配置目录
395
     * @access public
396
     * @return string
397
     */
398
    public function getConfigPath(): string
399
    {
400 3
        return $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
401
    }
402 3
403
    /**
404
     * 获取配置后缀
405
     * @access public
406
     * @return string
407
     */
408
    public function getConfigExt(): string
409
    {
410 3
        return $this->configExt;
411
    }
412 3
413
    /**
414
     * 获取应用开启时间
415
     * @access public
416
     * @return float
417
     */
418
    public function getBeginTime(): float
419
    {
420
        return $this->beginTime;
421 3
    }
422
423
    /**
424 3
     * 获取应用初始内存占用
425
     * @access public
426 3
     * @return integer
427 3
     */
428
    public function getBeginMem(): int
429
    {
430
        return $this->beginMem;
431
    }
432
433
    /**
434
     * 加载环境变量定义
435
     * @access public
436 3
     * @param string $envName 环境标识
437
     * @return void
438 3
     */
439
    public function loadEnv(string $envName = ''): void
440 3
    {
441 3
        // 加载环境变量
442
        $envFile = $envName ? $this->rootPath . '.env.' . $envName : $this->rootPath . '.env';
443 3
444
        if (is_file($envFile)) {
445 3
            $this->env->load($envFile);
446
        }
447 3
    }
448
449
    /**
450 3
     * 初始化应用
451
     * @access public
452
     * @return $this
453 3
     */
454
    public function initialize()
455
    {
456 3
        $this->initialized = true;
457
458 3
        $this->beginTime = microtime(true);
0 ignored issues
show
Documentation Bug introduced by
It seems like microtime(true) can also be of type string. However, the property $beginTime is declared as type double. 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...
459
        $this->beginMem  = memory_get_usage();
460
461 3
        // 加载环境变量
462 3
        if ($this->baseEnvName) {
463
            $this->loadEnv($this->baseEnvName);
464
        }
465 3
466
        $this->envName = $this->envName ?: (string) $this->env->get('env_name', '');
467
        $this->loadEnv($this->envName);
468
469
        $this->configExt = $this->env->get('config_ext', '.php');
470
471
        $this->debugModeInit();
472 6
473
        // 加载全局初始化文件
474 6
        $this->load();
475
476
        // 加载应用默认语言包
477
        $this->loadLangPack();
478
479
        // 监听AppInit
480
        $this->event->trigger(AppInit::class);
481 3
482
        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 $timezoneId 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

482
        date_default_timezone_set(/** @scrutinizer ignore-type */ $this->config->get('app.default_timezone', 'Asia/Shanghai'));
Loading history...
483
484 3
        // 初始化
485 3
        foreach ($this->initializers as $initializer) {
486
            $this->make($initializer)->init($this);
487
        }
488
489
        return $this;
490
    }
491
492
    /**
493 3
     * 是否初始化过
494
     * @return bool
495 3
     */
496 3
    public function initialized()
497 3
    {
498
        return $this->initialized;
499
    }
500
501
    /**
502
     * 加载语言包
503
     * @return void
504
     */
505 3
    public function loadLangPack(): void
506
    {
507 3
        // 加载默认语言包
508
        $langSet = $this->lang->defaultLangSet();
509 3
        $this->lang->switchLangSet($langSet);
510 3
    }
511
512
    /**
513 3
     * 引导应用
514
     * @access public
515 3
     * @return void
516
     */
517 3
    public function boot(): void
518
    {
519 3
        array_walk($this->services, function ($service) {
520 3
            $this->bootService($service);
521
        });
522
    }
523 3
524
    /**
525
     * 加载应用文件和配置
526
     * @access protected
527 3
     * @return void
528 3
     */
529
    protected function load(): void
530
    {
531 3
        $appPath = $this->getAppPath();
532
533
        if (is_file($appPath . 'common.php')) {
534
            include_once $appPath . 'common.php';
535
        }
536
537
        include_once $this->thinkPath . 'helper.php';
538
539
        $configPath = $this->getConfigPath();
540
541
        $files = [];
542
543
        if (is_dir($configPath)) {
544 3
            $files = glob($configPath . '*' . $this->configExt);
545
        }
546
547 3
        foreach ($files as $file) {
548 3
            $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
0 ignored issues
show
Bug introduced by
It seems like pathinfo($file, think\PATHINFO_FILENAME) can also be of type array; however, parameter $name of think\Config::load() 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

548
            $this->config->load($file, /** @scrutinizer ignore-type */ pathinfo($file, PATHINFO_FILENAME));
Loading history...
549
        }
550
551 3
        if (is_file($appPath . 'event.php')) {
552
            $this->loadEvent(include $appPath . 'event.php');
553
        }
554
555 3
        if (is_file($appPath . 'service.php')) {
556
            $services = include $appPath . 'service.php';
557
            foreach ($services as $service) {
558
                $this->register($service);
559
            }
560
        }
561
    }
562
563
    /**
564
     * 调试模式设置
565
     * @access protected
566
     * @return void
567
     */
568
    protected function debugModeInit(): void
569
    {
570
        // 应用调试模式
571
        if (!$this->appDebug) {
572
            $this->appDebug = $this->env->get('app_debug') ? true : false;
573 3
        }
574
575 3
        if (!$this->appDebug) {
576 3
            ini_set('display_errors', 'Off');
577
        }
578
579 3
        if (!$this->runningInConsole()) {
580 3
            //重新申请一块比较大的buffer
581
            if (ob_get_level() > 0) {
582
                $output = ob_get_clean();
583 3
            }
584 3
            ob_start();
585
            if (!empty($output)) {
586
                echo $output;
587
            }
588
        }
589
    }
590
591
    /**
592
     * 注册应用事件
593
     * @access protected
594
     * @param array $event 事件数据
595 6
     * @return void
596
     */
597 6
    public function loadEvent(array $event): void
598 6
    {
599 6
        if (isset($event['bind'])) {
600 6
            $this->event->bind($event['bind']);
601
        }
602 6
603
        if (isset($event['listen'])) {
604
            $this->event->listenEvents($event['listen']);
605
        }
606
607
        if (isset($event['subscribe'])) {
608
            $this->event->subscribe($event['subscribe']);
609 3
        }
610
    }
611 3
612
    /**
613
     * 解析应用类的类名
614
     * @access public
615
     * @param string $layer 层名 controller model ...
616
     * @param string $name  类名
617
     * @return string
618
     */
619 30
    public function parseClass(string $layer, string $name): string
620
    {
621 30
        $name  = str_replace(['/', '.'], '\\', $name);
622
        $array = explode('\\', $name);
623
        $class = Str::studly(array_pop($array));
624
        $path  = $array ? implode('\\', $array) . '\\' : '';
625
626
        return $this->namespace . '\\' . $layer . '\\' . $path . $class;
627
    }
628
629
    /**
630
     * 是否运行在命令行下
631
     * @return bool
632
     */
633
    public function runningInConsole(): bool
634
    {
635
        return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';
636
    }
637
638
    /**
639
     * 获取应用根目录
640
     * @access protected
641
     * @return string
642
     */
643
    protected function getDefaultRootPath(): string
644
    {
645
        return dirname($this->thinkPath, 4) . DIRECTORY_SEPARATOR;
646
    }
647
}
648