Passed
Push — 5.2 ( c447dc...38a73e )
by liu
02:35
created

App::version()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: liu21st <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think;
14
15
use Opis\Closure\SerializableClosure;
16
use think\exception\ClassNotFoundException;
17
use think\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
     * 配置后缀
131
     * @var string
132
     */
133
    protected $configExt = '.php';
134
135
    /**
136
     * 初始化
137
     * @var bool
138
     */
139
    protected $initialized = false;
140
141
    /**
142
     * 是否为多应用模式
143
     * @var bool
144
     */
145
    protected $multi = false;
146
147
    /**
148
     * 是否为自动多应用
149
     * @var bool
150
     */
151
    protected $auto = false;
152
153
    /**
154
     * 应用映射
155
     * @var array
156
     */
157
    protected $map = [];
158
159
    /**
160
     * 是否需要事件响应
161
     * @var bool
162
     */
163
    protected $withEvent = true;
164
165
    /**
166
     * 是否需要使用路由
167
     * @var bool
168
     */
169
    protected $withRoute = true;
170
171
    /**
172
     * 访问控制器层名称
173
     * @var string
174
     */
175
    protected $controllerLayer = 'controller';
176
177
    /**
178
     * 是否使用控制器类库后缀
179
     * @var bool
180
     */
181
    protected $controllerSuffix = false;
182
183
    /**
184
     * 空控制器名称
185
     * @var string
186
     */
187
    protected $emptyController = 'Error';
188
189
    /**
190
     * 架构方法
191
     * @access public
192
     * @param  string $rootPath 应用根目录
193
     */
194
    public function __construct(string $rootPath = '')
195
    {
196
        $this->thinkPath = dirname(__DIR__) . DIRECTORY_SEPARATOR;
197
        $this->rootPath  = $rootPath ? realpath($rootPath) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
198
        $this->basePath  = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
199
200
        $this->multi = is_dir($this->basePath . 'controller') ? false : true;
201
202
        static::setInstance($this);
203
204
        $this->instance('app', $this);
205
206
        // 注册错误和异常处理机制
207
        Error::register();
208
    }
209
210
    /**
211
     * 自动多应用访问
212
     * @access public
213
     * @param  array $map 应用路由映射
214
     * @return $this
215
     */
216
    public function autoMulti(array $map = [])
217
    {
218
        $this->multi = true;
219
        $this->auto  = true;
220
        $this->map   = $map;
221
222
        return $this;
223
    }
224
225
    /**
226
     * 是否为自动多应用模式
227
     * @access public
228
     * @return bool
229
     */
230
    public function isAutoMulti(): bool
231
    {
232
        return $this->auto;
233
    }
234
235
    /**
236
     * 设置应用模式
237
     * @access public
238
     * @param  bool $multi
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
239
     * @return $this
240
     */
241
    public function multi(bool $multi)
242
    {
243
        $this->multi = $multi;
244
        return $this;
245
    }
246
247
    /**
248
     * 是否为多应用模式
249
     * @access public
250
     * @return bool
251
     */
252
    public function isMulti(): bool
253
    {
254
        return $this->multi;
255
    }
256
257
    /**
258
     * 设置是否使用事件机制
259
     * @access public
260
     * @param  bool $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
261
     * @return $this
262
     */
263
    public function withEvent(bool $event)
264
    {
265
        $this->withEvent = $event;
266
        return $this;
267
    }
268
269
    /**
270
     * 设置是否使用路由
271
     * @access public
272
     * @param  bool $route
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
273
     * @return $this
274
     */
275
    public function withRoute(bool $route)
276
    {
277
        $this->withRoute = $route;
278
        return $this;
279
    }
280
281
    /**
282
     * 设置应用路径
283
     * @access public
284
     * @param  string $path 应用目录
285
     * @return $this
286
     */
287
    public function path(string $path)
288
    {
289
        $this->appPath = $path;
290
        return $this;
291
    }
292
293
    /**
294
     * 开启应用调试模式
295
     * @access public
296
     * @param  bool $debug 开启应用调试模式
297
     * @return $this
298
     */
299
    public function debug(bool $debug = true)
300
    {
301
        $this->appDebug = $debug;
302
        return $this;
303
    }
304
305
    /**
306
     * 是否为调试模式
307
     * @access public
308
     * @return bool
309
     */
310
    public function isDebug(): bool
311
    {
312
        return $this->appDebug;
313
    }
