Passed
Push — 5.2 ( b7aaad...0529c5 )
by liu
02:28
created

App::parseAppName()   B

Complexity

Conditions 11
Paths 6

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 15
nc 6
nop 0
dl 0
loc 24
rs 7.3166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\exception\HttpException;
18
use think\exception\HttpResponseException;
19
use think\route\Dispatch;
20
21
/**
22
 * App 应用管理
23
 * @property Route $route
24
 * @property Config $config
25
 * @property Cache $cache
26
 * @property Request $request
27
 * @property Env $env
28
 * @property Debug $debug
29
 * @property Event $event
30
 * @property Middleware $middleware
31
 * @property Log $log
32
 * @property Lang $lang
33
 * @property Db $db
34
 * @property Cookie $cookie
35
 * @property Session $session
36
 * @property Url $url
37
 * @property Validate $validate
38
 * @property Build $build
39
 * @property \think\route\RuleName $rule_name
40
 */
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...
41
class App extends Container
42
{
43
    const VERSION = '5.2.0RC1';
44
45
    /**
46
     * 应用名称
47
     * @var string
48
     */
49
    protected $name;
50
51
    /**
52
     * 应用调试模式
53
     * @var bool
54
     */
55
    protected $appDebug = true;
56
57
    /**
58
     * 应用开始时间
59
     * @var float
60
     */
61
    protected $beginTime;
62
63
    /**
64
     * 应用内存初始占用
65
     * @var integer
66
     */
67
    protected $beginMem;
68
69
    /**
70
     * 应用类库顶级命名空间
71
     * @var string
72
     */
73
    protected $rootNamespace = 'app';
74
75
    /**
76
     * 当前应用类库命名空间
77
     * @var string
78
     */
79
    protected $namespace = '';
80
81
    /**
82
     * 应用根目录
83
     * @var string
84
     */
85
    protected $rootPath = '';
86
87
    /**
88
     * 框架目录
89
     * @var string
90
     */
91
    protected $thinkPath = '';
92
93
    /**
94
     * 应用基础目录
95
     * @var string
96
     */
97
    protected $basePath = '';
98
99
    /**
100
     * 应用类库目录
101
     * @var string
102
     */
103
    protected $appPath = '';
104
105
    /**
106
     * 默认应用名(多应用模式)
107
     * @var string
108
     */
109
    protected $defaultApp = 'index';
110
111
    /**
112
     * 运行时目录
113
     * @var string
114
     */
115
    protected $runtimePath = '';
116
117
    /**
118
     * 配置目录
119
     * @var string
120
     */
121
    protected $configPath = '';
122
123
    /**
124
     * 路由目录
125
     * @var string
126
     */
127
    protected $routePath = '';
128
129
    /**
130
     * URL
131
     * @var string
132
     */
133
    protected $urlPath = '';
134
135
    /**
136
     * 配置后缀
137
     * @var string
138
     */
139
    protected $configExt = '.php';
140
141
    /**
142
     * 初始化
143
     * @var bool
144
     */
145
    protected $initialized = false;
146
147
    /**
148
     * 是否为多应用模式
149
     * @var bool
150
     */
151
    protected $multi = false;
152
153
    /**
154
     * 是否为自动多应用
155
     * @var bool
156
     */
157
    protected $auto = false;
158
159
    /**
160
     * 应用映射
161
     * @var array
162
     */
163
    protected $map = [];
164
165
    /**
166
     * 是否需要事件响应
167
     * @var bool
168
     */
169
    protected $withEvent = true;
170
171
    /**
172
     * 是否需要使用路由
173
     * @var bool
174
     */
175
    protected $withRoute = true;
176
177
    /**
178
     * 访问控制器层名称
179
     * @var string
180
     */
181
    protected $controllerLayer = 'controller';
182
183
    /**
184
     * 是否使用控制器类库后缀
185
     * @var bool
186
     */
187
    protected $controllerSuffix = false;
188
189
    /**
190
     * 空控制器名称
191
     * @var string
192
     */
193
    protected $emptyController = 'Error';
194
195
    /**
196
     * 架构方法
197
     * @access public
198
     * @param  string $rootPath 应用根目录
199
     */
200
    public function __construct(string $rootPath = '')
201
    {
202
        $this->thinkPath = dirname(__DIR__) . DIRECTORY_SEPARATOR;
203
        $this->rootPath  = $rootPath ? realpath($rootPath) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
204
        $this->basePath  = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
205
206
        $this->multi = is_dir($this->basePath . 'controller') ? false : true;
207
208
        static::setInstance($this);
209
210
        $this->instance('app', $this);
211
212
        // 注册错误和异常处理机制
213
        Error::register();
214
    }
215
216
    /**
217
     * 自动多应用访问
218
     * @access public
219
     * @param  array $map 应用路由映射
220
     * @return $this
221
     */
222
    public function autoMulti(array $map = [])
223
    {
224
        $this->multi = true;
225
        $this->auto  = true;
226
        $this->map   = $map;
227
228
        return $this;
229
    }
230
231
    /**
232
     * 是否为自动多应用模式
233
     * @access public
234
     * @return bool
235
     */
236
    public function isAutoMulti(): bool
237
    {
238
        return $this->auto;
239
    }
240
241
    /**
242
     * 设置应用模式
243
     * @access public
244
     * @param  bool $multi
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
245
     * @return $this
246
     */
247
    public function multi(bool $multi)
248
    {
249
        $this->multi = $multi;
250
        return $this;
251
    }
252
253
    /**
254
     * 是否为多应用模式
255
     * @access public
256
     * @return bool
257
     */
258
    public function isMulti(): bool
259
    {
260
        return $this->multi;
261
    }
262
263
    /**
264
     * 设置是否使用事件机制
265
     * @access public
266
     * @param  bool $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
267
     * @return $this
268
     */
269
    public function withEvent(bool $event)
270
    {
271
        $this->withEvent = $event;
272
        return $this;
273
    }
274
275
    /**
276
     * 设置是否使用路由
277
     * @access public
278
     * @param  bool $route
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
279
     * @return $this
280
     */
281
    public function withRoute(bool $route)
282
    {
283
        $this->withRoute = $route;
284
        return $this;
285
    }
286
287
    /**
288
     * 设置应用路径
289
     * @access public
290
     * @param  string $path 应用目录
291
     * @return $this
292
     */
293
    public function path(string $path)
294
    {
295
        $this->appPath = $path;
296
        return $this;
297
    }
298
299
    /**
300
     * 开启应用调试模式
301
     * @access public
302
     * @param  bool $debug 开启应用调试模式
303
     * @return $this
304
     */
305
    public function debug(bool $debug = true)
306
    {
307
        $this->appDebug = $debug;
308
        return $this;
309
    }
310
311
    /**
312
     * 是否为调试模式
313
     * @access public
314
     * @return bool
315
     */
316
    public function isDebug(): bool
317
    {
318
        return $this->appDebug;
319
    }
320
321
    /**
322
     * 设置应用名称
323
     * @access public
324
     * @param  string $name 应用名称
325
     * @return $this
326
     */
327
    public function name(string $name)
328
    {
329
        $this->name = $name;
330
        return $this;
331
    }
332
333
    /**
334
     * 设置默认应用(对多应用有效)
335
     * @access public
336
     * @param  string $name 应用名
337
     * @return $this
338
     */
339
    public function defaultApp(string $name)
340
    {
341
        $this->defaultApp = $name;
342
        return $this;
343
    }
344
345
    /**
346
     * 设置控制器层名称
347
     * @access public
348
     * @param  string $layer 控制器层名称
349
     * @return $this
350
     */
351
    public function controllerLayer(string $layer)
352
    {
353
        $this->controllerLayer = $layer;
354
        return $this;
355
    }
356
357
    /**
358
     * 设置空控制器名称
359
     * @access public
360
     * @param  string $empty 空控制器名称
361
     * @return $this
362
     */
363
    public function emptyController(string $empty)
364
    {
365
        $this->emptyController = $empty;
366
        return $this;
367
    }
368
369
    /**
370
     * 设置应用命名空间
371
     * @access public
372
     * @param  string $namespace 应用命名空间
373
     * @return $this
374
     */
375
    public function setNamespace(string $namespace)
376
    {
377
        $this->namespace = $namespace;
378
        return $this;
379
    }
380
381
    /**
382
     * 设置应用根命名空间
383
     * @access public
384
     * @param  string $rootNamespace 应用命名空间
385
     * @return $this
386
     */
387
    public function setRootNamespace(string $rootNamespace)
388
    {
389
        $this->rootNamespace = $rootNamespace;
390
        return $this;
391
    }
392
393
    /**
394
     * 设置是否启用控制器类库后缀
395
     * @access public
396
     * @param  bool  $suffix 启用控制器类库后缀
397
     * @return $this
398
     */
399
    public function controllerSuffix(bool $suffix = true)
400
    {
401
        $this->controllerSuffix = $suffix;
402
        return $this;
403
    }
404
405
    /**
406
     * 获取框架版本
407
     * @access public
408
     * @return string
409
     */
410
    public function version(): string
411
    {
412
        return static::VERSION;
413
    }
414
415
    /**
416
     * 获取应用名称
417
     * @access public
418
     * @return string
419
     */
420
    public function getName(): string
421
    {
422
        return $this->name ?: '';
423
    }
424
425
    /**
426
     * 获取应用根目录
427
     * @access public
428
     * @return string
429
     */
430
    public function getRootPath(): string
431
    {
432
        return $this->rootPath;
433
    }
434
435
    /**
436
     * 获取应用基础目录
437
     * @access public
438
     * @return string
439
     */
440
    public function getBasePath(): string
441
    {
442
        return $this->basePath;
443
    }
444
445
    /**
446
     * 获取当前应用目录
447
     * @access public
448
     * @return string
449
     */
450
    public function getAppPath(): string
451
    {
452
        return $this->appPath;
453
    }
454
455
    /**
456
     * 获取应用运行时目录
457
     * @access public
458
     * @return string
459
     */
460
    public function getRuntimePath(): string
461
    {
462
        return $this->runtimePath;
463
    }
464
465
    /**
466
     * 获取核心框架目录
467
     * @access public
468
     * @return string
469
     */
470
    public function getThinkPath(): string
471
    {
472
        return $this->thinkPath;
473
    }
474
475
    /**
476
     * 获取路由目录
477
     * @access public
478
     * @return string
479
     */
480
    public function getRoutePath(): string
481
    {
482
        return $this->routePath;
483
    }
484
485
    /**
486
     * 获取应用配置目录
487
     * @access public
488
     * @return string
489
     */
490
    public function getConfigPath(): string
491
    {
492
        return $this->configPath;
493
    }
494
495
    /**
496
     * 获取配置后缀
497
     * @access public
498
     * @return string
499
     */
500
    public function getConfigExt(): string
501
    {
502
        return $this->configExt;
503
    }
504
505
    /**
506
     * 获取应用类基础命名空间
507
     * @access public
508
     * @return string
509
     */
510
    public function getRootNamespace(): string
511
    {
512
        return $this->rootNamespace;
513
    }
514
515
    /**
516
     * 获取应用类库命名空间
517
     * @access public
518
     * @return string
519
     */
520
    public function getNamespace(): string
521
    {
522
        return $this->namespace;
523
    }
524
525
    /**
526
     * 是否启用控制器类库后缀
527
     * @access public
528
     * @return bool
529
     */
530
    public function hasControllerSuffix(): bool
531
    {
532
        return $this->controllerSuffix;
533
    }
534
535
    /**
536
     * 获取应用开启时间
537
     * @access public
538
     * @return float
539
     */
540
    public function getBeginTime(): float
541
    {
542
        return $this->beginTime;
543
    }
544
545
    /**
546
     * 获取应用初始内存占用
547
     * @access public
548
     * @return integer
549
     */
550
    public function getBeginMem(): int
551
    {
552
        return $this->beginMem;
553
    }
554
555
    /**
556
     * 获取控制器层名称
557
     * @access public
558
     * @return string
559
     */
560
    public function getControllerLayer(): string
561
    {
562
        return $this->controllerLayer;
563
    }
564
565
    /**
566
     * 初始化应用
567
     * @access public
568
     * @return $this
569
     */
570
    public function initialize()
571
    {
572
        $this->beginTime = microtime(true);
573
        $this->beginMem  = memory_get_usage();
574
575
        $this->parse();
576
577
        $this->init();
578
579
        return $this;
580
    }
581
582
    /**
583
     * 分析应用(参数)
584
     * @access protected
585
     * @return void
586
     */
587
    protected function parse(): void
588
    {
589
        if (is_file($this->rootPath . '.env')) {
590
            $this->env->load($this->rootPath . '.env');
591
        }
592
593
        $this->parseAppName();
594
595
        $this->parsePath();
596
597
        if (!$this->namespace) {
598
            $this->namespace = $this->multi ? $this->rootNamespace . '\\' . $this->name : $this->rootNamespace;
599
        }
600
601
        $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...
602
    }
603
604
    /**
605
     * 分析当前请求的应用名
606
     * @access protected
607
     * @return void
608
     */
609
    protected function parseAppName(): void
610
    {
611
        $this->urlPath = $this->request->path();
612
613
        if ($this->auto && $this->urlPath) {
614
            // 自动多应用识别
615
            $name = current(explode('/', $this->urlPath));
616
617
            if (isset($this->map[$name])) {
618
                if ($this->map[$name] instanceof \Closure) {
619
                    call_user_func_array($this->map[$name], [$this]);
620
                } else {
621
                    $this->name = $this->map[$name];
622
                }
623
            } elseif ($name && array_search($name, $this->map)) {
624
                throw new HttpException(404, 'app not exists:' . $name);
625
            } else {
626
                $this->name = $name ?: $this->defaultApp;
627
            }
628
        } elseif ($this->multi) {
629
            $this->name = $this->name ?: $this->getScriptName();
630
        }
631
632
        $this->request->setApp($this->name ?: '');
633
    }
634
635
    protected function parsePath(): void
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
636
    {
637
        if ($this->multi) {
638
            $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR . $this->name . DIRECTORY_SEPARATOR;
639
            $this->routePath   = $this->rootPath . 'route' . DIRECTORY_SEPARATOR . $this->name . DIRECTORY_SEPARATOR;
640
        } else {
641
            $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
642
            $this->routePath   = $this->rootPath . 'route' . DIRECTORY_SEPARATOR;
643
        }
644
645
        if (!$this->appPath) {
646
            $this->appPath = $this->multi ? $this->basePath . $this->name . DIRECTORY_SEPARATOR : $this->basePath;
647
        }
648
649
        $this->configPath = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
650
651
        $this->env->set([
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...
652
            'think_path'   => $this->thinkPath,
653
            'root_path'    => $this->rootPath,
654
            'app_path'     => $this->appPath,
655
            'runtime_path' => $this->runtimePath,
656
            'route_path'   => $this->routePath,
657
            'config_path'  => $this->configPath,
658
        ]);
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...
659
    }
660
661
    /**
662
     * 初始化应用
663
     * @access public
664
     * @return void
665
     */
666
    public function init(): void
667
    {
668
        // 加载初始化文件
669
        if (is_file($this->runtimePath . 'init.php')) {
670
            include $this->runtimePath . 'init.php';
671
        } else {
672
            $this->load();
673
        }
674
675
        if ($this->config->get('app.exception_handle')) {
676
            Error::setExceptionHandler($this->config->get('app.exception_handle'));
677
        }
678
679
        // 设置开启事件机制
680
        $this->event->withEvent($this->withEvent);
681
682
        // 监听AppInit
683
        $this->event->trigger('AppInit');
684
685
        $this->debugModeInit();
686
687
        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

687
        date_default_timezone_set(/** @scrutinizer ignore-type */ $this->config->get('app.default_timezone', 'Asia/Shanghai'));
Loading history...
688
    }
689
690
    /**
691
     * 加载应用文件和配置
692
     * @access protected
693
     * @return void
694
     */
695
    protected function load(): void
696
    {
697
        if ($this->multi && is_file($this->basePath . 'event.php')) {
698
            $this->loadEvent(include $this->basePath . 'event.php');
699
        }
700
701
        if (is_file($this->appPath . 'event.php')) {
702
            $this->loadEvent(include $this->appPath . 'event.php');
703
        }
704
705
        if ($this->multi && is_file($this->basePath . 'common.php')) {
706
            include_once $this->basePath . 'common.php';
707
        }
708
709
        if (is_file($this->appPath . 'common.php')) {
710
            include_once $this->appPath . 'common.php';
711
        }
712
713
        include $this->thinkPath . 'helper.php';
714
715
        if ($this->multi && is_file($this->basePath . 'middleware.php')) {
716
            $this->middleware->import(include $this->basePath . 'middleware.php');
717
        }
718
719
        if (is_file($this->appPath . 'middleware.php')) {
720
            $this->middleware->import(include $this->appPath . 'middleware.php');
721
        }
722
723
        if ($this->multi && is_file($this->basePath . 'provider.php')) {
724
            $this->bind(include $this->basePath . 'provider.php');
725
        }
726
727
        if (is_file($this->appPath . 'provider.php')) {
728
            $this->bind(include $this->appPath . 'provider.php');
729
        }
730
731
        $files = [];
732
733
        if (is_dir($this->configPath)) {
734
            $files = glob($this->configPath . '*' . $this->configExt);
735
        }
736
737
        if ($this->multi) {
738
            if (is_dir($this->appPath . 'config')) {
739
                $files = array_merge($files, glob($this->appPath . 'config' . DIRECTORY_SEPARATOR . '*' . $this->configExt));
740
            } elseif (is_dir($this->configPath . $this->name)) {
741
                $files = array_merge($files, glob($this->configPath . $this->name . DIRECTORY_SEPARATOR . '*' . $this->configExt));
742
            }
743
        }
744
745
        foreach ($files as $file) {
746
            $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
747
        }
748
    }
749
750
    /**
751
     * 调试模式设置
752
     * @access protected
753
     * @return void
754
     */
755
    protected function debugModeInit(): void
756
    {
757
        // 应用调试模式
758
        if (!$this->appDebug) {
759
            $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...
760
        }
761
762
        if (!$this->appDebug) {
763
            ini_set('display_errors', 'Off');
764
        } elseif (PHP_SAPI != 'cli') {
765
            //重新申请一块比较大的buffer
766
            if (ob_get_level() > 0) {
767
                $output = ob_get_clean();
768
            }
769
            ob_start();
770
            if (!empty($output)) {
771
                echo $output;
772
            }
773
        }
774
    }
775
776
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $event should have a doc-comment as per coding-style.
Loading history...
777
     * 注册应用事件
778
     * @access protected
779
     * @return void
780
     */
781
    protected function loadEvent(array $event): void
782
    {
783
        if (isset($event['bind'])) {
784
            $this->event->bind($event['bind']);
785
        }
786
787
        if (isset($event['listen'])) {
788
            $this->event->listenEvents($event['listen']);
789
        }
790
791
        if (isset($event['subscribe'])) {
792
            $this->event->subscribe($event['subscribe']);
793
        }
794
    }
795
796
    /**
797
     * 获取自动多应用模式下的实际URL Path
798
     * @access public
799
     * @return string
800
     */
801
    public function getRealPath(): string
802
    {
803
        $path = $this->urlPath;
804
805
        if ($path && $this->auto) {
806
            $path = substr_replace($path, '', 0, strpos($path, '/') ? strpos($path, '/') + 1 : strlen($path));
807
        }
808
809
        return $path;
810
    }
811
812
    /**
813
     * 执行应用程序
814
     * @access public
815
     * @return Response
816
     * @throws Exception
817
     */
818
    public function run(): Response
819
    {
820
        try {
821
            if ($this->withRoute) {
822
                $dispatch = $this->routeCheck()->init();
823
            } else {
824
                $dispatch = $this->route->url($this->getRealPath())->init();
825
            }
826
827
            // 监听AppBegin
828
            $this->event->trigger('AppBegin');
829
830
            $data = null;
831
        } catch (HttpResponseException $exception) {
832
            $dispatch = null;
833
            $data     = $exception->getResponse();
834
        }
835
836
        $this->middleware->add(function (Request $request, $next) use ($dispatch, $data) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

836
        $this->middleware->add(function (/** @scrutinizer ignore-unused */ Request $request, $next) use ($dispatch, $data) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $next is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

836
        $this->middleware->add(function (Request $request, /** @scrutinizer ignore-unused */ $next) use ($dispatch, $data) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
837
            return is_null($data) ? $dispatch->run() : $data;
838
        });
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...
839
840
        $response = $this->middleware->dispatch($this->request);
841
842
        // 监听AppEnd
843
        $this->event->trigger('AppEnd', $response);
844
845
        return $response;
846
    }
847
848
    /**
849
     * 实例化访问控制器
850
     * @access public
851
     * @param  string $name 资源地址
852
     * @return object
853
     * @throws ClassNotFoundException
854
     */
855
    public function controller(string $name)
856
    {
857
        $suffix = $this->controllerSuffix ? 'Controller' : '';
858
        $class  = $this->parseClass($this->controllerLayer, $name . $suffix);
859
860
        if (class_exists($class)) {
861
            return $this->make($class, [], true);
862
        } elseif ($this->emptyController && class_exists($emptyClass = $this->parseClass($this->controllerLayer, $this->emptyController . $suffix))) {
863
            return $this->make($emptyClass, [], true);
864
        }
865
866
        throw new ClassNotFoundException('class not exists:' . $class, $class);
867
    }
868
869
    /**
870
     * 解析应用类的类名
871
     * @access public
872
     * @param  string $layer  层名 controller model ...
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
873
     * @param  string $name   类名
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 3 found
Loading history...
874
     * @return string
875
     */
876
    public function parseClass(string $layer, string $name): string
877
    {
878
        $name  = str_replace(['/', '.'], '\\', $name);
879
        $array = explode('\\', $name);
880
        $class = self::parseName(array_pop($array), 1);
881
        $path  = $array ? implode('\\', $array) . '\\' : '';
882
883
        return $this->namespace . '\\' . $layer . '\\' . $path . $class;
884
    }
885
886
    /**
887
     * 获取应用根目录
888
     * @access protected
889
     * @return string
890
     */
891
    protected function getDefaultRootPath(): string
892
    {
893
        $path = dirname(dirname(dirname(dirname($this->thinkPath))));
894
895
        return $path . DIRECTORY_SEPARATOR;
896
    }
897
898
    /**
899
     * 路由初始化(路由规则注册)
900
     * @access protected
901
     * @return void
902
     */
903
    protected function routeInit(): void
904
    {
905
        // 加载路由定义
906
        if (is_dir($this->routePath)) {
907
            $files = glob($this->routePath . DIRECTORY_SEPARATOR . '*.php');
908
            foreach ($files as $file) {
909
                include $file;
910
            }
911
        }
912
913
        if ($this->route->config('route_annotation')) {
914
            // 自动生成注解路由定义
915
            if ($this->isDebug()) {
916
                $this->build->buildRoute();
917
            }
918
919
            $filename = $this->runtimePath . 'build_route.php';
920
921
            if (is_file($filename)) {
922
                include $filename;
923
            }
924
        }
925
    }
926
927
    /**
928
     * URL路由检测(根据PATH_INFO)
929
     * @access protected
930
     * @return Dispatch
931
     */
932
    protected function routeCheck(): Dispatch
933
    {
934
        // 检测路由缓存
935
        if (!$this->isDebug() && $this->route->config('route_check_cache')) {
936
            $routeKey = $this->getRouteCacheKey();
937
            $option   = $this->route->config('route_cache_option');
938
939
            if ($option && $this->cache->connect($option)->has($routeKey)) {
940
                return $this->cache->connect($option)->get($routeKey);
941
            } elseif ($this->cache->has($routeKey)) {
942
                return $this->cache->get($routeKey);
943
            }
944
        }
945
946
        $this->routeInit();
947
948
        // 路由检测
949
        $dispatch = $this->route->check($this->getRealPath());
950
951
        if (!empty($routeKey)) {
952
            try {
953
                if (!empty($option)) {
954
                    $this->cache->connect($option)->tag('route_cache')->set($routeKey, $dispatch);
955
                } else {
956
                    $this->cache->tag('route_cache')->set($routeKey, $dispatch);
957
                }
958
            } catch (\Exception $e) {
959
                // 存在闭包的时候缓存无效
960
            }
961
        }
962
963
        return $dispatch;
964
    }
965
966
    /**
967
     * 获取路由缓存Key
968
     * @access protected
969
     * @return string
970
     */
971
    protected function getRouteCacheKey(): string
972
    {
973
        if ($this->route->config('route_check_cache_key')) {
974
            $closure  = $this->route->config('route_check_cache_key');
975
            $routeKey = $closure($this->request);
976
        } else {
977
            $routeKey = md5($this->request->baseUrl(true) . ':' . $this->request->method());
978
        }
979
980
        return $routeKey;
981
    }
982
983
    /**
984
     * 获取当前运行入口名称
985
     * @access protected
986
     * @return string
987
     */
988
    protected function getScriptName(): string
989
    {
990
        if (isset($_SERVER['SCRIPT_FILENAME'])) {
991
            $file = $_SERVER['SCRIPT_FILENAME'];
992
        } elseif (isset($_SERVER['argv'][0])) {
993
            $file = realpath($_SERVER['argv'][0]);
994
        }
995
996
        return isset($file) ? pathinfo($file, PATHINFO_FILENAME) : $this->defaultApp;
997
    }
998
999
    /**
1000
     * 字符串命名风格转换
1001
     * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
1002
     * @access public
1003
     * @param  string  $name 字符串
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
1004
     * @param  integer $type 转换类型
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
1005
     * @param  bool    $ucfirst 首字母是否大写(驼峰规则)
1006
     * @return string
1007
     */
1008
    public static function parseName(string $name = null, int $type = 0, bool $ucfirst = true): string
1009
    {
1010
        if ($type) {
1011
            $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...
1012
                return strtoupper($match[1]);
1013
            }, $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...
1014
            return $ucfirst ? ucfirst($name) : lcfirst($name);
1015
        }
1016
1017
        return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
1018
    }
1019
1020
    /**
1021
     * 获取类名(不包含命名空间)
1022
     * @access public
1023
     * @param  string|object $class
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
1024
     * @return string
1025
     */
1026
    public static function classBaseName($class): string
1027
    {
1028
        $class = is_object($class) ? get_class($class) : $class;
1029
        return basename(str_replace('\\', '/', $class));
1030
    }
1031
1032
    /**
0 ignored issues
show
Coding Style introduced by
Parameter ...$args should have a doc-comment as per coding-style.
Loading history...
1033
     * 创建工厂对象实例
1034
     * @access public
1035
     * @param  string $name         工厂类名
0 ignored issues
show
Coding Style introduced by
Expected 6 spaces after parameter name; 9 found
Loading history...
1036
     * @param  string $namespace    默认命名空间
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
1037
     * @return mixed
1038
     */
1039
    public static function factory(string $name, string $namespace = '', ...$args)
1040
    {
1041
        $class = false !== strpos($name, '\\') ? $name : $namespace . ucwords($name);
1042
1043
        if (class_exists($class)) {
1044
            return Container::getInstance()->invokeClass($class, $args);
1045
        }
1046
1047
        throw new ClassNotFoundException('class not exists:' . $class, $class);
1048
    }
1049
1050
    public static function serialize($data): string
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
1051
    {
1052
        SerializableClosure::enterContext();
1053
        SerializableClosure::wrapClosures($data);
1054
        $data = \serialize($data);
1055
        SerializableClosure::exitContext();
1056
        return $data;
1057
    }
1058
1059
    public static function unserialize(string $data)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
1060
    {
1061
        SerializableClosure::enterContext();
1062
        $data = \unserialize($data);
1063
        SerializableClosure::unwrapClosures($data);
1064
        SerializableClosure::exitContext();
1065
        return $data;
1066
    }
1067
}
1068