Passed
Push — 5.1 ( b72800...b88336 )
by liu
06:41
created

Dispatch::autoResponse()   B

Complexity

Conditions 7
Paths 11

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 12
nc 11
nop 1
dl 0
loc 19
rs 8.8333
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2018 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
12
namespace think\route;
13
14
use think\App;
15
use think\Container;
16
use think\exception\ValidateException;
17
use think\Request;
18
use think\Response;
19
20
abstract class Dispatch
1 ignored issue
show
Coding Style introduced by
Missing class doc comment
Loading history...
21
{
22
    /**
23
     * 应用对象
24
     * @var App
25
     */
26
    protected $app;
27
28
    /**
29
     * 请求对象
30
     * @var Request
31
     */
32
    protected $request;
33
34
    /**
35
     * 路由规则
36
     * @var Rule
37
     */
38
    protected $rule;
39
40
    /**
41
     * 调度信息
42
     * @var mixed
43
     */
44
    protected $dispatch;
45
46
    /**
47
     * 调度参数
48
     * @var array
49
     */
50
    protected $param;
51
52
    /**
53
     * 状态码
54
     * @var string
55
     */
56
    protected $code;
57
58
    /**
59
     * 是否进行大小写转换
60
     * @var bool
61
     */
62
    protected $convert;
63
64
    public function __construct(Request $request, Rule $rule, $dispatch, $param = [], $code = null)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
65
    {
66
        $this->request  = $request;
67
        $this->rule     = $rule;
68
        $this->app      = Container::get('app');
69
        $this->dispatch = $dispatch;
70
        $this->param    = $param;
71
        $this->code     = $code;
72
73
        if (isset($param['convert'])) {
74
            $this->convert = $param['convert'];
75
        }
76
    }
77
78
    public function init()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
79
    {
80
        // 执行路由后置操作
81
        if ($this->rule->doAfter()) {
82
            // 设置请求的路由信息
83
84
            // 设置当前请求的参数
85
            $this->request->setRouteVars($this->rule->getVars());
86
            $this->request->routeInfo([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
87
                'rule'   => $this->rule->getRule(),
88
                'route'  => $this->rule->getRoute(),
89
                'option' => $this->rule->getOption(),
90
                'var'    => $this->rule->getVars(),
91
            ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
92
93
            $this->doRouteAfter();
94
        }
95
96
        return $this;
97
    }
98
99
    /**
100
     * 检查路由后置操作
101
     * @access protected
102
     * @return void
103
     */
104
    protected function doRouteAfter()
105
    {
106
        // 记录匹配的路由信息
107
        $option  = $this->rule->getOption();
108
        $matches = $this->rule->getVars();
109
110
        // 添加中间件
111
        if (!empty($option['middleware'])) {
112
            $this->app['middleware']->import($option['middleware']);
113
        }
114
115
        // 绑定模型数据
116
        if (!empty($option['model'])) {
117
            $this->createBindModel($option['model'], $matches);
118
        }
119
120
        // 指定Header数据
121
        if (!empty($option['header'])) {
122
            $header = $option['header'];
123
            $this->app['hook']->add('response_send', function ($response) use ($header) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
124
                $response->header($header);
125
            });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
126
        }
127
128
        // 指定Response响应数据
129
        if (!empty($option['response'])) {
130
            foreach ($option['response'] as $response) {
131
                $this->app['hook']->add('response_send', $response);
132
            }
133
        }
134
135
        // 开启请求缓存
136
        if (isset($option['cache']) && $this->request->isGet()) {
137
            $this->parseRequestCache($option['cache']);
138
        }
139
140
        if (!empty($option['append'])) {
141
            $this->request->setRouteVars($option['append']);
142
        }
143
    }
144
145
    /**
146
     * 执行路由调度
147
     * @access public
148
     * @return mixed
149
     */
150
    public function run()
151
    {
152
        $option = $this->rule->getOption();
153
154
        // 检测路由after行为
155
        if (!empty($option['after'])) {
156
            $dispatch = $this->checkAfter($option['after']);
157
158
            if ($dispatch instanceof Response) {
159
                return $dispatch;
160
            }
161
        }
162
163
        // 数据自动验证
164
        if (isset($option['validate'])) {
165
            $this->autoValidate($option['validate']);
166
        }
167
168
        $data = $this->exec();
169
170
        return $this->autoResponse($data);
171
    }
172
173
    protected function autoResponse($data)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
174
    {
175
        if ($data instanceof Response) {
176
            $response = $data;
177
        } elseif (!is_null($data)) {
178
            // 默认自动识别响应输出类型
179
            $isAjax = $this->request->isAjax();
180
            $type   = $isAjax ? $this->rule->getConfig('default_ajax_return') : $this->rule->getConfig('default_return_type');
181
182
            $response = Response::create($data, $type);
183
        } else {
184
            $data    = ob_get_clean();
185
            $content = false === $data ? '' : $data;
186
            $status  = '' === $content && $this->request->isJson() ? 204 : 200;
187
188
            $response = Response::create($content, '', $status);
189
        }
190
191
        return $response;
192
    }
193
194
    /**
195
     * 检查路由后置行为
196
     * @access protected
197
     * @param  mixed   $after 后置行为
198
     * @return mixed
199
     */
200
    protected function checkAfter($after)
201
    {
202
        $this->app['log']->notice('路由后置行为建议使用中间件替代!');
203
204
        $hook = $this->app['hook'];
205
206
        $result = null;
207
208
        foreach ((array) $after as $behavior) {
209
            $result = $hook->exec($behavior);
210
211
            if (!is_null($result)) {
212
                break;
213
            }
214
        }
215
216
        // 路由规则重定向
217
        if ($result instanceof Response) {
218
            return $result;
219
        }
220
221
        return false;
222
    }
223
224
    /**
225
     * 验证数据
226
     * @access protected
227
     * @param  array             $option
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
228
     * @return void
229
     * @throws ValidateException
230
     */
231
    protected function autoValidate($option)
232
    {
233
        list($validate, $scene, $message, $batch) = $option;
234
235
        if (is_array($validate)) {
236
            // 指定验证规则
237
            $v = $this->app->validate();
238
            $v->rule($validate);
239
        } else {
240
            // 调用验证器
241
            $v = $this->app->validate($validate);
242
            if (!empty($scene)) {
243
                $v->scene($scene);
244
            }
245
        }
246
247
        if (!empty($message)) {
248
            $v->message($message);
249
        }
250
251
        // 批量验证
252
        if ($batch) {
253
            $v->batch(true);
254
        }
255
256
        if (!$v->check($this->request->param())) {
0 ignored issues
show
Bug introduced by
It seems like $this->request->param() can also be of type 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

256
        if (!$v->check(/** @scrutinizer ignore-type */ $this->request->param())) {
Loading history...
257
            throw new ValidateException($v->getError());
258
        }
259
    }
260
261
    /**
262
     * 处理路由请求缓存
263
     * @access protected
264
     * @param  Request       $request 请求对象
0 ignored issues
show
Coding Style introduced by
Doc comment for parameter $request does not match actual variable name $cache
Loading history...
265
     * @param  string|array  $cache  路由缓存
0 ignored issues
show
Coding Style introduced by
Superfluous parameter comment
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter name; 2 found
Loading history...
266
     * @return void
267
     */
268
    protected function parseRequestCache($cache)
269
    {
270
        if (is_array($cache)) {
271
            list($key, $expire, $tag) = array_pad($cache, 3, null);
272
        } else {
273
            $key    = str_replace('|', '/', $this->request->url());
274
            $expire = $cache;
275
            $tag    = null;
276
        }
277
278
        $cache = $this->request->cache($key, $expire, $tag);
279
        $this->app->setResponseCache($cache);
280
    }
281
282
    /**
283
     * 路由绑定模型实例
284
     * @access protected
285
     * @param  array|\Clousre    $bindModel 绑定模型
0 ignored issues
show
Bug introduced by
The type Clousre was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
286
     * @param  array             $matches   路由变量
287
     * @return void
288
     */
289
    protected function createBindModel($bindModel, $matches)
290
    {
291
        foreach ($bindModel as $key => $val) {
292
            if ($val instanceof \Closure) {
293
                $result = $this->app->invokeFunction($val, $matches);
294
            } else {
295
                $fields = explode('&', $key);
296
297
                if (is_array($val)) {
298
                    list($model, $exception) = $val;
299
                } else {
300
                    $model     = $val;
301
                    $exception = true;
302
                }
303
304
                $where = [];
305
                $match = true;
306
307
                foreach ($fields as $field) {
308
                    if (!isset($matches[$field])) {
309
                        $match = false;
310
                        break;
311
                    } else {
312
                        $where[] = [$field, '=', $matches[$field]];
313
                    }
314
                }
315
316
                if ($match) {
317
                    $query  = strpos($model, '\\') ? $model::where($where) : $this->app->model($model)->where($where);
318
                    $result = $query->failException($exception)->find();
319
                }
320
            }
321
322
            if (!empty($result)) {
323
                // 注入容器
324
                $this->app->instance(get_class($result), $result);
325
            }
326
        }
327
    }
328
329
    public function convert($convert)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
330
    {
331
        $this->convert = $convert;
332
333
        return $this;
334
    }
335
336
    public function getDispatch()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
337
    {
338
        return $this->dispatch;
339
    }
340
341
    public function getParam()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
342
    {
343
        return $this->param;
344
    }
345
346
    abstract public function exec();
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
347
348
    public function __sleep()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
349
    {
350
        return ['rule', 'dispatch', 'convert', 'param', 'code', 'controller', 'actionName'];
351
    }
352
353
    public function __wakeup()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
354
    {
355
        $this->app     = Container::get('app');
356
        $this->request = $this->app['request'];
357
    }
358
359
    public function __debugInfo()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
360
    {
361
        $data = get_object_vars($this);
362
        unset($data['app'], $data['request'], $data['rule']);
363
364
        return $data;
365
    }
366
}
367