314
315
    /**
316
     * 设置应用名称
317
     * @access public
318
     * @param  string $name 应用名称
319
     * @return $this
320
     */
321
    public function name(string $name)
322
    {
323
        $this->name = $name;
324
        return $this;
325
    }
326
327
    /**
328
     * 设置默认应用(对多应用有效)
329
     * @access public
330
     * @param  string $name 应用名
331
     * @return $this
332
     */
333
    public function defaultApp(string $name)
334
    {
335
        $this->defaultApp = $name;
336
        return $this;
337
    }
338
339
    /**
340
     * 设置控制器层名称
341
     * @access public
342
     * @param  string $layer 控制器层名称
343
     * @return $this
344
     */
345
    public function controllerLayer(string $layer)
346
    {
347
        $this->controllerLayer = $layer;
348
        return $this;
349
    }
350
351
    /**
352
     * 设置空控制器名称
353
     * @access public
354
     * @param  string $empty 空控制器名称
355
     * @return $this
356
     */
357
    public function emptyController(string $empty)
358
    {
359
        $this->emptyController = $empty;
360
        return $this;
361
    }
362
363
    /**
364
     * 设置应用命名空间
365
     * @access public
366
     * @param  string $namespace 应用命名空间
367
     * @return $this
368
     */
369
    public function setNamespace(string $namespace)
370
    {
371
        $this->namespace = $namespace;
372
        return $this;
373
    }
374
375
    /**
376
     * 设置应用根命名空间
377
     * @access public
378
     * @param  string $rootNamespace 应用命名空间
379
     * @return $this
380
     */
381
    public function setRootNamespace(string $rootNamespace)
382
    {
383
        $this->rootNamespace = $rootNamespace;
384
        return $this;
385
    }
386
387
    /**
388
     * 设置是否启用控制器类库后缀
389
     * @access public
390
     * @param  bool  $suffix 启用控制器类库后缀
391
     * @return $this
392
     */
393
    public function controllerSuffix(bool $suffix = true)
394
    {
395
        $this->controllerSuffix = $suffix;
396
        return $this;
397
    }
398
399
    /**
400
     * 获取框架版本
401
     * @access public
402
     * @return string
403
     */
404
    public function version(): string
405
    {
406
        return static::VERSION;
407
    }
408
409
    /**
410
     * 获取应用名称
411
     * @access public
412
     * @return string
413
     */
414
    public function getName(): string
415
    {
416
        return $this->name ?: '';
417
    }
418
419
    /**
420
     * 获取应用根目录
421
     * @access public
422
     * @return string
423
     */
424
    public function getRootPath(): string
425
    {
426
        return $this->rootPath;
427
    }
428
429
    /**
430
     * 获取应用基础目录
431
     * @access public
432
     * @return string
433
     */
434
    public function getBasePath(): string
435
    {
436
        return $this->basePath;
437
    }
438
439
    /**
440
     * 获取当前应用目录
441
     * @access public
442
     * @return string
443
     */
444
    public function getAppPath(): string
445
    {
446
        return $this->appPath;
447
    }
448
449
    /**
450
     * 获取应用运行时目录
451
     * @access public
452
     * @return string
453
     */
454
    public function getRuntimePath(): string
455
    {
456
        return $this->runtimePath;
457
    }
458
459
    /**
460
     * 获取核心框架目录
461
     * @access public
462
     * @return string
463
     */
464
    public function getThinkPath(): string
465
    {
466
        return $this->thinkPath;
467
    }
468
469
    /**
470
     * 获取路由目录
471
     * @access public
472
     * @return string
473
     */
474
    public function getRoutePath(): string
475
    {
476
        return $this->routePath;
477
    }
478
479
    /**
480
     * 获取应用配置目录
481
     * @access public
482
     * @return string
483
     */
484
    public function getConfigPath(): string
485
    {
486
        return $this->configPath;
487
    }
488
489
    /**
490
     * 获取配置后缀
491
     * @access public
492
     * @return string
493
     */
494
    public function getConfigExt(): string
495
    {
496
        return $this->configExt;
497
    }
498
499
    /**
500
     * 获取应用类基础命名空间
501
     * @access public
502
     * @return string
503
     */
504
    public function getRootNamespace(): string
505
    {
506
        return $this->rootNamespace;
507
    }
508
509
    /**
510
     * 获取应用类库命名空间
511
     * @access public
512
     * @return string
513
     */
514
    public function getNamespace(): string
515
    {
516
        return $this->namespace;
517
    }
