Passed
Push — 8.0 ( 656701...f6550d )
by liu
02:18
created

Controller::init()   C

Complexity

Conditions 11
Paths 288

Size

Total Lines 42
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 17.8835

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 11
eloc 26
c 3
b 0
f 0
nc 288
nop 1
dl 0
loc 42
ccs 16
cts 26
cp 0.6153
crap 17.8835
rs 5.3833

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 $controller;
34
35
    /**
36
     * 操作名
37
     * @var string
38
     */
39
    protected $actionName;
40
41 12
    public function init(App $app)
42
    {
43 12
        parent::init($app);
44
45 12
        $path = $this->dispatch;
46 12
        if (is_string($path)) {
47
            $path = explode('/', $path);
48
        }
49
50 12
        $action     = !empty($path) ? array_pop($path) : $this->rule->config('default_action');
51 12
        $controller = !empty($path) ? array_pop($path) : $this->rule->config('default_controller');
52 12
        $module     = !empty($path) ? array_pop($path) : '';
53
54
        // 获取控制器名
55 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

55
        if (str_contains(/** @scrutinizer ignore-type */ $controller, '.')) {
Loading history...
56
            $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

56
            $pos        = strrpos(/** @scrutinizer ignore-type */ $controller, '.');
Loading history...
57
            $module     = ($module ? $module . '.' : '') . 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

57
            $module     = ($module ? $module . '.' : '') . substr(/** @scrutinizer ignore-type */ $controller, 0, $pos);
Loading history...
58
            $controller = Str::studly(substr($controller, $pos + 1));
59
        } else {
60 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

60
            $controller = Str::studly(/** @scrutinizer ignore-type */ $controller);
Loading history...
61
        }
62
63 12
        $this->actionName = $action;
64 12
        $this->controller = ($module ? $module . '.' : '') . $controller;
65
66
        // 设置当前请求的控制器、操作
67 12
        $this->request
68 12
            ->setModule($module)
69 12
            ->setController($this->controller)
70 12
            ->setAction($this->actionName);
0 ignored issues
show
Bug introduced by
It seems like $this->actionName can also be of type null; however, parameter $action of think\Request::setAction() 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

70
            ->setAction(/** @scrutinizer ignore-type */ $this->actionName);
Loading history...
71
72
        // 注册模块中间件
73 12
        if ($module) {
74
            $middleware = $this->app->config->get('middleware.' . $module, []);
75
76
            if (is_array($middleware) && !empty($middleware)) {
77
                $this->app->middleware->import($middleware, 'module');
78
            }
79
80
            $this->app->middleware->pipeline('module')
81
                ->send($this->request)
82
                ->then(function () {
83
                });
84
        }
85
    }
86
87 12
    public function exec()
88
    {
89
        try {
90
            // 实例化控制器
91 12
            $instance = $this->controller($this->controller);
92
        } catch (ClassNotFoundException $e) {
93
            throw new HttpException(404, 'controller not exists:' . $e->getClass());
94
        }
95
96
        // 注册控制器中间件
97 12
        $this->registerControllerMiddleware($instance);
98
99 12
        return $this->app->middleware->pipeline('controller')
100 12
            ->send($this->request)
101 12
            ->then(function () use ($instance) {
102
                // 获取当前操作名
103 12
                $suffix = $this->rule->config('action_suffix');
104 12
                $action = $this->actionName . $suffix;
105
106 12
                if (is_callable([$instance, $action])) {
107 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

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