Completed
Push — 6.0 ( 50e0b0...f5a91c )
by liu
07:10
created

Dispatch   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 242
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 90
dl 0
loc 242
ccs 0
cts 95
cp 0
rs 9.52
c 0
b 0
f 0
wmc 36

12 Methods

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

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