Completed
Branch 6.0 (d30585)
by yun
02:10
created

Dispatch::run()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

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

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