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

Controller::exec()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 43
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 5.1158

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 5
eloc 26
c 4
b 0
f 0
nc 2
nop 0
dl 0
loc 43
ccs 20
cts 24
cp 0.8333
crap 5.1158
rs 9.1928
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