Completed
Branch 6.0 (d30585)
by yun
06:27
created

Controller::exec()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 43
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 5.1314

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 5
eloc 26
nc 2
nop 0
dl 0
loc 43
ccs 19
cts 23
cp 0.8261
crap 5.1314
rs 9.1928
c 2
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2019 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 4
    public function init(App $app)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function init()
Loading history...
42
    {
43 4
        parent::init($app);
44
45 4
        $result = $this->dispatch;
46
47 4
        if (is_string($result)) {
48
            $result = explode('/', $result);
49
        }
50
51
        // 获取控制器名
52 4
        $controller = strip_tags($result[0] ?: $this->rule->config('default_controller'));
53
54 4
        if (strpos($controller, '.')) {
55
            $pos              = strrpos($controller, '.');
56
            $this->controller = substr($controller, 0, $pos) . '.' . Str::studly(substr($controller, $pos + 1));
57
        } else {
58 4
            $this->controller = Str::studly($controller);
59
        }
60
61
        // 获取操作名
62 4
        $this->actionName = strip_tags($result[1] ?: $this->rule->config('default_action'));
63
64
        // 设置当前请求的控制器、操作
65 4
        $this->request
66 4
            ->setController($this->controller)
67 4
            ->setAction($this->actionName);
68 4
    }
69
70 3
    public function exec()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function exec()
Loading history...
71
    {
72
        try {
73
            // 实例化控制器
74 3
            $instance = $this->controller($this->controller);
75
        } catch (ClassNotFoundException $e) {
76
            throw new HttpException(404, 'controller not exists:' . $e->getClass());
77
        }
78
79
        // 注册控制器中间件
80 3
        $this->registerControllerMiddleware($instance);
81
82 3
        return $this->app->middleware->pipeline('controller')
83 3
            ->send($this->request)
84
            ->then(function () use ($instance) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
85
                // 获取当前操作名
86 3
                $suffix = $this->rule->config('action_suffix');
87 3
                $action = $this->actionName . $suffix;
88
89 3
                if (is_callable([$instance, $action])) {
90 3
                    $vars = $this->request->param();
91
                    try {
92 3
                        $reflect = new ReflectionMethod($instance, $action);
93
                        // 严格获取当前操作方法名
94 1
                        $actionName = $reflect->getName();
95 1
                        if ($suffix) {
96
                            $actionName = substr($actionName, 0, -strlen($suffix));
97
                        }
98
99 1
                        $this->request->setAction($actionName);
100 2
                    } catch (ReflectionException $e) {
101 2
                        $reflect = new ReflectionMethod($instance, '__call');
102 2
                        $vars    = [$action, $vars];
103 3
                        $this->request->setAction($action);
104
                    }
105
                } else {
106
                    // 操作不存在
107
                    throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
108
                }
109
110 3
                $data = $this->app->invokeReflectMethod($instance, $reflect, $vars);
111
112 3
                return $this->autoResponse($data);
113 3
            });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
114
    }
115
116
    /**
117
     * 使用反射机制注册控制器中间件
118
     * @access public
119
     * @param object $controller 控制器实例
120
     * @return void
121
     */
122 3
    protected function registerControllerMiddleware($controller): void
123
    {
124 3
        $class = new ReflectionClass($controller);
125
126 3
        if ($class->hasProperty('middleware')) {
127 1
            $reflectionProperty = $class->getProperty('middleware');
128 1
            $reflectionProperty->setAccessible(true);
129
130 1
            $middlewares = $reflectionProperty->getValue($controller);
131
132 1
            foreach ($middlewares as $key => $val) {
133 1
                if (!is_int($key)) {
134
                    if (isset($val['only']) && !in_array($this->request->action(true), array_map(function ($item) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
135 1
                        return strtolower($item);
136 1
                    }, is_string($val['only']) ? explode(",", $val['only']) : $val['only']))) {
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
137
                        continue;
138
                    } elseif (isset($val['except']) && in_array($this->request->action(true), array_map(function ($item) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
139 1
                        return strtolower($item);
140 1
                    }, is_string($val['except']) ? explode(',', $val['except']) : $val['except']))) {
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
141 1
                        continue;
142
                    } else {
143 1
                        $val = $key;
144
                    }
145
                }
146
147 1
                if (is_string($val) && strpos($val, ':')) {
148 1
                    $val = explode(':', $val);
149 1
                    if (count($val) > 1) {
150 1
                        $val = [$val[0], array_slice($val, 1)];
151
                    }
152
                }
153
154 1
                $this->app->middleware->controller($val);
155
            }
156
        }
157 3
    }
158
159
    /**
160
     * 实例化访问控制器
161
     * @access public
162
     * @param string $name 资源地址
163
     * @return object
164
     * @throws ClassNotFoundException
165
     */
166 3
    public function controller(string $name)
167
    {
168 3
        $suffix = $this->rule->config('controller_suffix') ? 'Controller' : '';
169
170 3
        $controllerLayer = $this->rule->config('controller_layer') ?: 'controller';
171 3
        $emptyController = $this->rule->config('empty_controller') ?: 'Error';
172
173 3
        $class = $this->app->parseClass($controllerLayer, $name . $suffix);
174
175 3
        if (class_exists($class)) {
176 2
            return $this->app->make($class, [], true);
177 1
        } elseif ($emptyController && class_exists($emptyClass = $this->app->parseClass($controllerLayer, $emptyController . $suffix))) {
178 1
            return $this->app->make($emptyClass, [], true);
179
        }
180
181
        throw new ClassNotFoundException('class not exists:' . $class, $class);
182
    }
183
}
184