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

Controller::parseActions()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 1
nop 1
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
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