518
519
    /**
520
     * 是否启用控制器类库后缀
521
     * @access public
522
     * @return bool
523
     */
524
    public function hasControllerSuffix(): bool
525
    {
526
        return $this->controllerSuffix;
527
    }
528
529
    /**
530
     * 获取应用开启时间
531
     * @access public
532
     * @return float
533
     */
534
    public function getBeginTime(): float
535
    {
536
        return $this->beginTime;
537
    }
538
539
    /**
540
     * 获取应用初始内存占用
541
     * @access public
542
     * @return integer
543
     */
544
    public function getBeginMem(): int
545
    {
546
        return $this->beginMem;
547
    }
548
549
    /**
550
     * 获取控制器层名称
551
     * @access public
552
     * @return string
553
     */
554
    public function getControllerLayer(): string
555
    {
556
        return $this->controllerLayer;
557
    }
558
559
    /**
560
     * 初始化应用
561
     * @access public
562
     * @return $this
563
     */
564
    public function initialize()
565
    {
566
        $this->beginTime = microtime(true);
567
        $this->beginMem  = memory_get_usage();
568
569
        if (is_file($this->rootPath . '.env')) {
570
            $this->env->load($this->rootPath . '.env');
571
        }
572
573
        $this->parse();
574
575
        $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...
576
577
        $this->init();
578
579
        $this->debugModeInit();
580
581
        if ($this->config->get('app.exception_handle')) {
582
            Error::setExceptionHandler($this->config->get('app.exception_handle'));
583
        }
584
585
        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

585
        date_default_timezone_set(/** @scrutinizer ignore-type */ $this->config->get('app.default_timezone', 'Asia/Shanghai'));
Loading history...
586
587
        // 设置开启事件机制
588
        $this->event->withEvent($this->withEvent);
589
590
        return $this;
591
    }
592
593
    protected function debugModeInit(): void
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
594
    {
595
        // 应用调试模式
596
        if (!$this->appDebug) {
597
            $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...
598
        }
599
600
        if (!$this->appDebug) {
601
            ini_set('display_errors', 'Off');
602
        } elseif (PHP_SAPI != 'cli') {
603
            //重新申请一块比较大的buffer
604
            if (ob_get_level() > 0) {
605
                $output = ob_get_clean();
606
            }
607
            ob_start();
608
            if (!empty($output)) {
609
                echo $output;
610
            }
611
        }
612
    }
613
614
    /**
615
     * 分析应用(参数)
616
     * @access protected
617
     * @return void
618
     */
619
    protected function parse(): void
620
    {
621
        if ($this->multi) {
622
            $this->namespace = null;
623
            $this->appPath   = null;
624
625
            $this->parseAppName();
626
627
            $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR . $this->name . DIRECTORY_SEPARATOR;
628
            $this->routePath   = $this->rootPath . 'route' . DIRECTORY_SEPARATOR . $this->name . DIRECTORY_SEPARATOR;
629
        } else {
630
            $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
631
            $this->routePath   = $this->rootPath . 'route' . DIRECTORY_SEPARATOR;
632
        }
633
634
        if (!$this->appPath) {
635
            $this->appPath = $this->multi ? $this->basePath . $this->name . DIRECTORY_SEPARATOR : $this->basePath;
636
        }
637
638
        if (!$this->namespace) {
639
            $this->namespace = $this->multi ? $this->rootNamespace . '\\' . $this->name : $this->rootNamespace;
640
        }
641
642
        $this->configPath = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
643
644
        $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...
645
            'think_path'   => $this->thinkPath,
646
            'root_path'    => $this->rootPath,
647
            'app_path'     => $this->appPath,
648
            'runtime_path' => $this->runtimePath,
649
            'route_path'   => $this->routePath,
650
            'config_path'  => $this->configPath,
651
        ]);
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...
652
    }
653
654
    /**
655
     * 分析当前请求的应用名(多应用模式下)
656
     * @access protected
657
     * @return void
658
     */
659
    protected function parseAppName(): void
660
    {
661
        $path = $this->request->path();
662
663
        if ($this->auto && $path) {
664
            // 自动多应用识别
665
            $name = current(explode('/', $path));
666
667
            if (isset($this->map[$name])) {
668
                if ($this->map[$name] instanceof \Closure) {
669
                    call_user_func_array($this->map[$name], [$this]);
670
                } else {
671
                    $this->name = $this->map[$name];
672
                }
673
            } elseif ($name && array_search($name, $this->map)) {
674
                throw new HttpException(404, 'app not exists:' . $name);
675
            } else {
676
                $this->name = $name ?: $this->defaultApp;
677
            }
678
        } else {
679
            $this->name = $this->name ?: $this->getScriptName();
680
        }
681
    }
