Passed
Push — 8.0 ( 7f09d5...656701 )
by liu
02:22
created

Controller::init()   C

Complexity

Conditions 11
Paths 288

Size

Total Lines 43
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 19.1817

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 11
eloc 27
c 2
b 0
f 0
nc 288
nop 1
dl 0
loc 43
ccs 16
cts 27
cp 0.5926
crap 19.1817
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
            $name = 
0 ignored issues
show
Unused Code introduced by
The assignment to $name is dead and can be removed.
Loading history...
75
            $middleware = $this->app->config->get('middleware.' . $module, []);
76
77
            if (is_array($middleware) && !empty($middleware)) {
78
                $this->app->middleware->import($middleware, 'module');
79
            }
80
81
            $this->app->middleware->pipeline('module')
82
                ->send($this->request)
83
                ->then(function () {
84
                });
85
        }
86
    }
87
88 12
    public function exec()
89
    {
90
        try {
91
            // 实例化控制器
92 12
            $instance = $this->controller($this->controller);
93
        } catch (ClassNotFoundException $e) {
94
            throw new HttpException(404, 'controller not exists:' . $e->getClass());
95
        }
96
97
        // 注册控制器中间件
98 12
        $this->registerControllerMiddleware($instance);
99
100 12
        return $this->app->middleware->pipeline('controller')
101 12
            ->send($this->request)
102 12
            ->then(function () use ($instance) {
103
                // 获取当前操作名
104 12
                $suffix = $this->rule->config('action_suffix');
105 12
                $action = $this->actionName . $suffix;
106
107 12
                if (is_callable([$instance, $action])) {
108 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

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