Passed
Push — 8.0 ( f70e0a...747446 )
by liu
02:28
created

Controller::init()   C

Complexity

Conditions 11
Paths 144

Size

Total Lines 39
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 14.4095

Importance

Changes 8
Bugs 0 Features 0
Metric Value
cc 11
eloc 23
c 8
b 0
f 0
nc 144
nop 1
dl 0
loc 39
ccs 16
cts 23
cp 0.6957
crap 14.4095
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
                $this->app->middleware->add($layer, 'route');
60
            }
61
        }
62
63
        // 获取控制器名和分层(目录)名
64 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

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

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

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

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

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

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