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

Dispatch::autoValidate()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 5
nop 1
dl 0
loc 24
ccs 0
cts 13
cp 0
crap 20
rs 9.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 6
    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 6
        $this->request  = $request;
65 6
        $this->rule     = $rule;
66 6
        $this->dispatch = $dispatch;
67 6
        $this->param    = $param;
68 6
        $this->code     = $code;
69 6
    }
70
71 6
    public function init(App $app)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function init()
Loading history...
72
    {
73 6
        $this->app = $app;
74
75
        // 执行路由后置操作
76 6
        $this->doRouteAfter();
77 6
    }
78
79
    /**
80
     * 执行路由调度
81
     * @access public
82
     * @return mixed
83
     */
84 6
    public function run(): Response
85
    {
86 6
        if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) {
87 2
            $rules = $this->rule->getRouter()->getRule($this->rule->getRule());
88 2
            $allow = [];
89 2
            foreach ($rules as $item) {
90 2
                $allow[] = strtoupper($item->getMethod());
91
            }
92
93 2
            return Response::create('', 'html', 204)->header(['Allow' => implode(', ', $allow)]);
94
        }
95
96 5
        $data = $this->exec();
97 5
        return $this->autoResponse($data);
98
    }
99
100 5
    protected function autoResponse($data): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function autoResponse()
Loading history...
101
    {
102 5
        if ($data instanceof Response) {
103 3
            $response = $data;
104 5
        } elseif (!is_null($data)) {
105
            // 默认自动识别响应输出类型
106 5
            $type     = $this->request->isJson() ? 'json' : 'html';
107 5
            $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 5
        return $response;
117
    }
118
119
    /**
120
     * 检查路由后置操作
121
     * @access protected
122
     * @return void
123
     */
124 6
    protected function doRouteAfter(): void
125
    {
126 6
        $option = $this->rule->getOption();
127
128
        // 添加中间件
129 6
        if (!empty($option['middleware'])) {
130 1
            $this->app->middleware->import($option['middleware'], 'route');
131
        }
132
133 6
        if (!empty($option['append'])) {
134
            $this->param = array_merge($this->param, $option['append']);
135
        }
136
137
        // 绑定模型数据
138 6
        if (!empty($option['model'])) {
139
            $this->createBindModel($option['model'], $this->param);
140
        }
141
142
        // 记录当前请求的路由规则
143 6
        $this->request->setRule($this->rule);
144
145
        // 记录路由变量
146 6
        $this->request->setRoute($this->param);
147
148
        // 数据自动验证
149 6
        if (isset($option['validate'])) {
150
            $this->autoValidate($option['validate']);
151
        }
152 6
    }
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
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
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 */
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...
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()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getDispatch()
Loading history...
234
    {
235
        return $this->dispatch;
236
    }
237
238
    public function getParam(): array
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getParam()
Loading history...
239
    {
240
        return $this->param;
241
    }
242
243
    abstract public function exec();
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function exec()
Loading history...
244
245
    public function __sleep()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __sleep()
Loading history...
246
    {
247
        return ['rule', 'dispatch', 'param', 'code', 'controller', 'actionName'];
248
    }
249
250
    public function __wakeup()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __wakeup()
Loading history...
251
    {
252
        $this->app     = Container::pull('app');
253
        $this->request = $this->app->request;
254
    }
255
256
    public function __debugInfo()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __debugInfo()
Loading history...
257
    {
258
        return [
259
            'dispatch' => $this->dispatch,
260
            'param'    => $this->param,
261
            'code'     => $this->code,
262
            'rule'     => $this->rule,
263
        ];
264
    }
265
}
266