Passed
Push — 8.0 ( f70e0a...747446 )
by liu
02:28
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
        $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