Passed
Push — 8.0 ( 495b3f...e5cbfb )
by liu
02:07
created

Dispatch::__sleep()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2023 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 Psr\Http\Message\ResponseInterface;
16
use think\App;
17
use think\Container;
18
use think\Request;
19
use think\Response;
20
use think\Validate;
21
22
/**
23
 * 路由调度基础类
24
 */
25
abstract class Dispatch
26
{
27
    /**
28
     * 应用对象
29
     * @var App
30
     */
31
    protected $app;
32
33 30
    public function __construct(protected Request $request, protected Rule $rule, protected $dispatch, protected array $param = [])
34
    {
35 30
    }
36
37 27
    public function init(App $app)
38
    {
39 27
        $this->app = $app;
40
41
        // 执行路由后置操作
42 27
        $this->doRouteAfter();
43
    }
44
45
    /**
46
     * 执行路由调度
47
     * @access public
48
     * @return Response
49
     */
50 30
    public function run(): Response
51
    {
52 30
        $data = $this->exec();
53 30
        return $this->autoResponse($data);
54
    }
55
56 30
    protected function autoResponse($data): Response
57
    {
58 30
        if ($data instanceof Response) {
59 18
            $response = $data;
60 24
        } elseif ($data instanceof ResponseInterface) {
61 3
            $response = Response::create((string) $data->getBody(), 'html', $data->getStatusCode());
62
63 3
            foreach ($data->getHeaders() as $header => $values) {
64 3
                $response->header([$header => implode(", ", $values)]);
65
            }
66 21
        } elseif (!is_null($data)) {
67
            // 默认自动识别响应输出类型
68 21
            $type     = $this->request->isJson() ? 'json' : 'html';
69 21
            $response = Response::create($data, $type);
70
        } else {
71
            $data = ob_get_clean();
72
73
            $content  = false === $data ? '' : $data;
74
            $status   = '' === $content && $this->request->isJson() ? 204 : 200;
75
            $response = Response::create($content, 'html', $status);
76
        }
77
78 30
        return $response;
79
    }
80
81
    /**
82
     * 检查路由后置操作
83
     * @access protected
84
     * @return void
85
     */
86 27
    protected function doRouteAfter(): void
87
    {
88 27
        $option = $this->rule->getOption();
89
90
        // 添加中间件
91 27
        if (!empty($option['middleware'])) {
92 3
            $this->app->middleware->import($option['middleware'], 'route');
93
        }
94
95 27
        if (!empty($option['append'])) {
96
            $this->param = array_merge($this->param, $option['append']);
97
        }
98
99
        // 绑定模型数据
100 27
        if (!empty($option['model'])) {
101
            $this->createBindModel($option['model'], $this->param);
102
        }
103
104
        // 记录当前请求的路由规则
105 27
        $this->request->setRule($this->rule);
106
107
        // 记录路由变量
108 27
        $this->request->setRoute($this->param);
109
110
        // 数据自动验证
111 27
        if (isset($option['validate'])) {
112
            $this->autoValidate($option['validate']);
113
        }
114
    }
115
116
    /**
117
     * 路由绑定模型实例
118
     * @access protected
119
     * @param array $bindModel 绑定模型
120
     * @param array $matches   路由变量
121
     * @return void
122
     */
123
    protected function createBindModel(array $bindModel, array $matches): void
124
    {
125
        foreach ($bindModel as $key => $val) {
126
            if ($val instanceof \Closure) {
127
                $result = $this->app->invokeFunction($val, $matches);
128
            } else {
129
                $fields = explode('&', $key);
130
131
                if (is_array($val)) {
132
                    [$model, $exception] = $val;
133
                } else {
134
                    $model     = $val;
135
                    $exception = true;
136
                }
137
138
                $where = [];
139
                $match = true;
140
141
                foreach ($fields as $field) {
142
                    if (!isset($matches[$field])) {
143
                        $match = false;
144
                        break;
145
                    } else {
146
                        $where[] = [$field, '=', $matches[$field]];
147
                    }
148
                }
149
150
                if ($match) {
151
                    $result = $model::where($where)->failException($exception)->find();
152
                }
153
            }
154
155
            if (!empty($result)) {
156
                // 注入容器
157
                $this->app->instance($result::class, $result);
158
            }
159
        }
160
    }
161
162
    /**
163
     * 验证数据
164
     * @access protected
165
     * @param array $option
166
     * @return void
167
     * @throws \think\exception\ValidateException
168
     */
169
    protected function autoValidate(array $option): void
170
    {
171
        [$validate, $scene, $message, $batch] = $option;
172
173
        if (is_array($validate)) {
174
            // 指定验证规则
175
            $v = new Validate();
176
            $v->rule($validate);
177
        } else {
178
            // 调用验证器
179
            $class = str_contains($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
180
181
            $v = new $class();
182
183
            if (!empty($scene)) {
184
                $v->scene($scene);
185
            }
186
        }
187
188
        /** @var Validate $v */
189
        $v->message($message)
190
            ->batch($batch)
191
            ->failException(true)
192
            ->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

192
            ->check(/** @scrutinizer ignore-type */ $this->request->param());
Loading history...
193
    }
194
195
    public function getDispatch()
196
    {
197
        return $this->dispatch;
198
    }
199
200
    public function getParam(): array
201
    {
202
        return $this->param;
203
    }
204
205
    abstract public function exec();
206
207
    public function __sleep()
208
    {
209
        return ['rule', 'dispatch', 'param', 'controller', 'actionName'];
210
    }
211
212
    public function __wakeup()
213
    {
214
        $this->app     = Container::pull('app');
215
        $this->request = $this->app->request;
216
    }
217
218
    public function __debugInfo()
219
    {
220
        return [
221
            'dispatch' => $this->dispatch,
222
            'param'    => $this->param,
223
            'rule'     => $this->rule,
224
        ];
225
    }
226
}
227