Passed
Push — 8.0 ( 4a7956...35202a )
by liu
04:54
created

Dispatch::doRouteAfter()   B

Complexity

Conditions 7
Paths 32

Size

Total Lines 32
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 7.9295

Importance

Changes 0
Metric Value
cc 7
eloc 15
c 0
b 0
f 0
nc 32
nop 0
dl 0
loc 32
ccs 11
cts 15
cp 0.7332
crap 7.9295
rs 8.8333
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 = [], protected array $option = [])
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 21
            $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->option;
89
90
        // 添加中间件
91 27
        if (!empty($option['middleware'])) {
92 3
            if (isset($option['without_middleware'])) {
93
                $middleware = !empty($option['without_middleware']) ? array_diff($option['middleware'], $option['without_middleware']) : [];
94
            } else {
95 3
                $middleware = $option['middleware'];
96
            }
97 3
            $this->app->middleware->import($middleware, 'route');
98
        }
99
100 27
        if (!empty($option['append'])) {
101
            $this->param = array_merge($this->param, $option['append']);
102
        }
103
104
        // 绑定模型数据
105 27
        if (!empty($option['model'])) {
106
            $this->createBindModel($option['model'], $this->param);
107
        }
108
109
        // 记录当前请求的路由规则
110 27
        $this->request->setRule($this->rule);
111
112
        // 记录路由变量
113 27
        $this->request->setRoute($this->param);
114
115
        // 数据自动验证
116 27
        if (isset($option['validate'])) {
117
            $this->autoValidate($option['validate']);
118
        }
119
    }
120
121
    /**
122
     * 路由绑定模型实例
123
     * @access protected
124
     * @param array $bindModel 绑定模型
125
     * @param array $matches   路由变量
126
     * @return void
127
     */
128
    protected function createBindModel(array $bindModel, array $matches): void
129
    {
130
        foreach ($bindModel as $key => $val) {
131
            if ($val instanceof \Closure) {
132
                $result = $this->app->invokeFunction($val, $matches);
133
            } else {
134
                $fields = explode('&', $key);
135
136
                if (is_array($val)) {
137
                    [$model, $exception] = $val;
138
                } else {
139
                    $model     = $val;
140
                    $exception = true;
141
                }
142
143
                $where = [];
144
                $match = true;
145
146
                foreach ($fields as $field) {
147
                    if (!isset($matches[$field])) {
148
                        $match = false;
149
                        break;
150
                    } else {
151
                        $where[] = [$field, '=', $matches[$field]];
152
                    }
153
                }
154
155
                if ($match) {
156
                    $result = $model::where($where)->failException($exception)->find();
157
                }
158
            }
159
160
            if (!empty($result)) {
161
                // 注入容器
162
                $this->app->instance($result::class, $result);
163
            }
164
        }
165
    }
166
167
    /**
168
     * 验证数据
169
     * @access protected
170
     * @param array $option
171
     * @return void
172
     * @throws \think\exception\ValidateException
173
     */
174
    protected function autoValidate(array $option): void
175
    {
176
        [$validate, $scene, $message, $batch] = $option;
177
178
        if (is_array($validate)) {
179
            // 指定验证规则
180
            $v = new Validate();
181
            $v->rule($validate);
182
        } else {
183
            // 调用验证器
184
            $class = str_contains($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
185
186
            $v = new $class();
187
188
            if (!empty($scene)) {
189
                $v->scene($scene);
190
            }
191
        }
192
193
        /** @var Validate $v */
194
        $v->message($message)
195
            ->batch($batch)
196
            ->failException(true)
197
            ->check($this->request->param());
0 ignored issues
show
Bug introduced by
$this->request->param() of type null|object is incompatible with the type array expected by parameter $data of think\Validate::check(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

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