Completed
Push — 6.0 ( a75685...8c1faf )
by liu
06:09
created

Dispatch::run()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 13
nc 6
nop 0
dl 0
loc 26
ccs 0
cts 14
cp 0
crap 56
rs 8.8333
c 0
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;
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
     * 状态码
58
     * @var int
59
     */
60
    protected $code;
61
62
    public function __construct(Request $request, Rule $rule, $dispatch, array $param = [], int $code = null)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
63
    {
64
        $this->request  = $request;
65
        $this->rule     = $rule;
66
        $this->dispatch = $dispatch;
67
        $this->param    = $param;
68
        $this->code     = $code;
69
    }
70
71
    public function init(App $app)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function init()
Loading history...
72
    {
73
        $this->app = $app;
74
75
        // 执行路由后置操作
76
        $this->doRouteAfter();
77
    }
78
79
    /**
80
     * 执行路由调度
81
     * @access public
82
     * @return mixed
83
     */
84
    public function run(): Response
85
    {
86
        if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) {
87
            $rules = $this->rule->getRouter()->getRule($this->rule->getRule());
88
            $allow = [];
89
            foreach ($rules as $item) {
90
                $allow[] = strtoupper($item->getMethod());
91
            }
92
93
            return Response::create('', '', 204)->header(['Allow' => implode(', ', $allow)]);
94
        }
95
96
        $option = $this->rule->getOption();
97
98
        // 数据自动验证
99
        if (isset($option['validate'])) {
100
            $this->autoValidate($option['validate']);
101
        }
102
103
        if (!empty($option['app'])) {
104
            $this->app->http->setApp($option['app']);
105
        }
106
107
        $data = $this->exec();
108
109
        return $this->autoResponse($data);
110
    }
111
112
    protected function autoResponse($data): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function autoResponse()
Loading history...
113
    {
114
        if ($data instanceof Response) {
115
            $response = $data;
116
        } elseif (!is_null($data)) {
117
            // 默认自动识别响应输出类型
118
            $type     = $this->request->isJson() ? 'json' : 'html';
119
            $response = Response::create($data, $type);
120
        } else {
121
            $data = ob_get_clean();
122
123
            $content  = false === $data ? '' : $data;
124
            $status   = '' === $content && $this->request->isJson() ? 204 : 200;
125
            $response = Response::create($content, '', $status);
126
        }
127
128
        return $response;
129
    }
130
131
    /**
132
     * 检查路由后置操作
133
     * @access protected
134
     * @return void
135
     */
136
    protected function doRouteAfter(): void
137
    {
138
        $option = $this->rule->getOption();
139
140
        // 添加中间件
141
        if (!empty($option['middleware'])) {
142
            $this->app->middleware->import($option['middleware']);
143
        }
144
145
        if (!empty($option['append'])) {
146
            $this->param = array_merge($this->param, $option['append']);
147
        }
148
149
        // 绑定模型数据
150
        if (!empty($option['model'])) {
151
            $this->createBindModel($option['model'], $this->param);
152
        }
153
154
        // 记录当前请求的路由规则
155
        $this->request->setRule($this->rule);
156
157
        // 记录路由变量
158
        $this->request->setRoute($this->param);
159
    }
160
161
    /**
162
     * 路由绑定模型实例
163
     * @access protected
164
     * @param array $bindModel 绑定模型
165
     * @param array $matches   路由变量
166
     * @return void
167
     */
168
    protected function createBindModel(array $bindModel, array $matches): void
169
    {
170
        foreach ($bindModel as $key => $val) {
171
            if ($val instanceof \Closure) {
172
                $result = $this->app->invokeFunction($val, $matches);
173
            } else {
174
                $fields = explode('&', $key);
175
176
                if (is_array($val)) {
177
                    list($model, $exception) = $val;
178
                } else {
179
                    $model     = $val;
180
                    $exception = true;
181
                }
182
183
                $where = [];
184
                $match = true;
185
186
                foreach ($fields as $field) {
187
                    if (!isset($matches[$field])) {
188
                        $match = false;
189
                        break;
190
                    } else {
191
                        $where[] = [$field, '=', $matches[$field]];
192
                    }
193
                }
194
195
                if ($match) {
196
                    $result = $model::where($where)->failException($exception)->find();
197
                }
198
            }
199
200
            if (!empty($result)) {
201
                // 注入容器
202
                $this->app->instance(get_class($result), $result);
203
            }
204
        }
205
    }
206
207
    /**
208
     * 验证数据
209
     * @access protected
210
     * @param array $option
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
211
     * @return void
212
     * @throws \think\exception\ValidateException
213
     */
214
    protected function autoValidate(array $option): void
215
    {
216
        list($validate, $scene, $message, $batch) = $option;
217
218
        if (is_array($validate)) {
219
            // 指定验证规则
220
            $v = new Validate();
221
            $v->rule($validate);
222
        } else {
223
            // 调用验证器
224
            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
225
226
            $v = new $class();
227
228
            if (!empty($scene)) {
229
                $v->scene($scene);
230
            }
231
        }
232
233
        /** @var Validate $v */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
234
        $v->message($message)
235
            ->batch($batch)
236
            ->failException(true)
237
            ->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

237
            ->check(/** @scrutinizer ignore-type */ $this->request->param());
Loading history...
238
    }
239
240
    public function getDispatch()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getDispatch()
Loading history...
241
    {
242
        return $this->dispatch;
243
    }
244
245
    public function getParam(): array
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getParam()
Loading history...
246
    {
247
        return $this->param;
248
    }
249
250
    abstract public function exec();
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function exec()
Loading history...
251
252
    public function __sleep()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __sleep()
Loading history...
253
    {
254
        return ['rule', 'dispatch', 'param', 'code', 'controller', 'actionName'];
255
    }
256
257
    public function __wakeup()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __wakeup()
Loading history...
258
    {
259
        $this->app     = Container::pull('app');
260
        $this->request = $this->app->request;
261
    }
262
263
    public function __debugInfo()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __debugInfo()
Loading history...
264
    {
265
        return [
266
            'dispatch' => $this->dispatch,
267
            'param'    => $this->param,
268
            'code'     => $this->code,
269
            'rule'     => $this->rule,
270
        ];
271
    }
272
}
273