Completed
Push — 6.0 ( 7d6f90...b04f47 )
by yun
02:12
created

Dispatch   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 232
Duplicated Lines 0 %

Test Coverage

Coverage 39.13%

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 86
c 6
b 0
f 0
dl 0
loc 232
ccs 36
cts 92
cp 0.3913
rs 9.52
wmc 36

12 Methods

Rating   Name   Duplication   Size   Complexity  
B createBindModel() 0 35 8
A __wakeup() 0 4 1
A __sleep() 0 3 1
A getDispatch() 0 3 1
A doRouteAfter() 0 27 5
A getParam() 0 3 1
A __debugInfo() 0 6 1
A autoValidate() 0 24 4
A __construct() 0 6 1
A run() 0 14 5
A init() 0 6 1
B autoResponse() 0 17 7
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;
14
15
use think\App;
16
use think\Container;
17
use think\Request;
18
use think\Response;
19
use think\Validate;
20
21
/**
22
 * 路由调度基础类
23
 */
24
abstract class Dispatch
25
{
26
    /**
27
     * 应用对象
28
     * @var \think\App
29
     */
30
    protected $app;
31
32
    /**
33
     * 请求对象
34
     * @var Request
35
     */
36
    protected $request;
37
38
    /**
39
     * 路由规则
40
     * @var Rule
41
     */
42
    protected $rule;
43
44
    /**
45
     * 调度信息
46
     * @var mixed
47
     */
48
    protected $dispatch;
49
50
    /**
51
     * 路由变量
52
     * @var array
53
     */
54
    protected $param;
55
56
57 33
    public function __construct(Request $request, Rule $rule, $dispatch, array $param = [])
58
    {
59 33
        $this->request  = $request;
60 33
        $this->rule     = $rule;
61 33
        $this->dispatch = $dispatch;
62 33
        $this->param    = $param;
63 33
    }
64
65 33
    public function init(App $app)
66
    {
67 33
        $this->app = $app;
68
69
        // 执行路由后置操作
70 33
        $this->doRouteAfter();
71 33
    }
72
73
    /**
74
     * 执行路由调度
75
     * @access public
76
     * @return mixed
77
     */
78 33
    public function run(): Response
79
    {
80 33
        if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) {
81 3
            $rules = $this->rule->getRouter()->getRule($this->rule->getRule());
82 3
            $allow = [];
83 3
            foreach ($rules as $item) {
84 3
                $allow[] = strtoupper($item->getMethod());
85
            }
86
87 3
            return Response::create('', 'html', 204)->header(['Allow' => implode(', ', $allow)]);
88
        }
89
90 33
        $data = $this->exec();
91 33
        return $this->autoResponse($data);
92
    }
93
94 33
    protected function autoResponse($data): Response
95
    {
96 33
        if ($data instanceof Response) {
97 27
            $response = $data;
98 21
        } elseif (!is_null($data)) {
99
            // 默认自动识别响应输出类型
100 21
            $type     = $this->request->isJson() ? 'json' : 'html';
101 21
            $response = Response::create($data, $type);
102
        } else {
103
            $data = ob_get_clean();
104
105
            $content  = false === $data ? '' : $data;
106
            $status   = '' === $content && $this->request->isJson() ? 204 : 200;
107
            $response = Response::create($content, 'html', $status);
108
        }
109
110 33
        return $response;
111
    }
112
113
    /**
114
     * 检查路由后置操作
115
     * @access protected
116
     * @return void
117
     */
118 33
    protected function doRouteAfter(): void
119
    {
120 33
        $option = $this->rule->getOption();
121
122
        // 添加中间件
123 33
        if (!empty($option['middleware'])) {
124 3
            $this->app->middleware->import($option['middleware'], 'route');
125
        }
126
127 33
        if (!empty($option['append'])) {
128
            $this->param = array_merge($this->param, $option['append']);
129
        }
130
131
        // 绑定模型数据
132 33
        if (!empty($option['model'])) {
133
            $this->createBindModel($option['model'], $this->param);
134
        }
135
136
        // 记录当前请求的路由规则
137 33
        $this->request->setRule($this->rule);
138
139
        // 记录路由变量
140 33
        $this->request->setRoute($this->param);
141
142
        // 数据自动验证
143 33
        if (isset($option['validate'])) {
144
            $this->autoValidate($option['validate']);
145
        }
146 33
    }
147
148
    /**
149
     * 路由绑定模型实例
150
     * @access protected
151
     * @param array $bindModel 绑定模型
152
     * @param array $matches   路由变量
153
     * @return void
154
     */
155
    protected function createBindModel(array $bindModel, array $matches): void
156
    {
157
        foreach ($bindModel as $key => $val) {
158
            if ($val instanceof \Closure) {
159
                $result = $this->app->invokeFunction($val, $matches);
160
            } else {
161
                $fields = explode('&', $key);
162
163
                if (is_array($val)) {
164
                    [$model, $exception] = $val;
165
                } else {
166
                    $model     = $val;
167
                    $exception = true;
168
                }
169
170
                $where = [];
171
                $match = true;
172
173
                foreach ($fields as $field) {
174
                    if (!isset($matches[$field])) {
175
                        $match = false;
176
                        break;
177
                    } else {
178
                        $where[] = [$field, '=', $matches[$field]];
179
                    }
180
                }
181
182
                if ($match) {
183
                    $result = $model::where($where)->failException($exception)->find();
184
                }
185
            }
186
187
            if (!empty($result)) {
188
                // 注入容器
189
                $this->app->instance(get_class($result), $result);
190
            }
191
        }
192
    }
193
194
    /**
195
     * 验证数据
196
     * @access protected
197
     * @param array $option
198
     * @return void
199
     * @throws \think\exception\ValidateException
200
     */
201
    protected function autoValidate(array $option): void
202
    {
203
        [$validate, $scene, $message, $batch] = $option;
204
205
        if (is_array($validate)) {
206
            // 指定验证规则
207
            $v = new Validate();
208
            $v->rule($validate);
209
        } else {
210
            // 调用验证器
211
            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
212
213
            $v = new $class();
214
215
            if (!empty($scene)) {
216
                $v->scene($scene);
217
            }
218
        }
219
220
        /** @var Validate $v */
221
        $v->message($message)
222
            ->batch($batch)
223
            ->failException(true)
224
            ->check($this->request->param());
0 ignored issues
show
Bug introduced by
It seems like $this->request->param() can also be of type null and object; however, parameter $data of think\Validate::check() does only seem to accept array, 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

224
            ->check(/** @scrutinizer ignore-type */ $this->request->param());
Loading history...
225
    }
226
227
    public function getDispatch()
228
    {
229
        return $this->dispatch;
230
    }
231
232
    public function getParam(): array
233
    {
234
        return $this->param;
235
    }
236
237
    abstract public function exec();
238
239
    public function __sleep()
240
    {
241
        return ['rule', 'dispatch', 'param', 'controller', 'actionName'];
242
    }
243
244
    public function __wakeup()
245
    {
246
        $this->app     = Container::pull('app');
247
        $this->request = $this->app->request;
248
    }
249
250
    public function __debugInfo()
251
    {
252
        return [
253
            'dispatch' => $this->dispatch,
254
            'param'    => $this->param,
255
            'rule'     => $this->rule,
256
        ];
257
    }
258
}
259