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

Controller::init()   B

Complexity

Conditions 10
Paths 144

Size

Total Lines 41
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 13.2767

Importance

Changes 7
Bugs 0 Features 0
Metric Value
cc 10
eloc 25
c 7
b 0
f 0
nc 144
nop 1
dl 0
loc 41
ccs 17
cts 25
cp 0.68
crap 13.2767
rs 7.2999

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
// +----------------------------------------------------------------------
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