Passed
Push — 8.0 ( d99d24...5fa412 )
by liu
02:46
created

Controller   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Test Coverage

Coverage 84.44%

Importance

Changes 14
Bugs 0 Features 0
Metric Value
eloc 93
c 14
b 0
f 0
dl 0
loc 187
ccs 76
cts 90
cp 0.8444
rs 9.44
wmc 37

5 Methods

Rating   Name   Duplication   Size   Complexity  
B init() 0 41 10
B exec() 0 44 6
A parseActions() 0 5 2
B controller() 0 16 7
C registerControllerMiddleware() 0 37 12
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\route\dispatch;
14
15
use ReflectionClass;
16
use ReflectionException;
17
use ReflectionMethod;
18
use think\App;
19
use think\exception\ClassNotFoundException;
20
use think\exception\HttpException;
21
use think\helper\Str;
22
use think\route\Dispatch;
23
24
/**
25
 * Controller Dispatcher
26
 */
27
class Controller extends Dispatch
28
{
29
    /**
30
     * 控制器分级
31
     * @var string
32
     */
33
    protected $layer;
34
35
    /**
36
     * 控制器名
37
     * @var string
38
     */
39
    protected $controller;
40
41
    /**
42
     * 操作名
43
     * @var string
44
     */
45
    protected $actionName;
46
47 12
    public function init(App $app)
48
    {
49 12
        parent::init($app);
50
51 12
        $path = $this->dispatch;
52 12
        if (is_string($path)) {
53
            $path = explode('/', $path);
54
        }
55
56 12
        $action     = !empty($path) ? array_pop($path) : $this->rule->config('default_action');
57 12
        $controller = !empty($path) ? array_pop($path) : $this->rule->config('default_controller');
58 12
        $layer      = !empty($path) ? implode('/', $path) : '';
59
60 12
        if ($layer && !empty($this->option['auto_middleware'])) {
61
            // 自动为顶层layer注册中间件
62
            $alias = $app->config->get('middleware.alias', []);
63
64
            if (isset($alias[$layer])) {
65
                $middleware = $alias[$layer];
0 ignored issues
show
Unused Code introduced by
The assignment to $middleware is dead and can be removed.
Loading history...
66
                $this->app->middleware->add($layer, 'route');
67
            }
68
        }
69
70
        // 获取控制器名和分层(目录)名
71 12
        if (str_contains($controller, '.')) {
0 ignored issues
show
Bug introduced by
It seems like $controller can also be of type null; however, parameter $haystack of str_contains() 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

71
        if (str_contains(/** @scrutinizer ignore-type */ $controller, '.')) {
Loading history...
72
            $pos        = strrpos($controller, '.');
0 ignored issues
show
Bug introduced by
It seems like $controller can also be of type null; however, parameter $haystack of strrpos() 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

72
            $pos        = strrpos(/** @scrutinizer ignore-type */ $controller, '.');
Loading history...
73
            $layer      = ($layer ? $layer . '.' : '') . substr($controller, 0, $pos);
0 ignored issues
show
Bug introduced by
It seems like $controller can also be of type null; however, parameter $string of substr() 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

73
            $layer      = ($layer ? $layer . '.' : '') . substr(/** @scrutinizer ignore-type */ $controller, 0, $pos);
Loading history...
74
            $controller = Str::studly(substr($controller, $pos + 1));
75
        } else {
76 12
            $controller = Str::studly($controller);
0 ignored issues
show
Bug introduced by
It seems like $controller can also be of type null; however, parameter $value of think\helper\Str::studly() 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

76
            $controller = Str::studly(/** @scrutinizer ignore-type */ $controller);
Loading history...
77
        }
78
79 12
        $this->actionName = strip_tags($action);
0 ignored issues
show
Bug introduced by
It seems like $action can also be of type null; however, parameter $string of strip_tags() 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

79
        $this->actionName = strip_tags(/** @scrutinizer ignore-type */ $action);
Loading history...
80 12
        $this->controller = strip_tags($controller);
81 12
        $this->layer      = strip_tags($layer);
82
83
        // 设置当前请求的控制器、操作
84 12
        $this->request
85 12
            ->setLayer($this->layer)
86 12
            ->setController($this->controller)
87 12
            ->setAction($this->actionName);
88
    }
89
90 12
    public function exec()
91
    {
92
        try {
93
            // 实例化控制器
94 12
            $controller = ($this->layer ? $this->layer . '.' : '') . $this->controller;
95 12
            $instance = $this->controller($controller);
96
        } catch (ClassNotFoundException $e) {
97
            throw new HttpException(404, 'controller not exists:' . $e->getClass());
98
        }
99
100
        // 注册控制器中间件
101 12
        $this->registerControllerMiddleware($instance);
102
103 12
        return $this->app->middleware->pipeline('controller')
104 12
            ->send($this->request)
105 12
            ->then(function () use ($instance) {
106
                // 获取当前操作名
107 12
                $suffix = $this->rule->config('action_suffix');
108 12
                $action = $this->actionName . $suffix;
109
110 12
                if (is_callable([$instance, $action])) {
111 12
                    $vars = array_merge($this->request->get(), $this->param);
0 ignored issues
show
Bug introduced by
$this->request->get() of type null|object is incompatible with the type array expected by parameter $arrays of array_merge(). ( Ignorable by Annotation )

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

111
                    $vars = array_merge(/** @scrutinizer ignore-type */ $this->request->get(), $this->param);
Loading history...
112
                    try {
113 12
                        $reflect = new ReflectionMethod($instance, $action);
114
                        // 严格获取当前操作方法名
115 3
                        $actionName = $reflect->getName();
116 3
                        if ($suffix) {
117
                            $actionName = substr($actionName, 0, -strlen($suffix));
118
                        }
119
120 3
                        $this->request->setAction($actionName);
121 9
                    } catch (ReflectionException $e) {
122 9
                        $reflect = new ReflectionMethod($instance, '__call');
123 9
                        $vars    = [$action, $vars];
124 11
                        $this->request->setAction($action);
125
                    }
126
                } else {
127
                    // 操作不存在
128
                    throw new HttpException(404, 'method not exists:' . $instance::class . '->' . $action . '()');
129
                }
130
131 12
                $data = $this->app->invokeReflectMethod($instance, $reflect, $vars);
132
133 12
                return $this->autoResponse($data);
134 12
            });
135
    }
136
137 3
    protected function parseActions($actions)
138
    {
139 3
        return array_map(function ($item) {
140 3
            return strtolower($item);
141 3
        }, is_string($actions) ? explode(",", $actions) : $actions);
142
    }
143
144
    /**
145
     * 使用反射机制注册控制器中间件
146
     * @access public
147
     * @param object $controller 控制器实例
148
     * @return void
149
     */
150 12
    protected function registerControllerMiddleware($controller): void
151
    {
152 12
        $class = new ReflectionClass($controller);
153
154 12
        if ($class->hasProperty('middleware')) {
155 6
            $reflectionProperty = $class->getProperty('middleware');
156 6
            $reflectionProperty->setAccessible(true);
157
158 6
            $middlewares = $reflectionProperty->getValue($controller);
159 6
            $action      = $this->request->action(true);
160
161 6
            foreach ($middlewares as $key => $val) {
162 3
                if (!is_int($key)) {
163 3
                    $middleware = $key;
164 3
                    $options    = $val;
165 3
                } elseif (isset($val['middleware'])) {
166 3
                    $middleware = $val['middleware'];
167 3
                    $options    = $val['options'] ?? [];
168
                } else {
169 3
                    $middleware = $val;
170 3
                    $options    = [];
171
                }
172
173 3
                if (isset($options['only']) && !in_array($action, $this->parseActions($options['only']))) {
174
                    continue;
175 3
                } elseif (isset($options['except']) && in_array($action, $this->parseActions($options['except']))) {
176 3
                    continue;
177
                }
178
179 3
                if (is_string($middleware) && str_contains($middleware, ':')) {
180 3
                    $middleware = explode(':', $middleware);
181 3
                    if (count($middleware) > 1) {
182 3
                        $middleware = [$middleware[0], array_slice($middleware, 1)];
183
                    }
184
                }
185
186 3
                $this->app->middleware->controller($middleware);
187
            }
188
        }
189
    }
190
191
    /**
192
     * 实例化访问控制器
193
     * @access public
194
     * @param string $name 资源地址
195
     * @return object
196
     * @throws ClassNotFoundException
197
     */
198 12
    public function controller(string $name)
199
    {
200 12
        $suffix = $this->rule->config('controller_suffix') ? 'Controller' : '';
201
202 12
        $controllerLayer = $this->rule->config('controller_layer') ?: 'controller';
203 12
        $emptyController = $this->rule->config('empty_controller') ?: 'Error';
204
205 12
        $class = $this->app->parseClass($controllerLayer, $name . $suffix);
206
207 12
        if (class_exists($class)) {
208 9
            return $this->app->make($class, [], true);
209 3
        } elseif ($emptyController && class_exists($emptyClass = $this->app->parseClass($controllerLayer, $emptyController . $suffix))) {
210 3
            return $this->app->make($emptyClass, [], true);
211
        }
212
213
        throw new ClassNotFoundException('class not exists:' . $class, $class);
214
    }
215
}
216