Passed
Push — dev ( 79ab41...849c07 )
by 世昌
02:49
created

Route::getRunnable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
namespace suda\framework;
3
4
use Closure;
5
use Exception;
6
use suda\framework\route\MatchResult;
7
use suda\framework\runnable\Runnable;
8
use suda\framework\route\RouteMatcher;
9
use suda\framework\route\uri\UriMatcher;
10
use suda\framework\route\RouteCollection;
11
12
class Route
13
{
14
    /**
15
     * 路由
16
     *
17
     * @var RouteCollection
18
     */
19
    protected $routes;
20
21
    /**
22
     * 可执行对象
23
     *
24
     * @var Runnable[]
25
     */
26
    protected $runnable;
27
28
    /**
29
     * 设置默认
30
     *
31
     * @var Runnable
32
     */
33
    protected $default;
34
35
    /**
36
     * 是否包含闭包
37
     *
38
     * @var boolean
39
     */
40
    protected $containClosure = false;
41
    
42
    public function __construct()
43
    {
44
        $this->routes = new RouteCollection;
45
        $this->runnable = [];
46
        $this->default = new Runnable([__CLASS__ , 'defaultResponse']);
47
    }
48
49
    /**
50
     * 创建 GET 路由
51
     *
52
     * @param string $name
53
     * @param string $url
54
     * @param Runnable|Closure|array|string $runnable
55
     * @param array $attributes
56
     * @return $this
57
     */
58
    public function get(string $name, string $url, $runnable, array $attributes = [])
59
    {
60
        return $this->request(['GET'], $name, $url, $runnable, $attributes);
61
    }
62
63
    /**
64
     * 创建 POST 路由
65
     *
66
     * @param string $name
67
     * @param string $url
68
     * @param Runnable|Closure|array|string $runnable
69
     * @param array $attributes
70
     * @return $this
71
     */
72
    public function post(string $name, string $url, $runnable, array $attributes = [])
73
    {
74
        return $this->request(['POST'], $name, $url, $runnable, $attributes);
75
    }
76
77
    /**
78
     * 创建 DELETE 路由
79
     *
80
     * @param string $name
81
     * @param string $url
82
     * @param Runnable|Closure|array|string $runnable
83
     * @param array $attributes
84
     * @return $this
85
     */
86
    public function delete(string $name, string $url, $runnable, array $attributes = [])
87
    {
88
        return $this->request(['DELETE'], $name, $url, $runnable, $attributes);
89
    }
90
91
    /**
92
     * 创建 HEAD 路由
93
     *
94
     * @param string $name
95
     * @param string $url
96
     * @param Runnable|Closure|array|string $runnable
97
     * @param array $attributes
98
     * @return $this
99
     */
100
    public function head(string $name, string $url, $runnable, array $attributes = [])
101
    {
102
        return $this->request(['HEAD'], $name, $url, $runnable, $attributes);
103
    }
104
105
106
    /**
107
     * 创建 OPTIONS 路由
108
     *
109
     * @param string $name
110
     * @param string $url
111
     * @param Runnable|Closure|array|string $runnable
112
     * @param array $attributes
113
     * @return $this
114
     */
115
    public function options(string $name, string $url, $runnable, array $attributes = [])
116
    {
117
        return $this->request(['OPTIONS'], $name, $url, $runnable, $attributes);
118
    }
119
120
    /**
121
     * 创建 PUT 路由
122
     *
123
     * @param string $name
124
     * @param string $url
125
     * @param Runnable|Closure|array|string $runnable
126
     * @param array $attributes
127
     * @return $this
128
     */
129
    public function put(string $name, string $url, $runnable, array $attributes = [])
130
    {
131
        return $this->request(['PUT'], $name, $url, $runnable, $attributes);
132
    }
133
134
    /**
135
     * 创建 TRACE 路由
136
     *
137
     * @param string $name
138
     * @param string $url
139
     * @param Runnable|Closure|array|string $runnable
140
     * @param array $attributes
141
     * @return $this
142
     */
143
    public function trace(string $name, string $url, $runnable, array $attributes = [])
144
    {
145
        return $this->request(['TRACE'], $name, $url, $runnable, $attributes);
146
    }
147
148
    /**
149
     * 创建路由
150
     *
151
     * @param string $name
152
     * @param string $url
153
     * @param Runnable|Closure|array|string $runnable
154
     * @param array $attributes
155
     * @return $this
156
     */
157
    public function any(string $name, string $url, $runnable, array $attributes = [])
158
    {
159
        return $this->request([], $name, $url, $runnable, $attributes);
160
    }
161
162
    /**
163
     * 添加请求
164
     *
165
     * @param array $method
166
     * @param string $name
167
     * @param string $url
168
     * @param Runnable|Closure|array|string $runnable
169
     * @param array $attributes
170
     * @return $this
171
     */
172
    public function request(array $method, string $name, string $url, $runnable, array $attributes = [])
173
    {
174
        $matcher = new RouteMatcher($method, $url, $attributes);
175
        $target = new Runnable($runnable);
176
        $this->routes->add($name, $matcher);
177
        $this->runnable[$name] = $target;
178
        if ($target->isClosure()) {
179
            $this->containClosure = true;
180
        }
181
        return $this;
182
    }
183
184
    /**
185
     * 设置默认运行器
186
     *
187
     * @param Runnable|Closure|array|string $runnable
188
     * @return $this
189
     */
190
    public function default($runnable)
191
    {
192
        $this->default = new Runnable($runnable);
193
        return $this;
194
    }
195
196
    /**
197
     * 匹配路由
198
     *
199
     * @param string $method
200
     * @param string $uri
201
     * @return MatchResult|null
202
     */
203
    public function match(string $method, string $uri): ?MatchResult
204
    {
205
        /** @var RouteMatcher $matcher */
206
        /** @var string $name */
207
        foreach ($this->routes as $name => $matcher) {
208
            if (($parameter = $matcher->match($method, $uri)) !== null) {
209
                return new MatchResult($matcher, $name, $this->runnable[$name], $parameter);
210
            }
211
        }
212
        return null;
213
    }
214
215
    /**
216
     * 运行结果
217
     *
218
     * @param MatchResult|null $result
219
     * @param Request $request
220
     * @param Response $response
221
     * @return Response
222
     * @throws Exception
223
     */
224
    public function run(?MatchResult $result, Request $request, Response $response):Response
225
    {
226
        if ($result !== null) {
227
            return $this->buildResponse(
228
                $result,
229
                $request
230
                    ->setParameter($request->getParameter())
231
                    ->mergeQueries($result->getParameter())
232
                    ->setAttributes($result->getMatcher()->getAttribute()),
233
                $response
234
            );
235
        }
236
        return $this->buildDefaultResponse($request, $response);
237
    }
238
239
    /**
240
     * 构建响应
241
     *
242
     * @param MatchResult $result
243
     * @param Request $request
244
     * @param Response $response
245
     * @return Response
246
     * @throws Exception
247
     */
248
    protected function buildResponse(MatchResult $result, Request $request, Response $response):Response
249
    {
250
        $content = $result->getRunnable()->run($request, $response);
251
        if ($content !== null && !$response->isSend()) {
252
            $response->setContent($content);
253
        }
254
        return $response;
255
    }
256
257
    /**
258
     * 构建默认响应
259
     *
260
     * @param Request $request
261
     * @param Response $response
262
     * @return Response
263
     * @throws Exception
264
     */
265
    protected function buildDefaultResponse(Request $request, Response $response):Response
266
    {
267
        $content = $this->default->run($request, $response);
268
        if ($content !== null && !$response->isSend()) {
269
            $response->setContent($content);
270
        }
271
        return $response;
272
    }
273
274
    /**
275
     * 创建默认运行器
276
     *
277
     * @return Runnable
278
     */
279
    public function getDefaultRunnable():Runnable
280
    {
281
        return $this->default;
282
    }
283
284
    /**
285
     * 默认响应
286
     *
287
     * @param Request $request
288
     * @param Response $response
289
     * @return mixed
290
     */
291
    protected static function defaultResponse(Request $request, Response $response)
292
    {
293
        $response->status(404);
294
        $response->setType('html');
295
        return 'Page Not Found: '.$request->getUrl();
296
    }
297
298
    /**
299
     * 创建URL
300
     *
301
     * @param string $name
302
     * @param array $parameter
303
     * @param bool $allowQuery
304
     * @return string|null
305
     */
306
    public function create(string $name, array $parameter, bool $allowQuery = true):?string
307
    {
308
        if ($matcher = $this->routes->get($name)) {
309
            return UriMatcher::buildUri($matcher->getMatcher(), $parameter, $allowQuery);
310
        }
311
        return null;
312
    }
313
314
    /**
315
     * 判断是否包含闭包
316
     *
317
     * @return  boolean
318
     */
319
    public function isContainClosure():bool
320
    {
321
        return $this->containClosure;
322
    }
323
324
    /**
325
     * Get 路由
326
     *
327
     * @return  RouteCollection
328
     */
329
    public function getRouteCollection():RouteCollection
330
    {
331
        return $this->routes;
332
    }
333
334
    /**
335
     * @param RouteCollection $routes
336
     */
337
    public function setRouteCollection(RouteCollection $routes)
338
    {
339
        $this->routes = $routes;
340
    }
341
342
    /**
343
     * @return Runnable[]
344
     */
345
    public function getRunnable(): array
346
    {
347
        return $this->runnable;
348
    }
349
350
    /**
351
     * @param Runnable[] $runnable
352
     */
353
    public function setRunnable(array $runnable): void
354
    {
355
        $this->runnable = $runnable;
356
    }
357
}
358