Passed
Push — 8.0 ( 35202a...0c94f2 )
by liu
07:21
created

Middleware::buildMiddleware()   A

Complexity

Conditions 6
Paths 11

Size

Total Lines 27
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6.0131

Importance

Changes 0
Metric Value
cc 6
eloc 13
nc 11
nop 2
dl 0
loc 27
ccs 13
cts 14
cp 0.9286
crap 6.0131
rs 9.2222
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: Slince <[email protected]>
10
// +----------------------------------------------------------------------
11
declare(strict_types=1);
12
13
namespace think;
14
15
use Closure;
16
use LogicException;
17
use think\exception\Handle;
18
use Throwable;
19
20
/**
21
 * 中间件管理类
22
 * @package think
23
 */
24
class Middleware
25
{
26
    /**
27
     * 中间件执行队列
28
     * @var array
29
     */
30
    protected $queue = [];
31
32 39
    public function __construct(protected App $app)
33
    {
34 39
    }
35
36
    /**
37
     * 导入中间件
38
     * @access public
39
     * @param array  $middlewares
40
     * @param string $type 中间件类型
41
     * @return void
42
     */
43 12
    public function import(array $middlewares = [], string $type = 'global'): void
44
    {
45 12
        foreach ($middlewares as $middleware) {
46 9
            $this->add($middleware, $type);
47
        }
48
    }
49
50
    /**
51
     * 注册中间件
52
     * @access public
53
     * @param mixed  $middleware
54
     * @param string $type 中间件类型
55
     * @return void
56
     */
57 12
    public function add(array|string|Closure $middleware, string $type = 'global'): void
58
    {
59 12
        $middleware = $this->buildMiddleware($middleware, $type);
60
61 12
        if (!empty($middleware)) {
62 12
            $this->queue[$type][] = $middleware;
63 12
            $this->queue[$type]   = array_unique($this->queue[$type], SORT_REGULAR);
64
        }
65
    }
66
67
    /**
68
     * 注册路由中间件
69
     * @access public
70
     * @param mixed $middleware
71
     * @return void
72
     */
73
    public function route(array|string|Closure $middleware): void
74
    {
75
        $this->add($middleware, 'route');
76
    }
77
78
    /**
79
     * 注册控制器中间件
80
     * @access public
81
     * @param mixed $middleware
82
     * @return void
83
     */
84 6
    public function controller(array|string|Closure $middleware): void
85
    {
86 6
        $this->add($middleware, 'controller');
87
    }
88
89
    /**
90
     * 注册中间件到开始位置
91
     * @access public
92
     * @param mixed  $middleware
93
     * @param string $type 中间件类型
94
     */
95 3
    public function unshift(array|string|Closure $middleware, string $type = 'global')
96
    {
97 3
        $middleware = $this->buildMiddleware($middleware, $type);
98
99 3
        if (!empty($middleware)) {
100 3
            if (!isset($this->queue[$type])) {
101
                $this->queue[$type] = [];
102
            }
103
104 3
            array_unshift($this->queue[$type], $middleware);
105
        }
106
    }
107
108
    /**
109
     * 获取注册的中间件
110
     * @access public
111
     * @param string $type 中间件类型
112
     * @return array
113
     */
114 3
    public function all(string $type = 'global'): array
115
    {
116 3
        return $this->queue[$type] ?? [];
117
    }
118
119
    /**
120
     * 调度管道
121
     * @access public
122
     * @param string $type 中间件类型
123
     * @return Pipeline
124
     */
125 33
    public function pipeline(string $type = 'global')
126
    {
127 33
        return (new Pipeline())
128 33
            ->through(array_map(function ($middleware) {
129 9
                return function ($request, $next) use ($middleware) {
130 9
                    [$call, $params] = $middleware;
131 9
                    if (is_array($call) && is_string($call[0])) {
132 9
                        $call = [$this->app->make($call[0]), $call[1]];
133
                    }
134 9
                    $response = call_user_func($call, $request, $next, ...$params);
135
136 9
                    if (!$response instanceof Response) {
137
                        throw new LogicException('The middleware must return Response instance');
138
                    }
139 9
                    return $response;
140 9
                };
141 33
            }, $this->sortMiddleware($this->queue[$type] ?? [])))
142 33
            ->whenException([$this, 'handleException']);
143
    }
144
145
    /**
146
     * 结束调度
147
     * @param Response $response
148
     */
149 6
    public function end(Response $response)
150
    {
151 6
        foreach ($this->queue as $queue) {
152 3
            foreach ($queue as $middleware) {
153 3
                [$call] = $middleware;
154 3
                if (is_array($call) && is_string($call[0])) {
155 3
                    $instance = $this->app->make($call[0]);
156 3
                    if (method_exists($instance, 'end')) {
157 3
                        $instance->end($response);
158
                    }
159
                }
160
            }
161
        }
162
    }
163
164
    /**
165
     * 异常处理
166
     * @param Request   $passable
167
     * @param Throwable $e
168
     * @return Response
169
     */
170 3
    public function handleException($passable, Throwable $e)
171
    {
172
        /** @var Handle $handler */
173 3
        $handler = $this->app->make(Handle::class);
174
175 3
        $handler->report($e);
176
177 3
        return $handler->render($passable, $e);
178
    }
179
180
    /**
181
     * 解析中间件
182
     * @access protected
183
     * @param array|string|Closure  $middleware
184
     * @param string $type 中间件类型
185
     * @return array
186
     */
187 12
    protected function buildMiddleware(array|string|Closure $middleware, string $type): array
188
    {
189 12
        if (empty($middleware)) {
190
            return [];
191
        }
192
        
193 12
        if (is_array($middleware)) {
0 ignored issues
show
introduced by
The condition is_array($middleware) is always true.
Loading history...
194 9
            [$middleware, $params] = $middleware;
195
        }
196
197 12
        if ($middleware instanceof Closure) {
198 6
            return [$middleware, $params ?? []];
199
        }
200
201
        //中间件别名检查
202 12
        $alias = $this->app->config->get('middleware.alias', []);
203
204 12
        if (isset($alias[$middleware])) {
205 3
            $middleware = $alias[$middleware];
206
        }
207
208 12
        if (is_array($middleware)) {
209 3
            $this->import($middleware, $type);
210 3
            return [];
211
        }
212
213 12
        return [[$middleware, 'handle'], $params ?? []];
214
    }
215
216
    /**
217
     * 中间件排序
218
     * @param array $middlewares
219
     * @return array
220
     */
221 33
    protected function sortMiddleware(array $middlewares)
222
    {
223 33
        $priority = $this->app->config->get('middleware.priority', []);
224 33
        uasort($middlewares, function ($a, $b) use ($priority) {
225 6
            $aPriority = $this->getMiddlewarePriority($priority, $a);
226 6
            $bPriority = $this->getMiddlewarePriority($priority, $b);
227 6
            return $bPriority - $aPriority;
228 33
        });
229
230 33
        return $middlewares;
231
    }
232
233
    /**
234
     * 获取中间件优先级
235
     * @param $priority
236
     * @param $middleware
237
     * @return int
238
     */
239 6
    protected function getMiddlewarePriority($priority, $middleware)
240
    {
241 6
        [$call] = $middleware;
242 6
        if (is_array($call) && is_string($call[0])) {
243 6
            $index = array_search($call[0], array_reverse($priority));
244 6
            return false === $index ? -1 : $index;
245
        }
246 3
        return -1;
247
    }
248
}
249