Passed
Push — 8.0 ( 572473...9d9871 )
by liu
02:16
created

Controller::init()   C

Complexity

Conditions 11
Paths 144

Size

Total Lines 40
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 15.4801

Importance

Changes 7
Bugs 0 Features 0
Metric Value
cc 11
eloc 24
c 7
b 0
f 0
nc 144
nop 1
dl 0
loc 40
ccs 16
cts 24
cp 0.6667
crap 15.4801
rs 6.95

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
        $layer      = !empty($path) ? implode('/', $path) : '';
53
54 12
        if ($layer && !empty($this->option['auto_middleware'])) {
55
            // 自动为顶层layer注册中间件
56
            $alias = $app->config->get('middleware.alias', []);
57
58
            if (isset($alias[$layer])) {
59
                $middleware = $alias[$layer];
0 ignored issues
show
Unused Code introduced by
The assignment to $middleware is dead and can be removed.
Loading history...
60
                $this->app->middleware->add($layer, 'route');
61
            }
62
        }
63
64
        // 获取控制器名和分层(目录)名
65 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

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

66
            $pos        = strrpos(/** @scrutinizer ignore-type */ $controller, '.');
Loading history...
67
            $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

67
            $layer      = ($layer ? $layer . '.' : '') . substr(/** @scrutinizer ignore-type */ $controller, 0, $pos);
Loading history...
68
            $controller = Str::studly(substr($controller, $pos + 1));
69
        } else {
70 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

70
            $controller = Str::studly(/** @scrutinizer ignore-type */ $controller);
Loading history...
71
        }
72
73 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

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

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