682
683
    /**
684
     * 初始化应用
685
     * @access public
686
     * @return void
687
     */
688
    public function init(): void
689
    {
690
        // 加载初始化文件
691
        if (is_file($this->runtimePath . 'init.php')) {
692
            include $this->runtimePath . 'init.php';
693
        } else {
694
            $this->loadAppFile();
695
        }
696
697
        $this->request->setApp($this->name ?: '');
698
699
        // 监听AppInit
700
        $this->event->trigger('AppInit');
701
    }
702
703
    /**
704
     * 加载应用文件和配置
705
     * @access protected
706
     * @return void
707
     */
708
    protected function loadAppFile(): void
709
    {
710
        if ($this->multi && is_file($this->basePath . 'event.php')) {
711
            $this->loadEvent(include $this->basePath . 'event.php');
712
        }
713
714
        if (is_file($this->appPath . 'event.php')) {
715
            $this->loadEvent(include $this->appPath . 'event.php');
716
        }
717
718
        if ($this->multi && is_file($this->basePath . 'common.php')) {
719
            include_once $this->basePath . 'common.php';
720
        }
721
722
        if (is_file($this->appPath . 'common.php')) {
723
            include_once $this->appPath . 'common.php';
724
        }
725
726
        include $this->thinkPath . 'helper.php';
727
728
        if ($this->multi && is_file($this->basePath . 'middleware.php')) {
729
            $this->middleware->import(include $this->basePath . 'middleware.php');
730
        }
731
732
        if (is_file($this->appPath . 'middleware.php')) {
733
            $this->middleware->import(include $this->appPath . 'middleware.php');
734
        }
735
736
        if ($this->multi && is_file($this->basePath . 'provider.php')) {
737
            $this->bind(include $this->basePath . 'provider.php');
738
        }
739
740
        if (is_file($this->appPath . 'provider.php')) {
741
            $this->bind(include $this->appPath . 'provider.php');
742
        }
743
744
        $files = [];
745
746
        if (is_dir($this->configPath)) {
747
            $files = glob($this->configPath . '*' . $this->configExt);
748
        }
749
750
        if ($this->multi) {
751
            if (is_dir($this->appPath . 'config')) {
752
                $files = array_merge($files, glob($this->appPath . 'config' . DIRECTORY_SEPARATOR . '*' . $this->configExt));
753
            } elseif (is_dir($this->configPath . $this->name)) {
754
                $files = array_merge($files, glob($this->configPath . $this->name . DIRECTORY_SEPARATOR . '*' . $this->configExt));
755
            }
756
        }
757
758
        foreach ($files as $file) {
759
            $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
760
        }
761
    }
762
763
    protected function loadEvent(array $event): void
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
764
    {
765
        if (isset($event['bind'])) {
766
            $this->event->bind($event['bind']);
767
        }
768
769
        if (isset($event['listen'])) {
770
            $this->event->listenEvents($event['listen']);
771
        }
772
773
        if (isset($event['subscribe'])) {
774
            $this->event->subscribe($event['subscribe']);
775
        }
776
    }
777
778
    public function getRealPath()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
779
    {
780
        $path = $this->request->path();
781
782
        if ($path && $this->auto) {
783
            $path = substr_replace($path, '', 0, strpos($path, '/') ? strpos($path, '/') + 1 : strlen($path));
784
        }
785
786
        return $path;
787
    }
788
789
    /**
790
     * 执行应用程序
791
     * @access public
792
     * @return Response
793
     * @throws Exception
794
     */
795
    public function run(): Response
796
    {
797
        try {
798
            if ($this->withRoute) {
799
                $dispatch = $this->routeCheck()->init();
800
            } else {
801
                $dispatch = $this->route->url($this->getRealPath())->init();
802
            }
803
804
            // 监听AppBegin
805
            $this->event->trigger('AppBegin');
806
807
            $data = null;
808
        } catch (HttpResponseException $exception) {
809
            $dispatch = null;
810
            $data     = $exception->getResponse();
811
        }
812
813
        $this->middleware->add(function (Request $request, $next) use ($dispatch, $data) {
0 ignored issues
show
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

813
        $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...
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

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