Completed
Push — 6.0 ( 3d7f88...4b2d81 )
by liu
14:14
created

Dispatch::doRouteAfter()   A

Complexity

Conditions 6
Paths 32

Size

Total Lines 32
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 13
c 1
b 0
f 0
nc 32
nop 0
dl 0
loc 32
ccs 0
cts 12
cp 0
crap 42
rs 9.2222
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
        $data = $this->exec();
97
        return $this->autoResponse($data);
98
    }
99
100
    protected function autoResponse($data): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function autoResponse()
Loading history...
101
    {
102
        if ($data instanceof Response) {
103
            $response = $data;
104
        } elseif (!is_null($data)) {
105
            // 默认自动识别响应输出类型
106
            $type     = $this->request->isJson() ? 'json' : 'html';
107
            $response = Response::create($data, $type);
108
        } else {
109
            $data = ob_get_clean();
110
111
            $content  = false === $data ? '' : $data;
112
            $status   = '' === $content && $this->request->isJson() ? 204 : 200;
113
            $response = Response::create($content, '', $status);
114
        }
115
116
        return $response;
117
    }
118
119
    /**
120
     * 检查路由后置操作
121
     * @access protected
122
     * @return void
123
     */
124
    protected function doRouteAfter(): void
125
    {
126
        $option = $this->rule->getOption();
127
128
        if (!empty($option['app'])) {
129
            $this->app->http->setApp($option['app']);
130
        }
131
132
        // 添加中间件
133
        if (!empty($option['middleware'])) {
134
            $this->app->middleware->import($option['middleware'], 'route');
135
        }
136
137
        if (!empty($option['append'])) {
138
            $this->param = array_merge($this->param, $option['append']);
139
        }
140
141
        // 绑定模型数据
142
        if (!empty($option['model'])) {
143
            $this->createBindModel($option['model'], $this->param);
144
        }
145
146
        // 数据自动验证
147
        if (isset($option['validate'])) {
148
            $this->autoValidate($option['validate']);
149
        }
150
151
        // 记录当前请求的路由规则
152
        $this->request->setRule($this->rule);
153
154
        // 记录路由变量
155
        $this->request->setRoute($this->param);
156
    }
157
158
    /**
159
     * 路由绑定模型实例
160
     * @access protected
161
     * @param array $bindModel 绑定模型
162
     * @param array $matches   路由变量
163
     * @return void
164
     */
165
    protected function createBindModel(array $bindModel, array $matches): void
166
    {
167
        foreach ($bindModel as $key => $val) {
168
            if ($val instanceof \Closure) {
169
                $result = $this->app->invokeFunction($val, $matches);
170
            } else {
171
                $fields = explode('&', $key);
172
173
                if (is_array($val)) {
174
                    list($model, $exception) = $val;
175
                } else {
176
                    $model     = $val;
177
                    $exception = true;
178
                }
179
180
                $where = [];
181
                $match = true;
182
183
                foreach ($fields as $field) {
184
                    if (!isset($matches[$field])) {
185
                        $match = false;
186
                        break;
187
                    } else {
188
                        $where[] = [$field, '=', $matches[$field]];
189
                    }
190
                }
191
192
                if ($match) {
193
                    $result = $model::where($where)->failException($exception)->find();
194
                }
195
            }
196
197
            if (!empty($result)) {
198
                // 注入容器
199
                $this->app->instance(get_class($result), $result);
200
            }
201
        }
202
    }
203
204
    /**
205
     * 验证数据
206
     * @access protected
207
     * @param array $option
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
208
     * @return void
209
     * @throws \think\exception\ValidateException
210
     */
211
    protected function autoValidate(array $option): void
212
    {
213
        list($validate, $scene, $message, $batch) = $option;
214
215
        if (is_array($validate)) {
216
            // 指定验证规则
217
            $v = new Validate();
218
            $v->rule($validate);
219
        } else {
220
            // 调用验证器
221
            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
222
223
            $v = new $class();
224
225
            if (!empty($scene)) {
226
                $v->scene($scene);
227
            }
228
        }
229
230
        /** @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...
231
        $v->message($message)
232
            ->batch($batch)
233
            ->failException(true)
234
            ->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

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