Completed
Push — 6.0 ( d4f1ba...a30ecc )
by yun
06:44
created

Middleware::pipeline()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4.0092

Importance

Changes 0
Metric Value
cc 4
eloc 12
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 18
ccs 11
cts 12
cp 0.9167
crap 4.0092
rs 9.8666
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: Slince <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think;
14
15
use Closure;
16
use InvalidArgumentException;
17
use LogicException;
18
use think\exception\Handle;
19
use Throwable;
20
21
/**
22
 * 中间件管理类
23
 * @package think
0 ignored issues
show
Coding Style introduced by
Package name "think" is not valid; consider "Think" instead
Loading history...
24
 */
25
class Middleware
26
{
27
    /**
28
     * 中间件执行队列
29
     * @var array
30
     */
31
    protected $queue = [];
32
33
    /**
34
     * 应用对象
35
     * @var App
36
     */
37
    protected $app;
38
39 4
    public function __construct(App $app)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
40
    {
41 4
        $this->app = $app;
42 4
    }
43
44
    /**
45
     * 导入中间件
46
     * @access public
47
     * @param array  $middlewares
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
48
     * @param string $type 中间件类型
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 1 found
Loading history...
49
     * @return void
50
     */
51 3
    public function import(array $middlewares = [], string $type = 'global'): void
52
    {
53 3
        foreach ($middlewares as $middleware) {
54 2
            $this->add($middleware, $type);
55
        }
56 3
    }
57
58
    /**
59
     * 注册中间件
60
     * @access public
61
     * @param mixed  $middleware
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
62
     * @param string $type 中间件类型
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 1 found
Loading history...
63
     * @return void
64
     */
65 2
    public function add($middleware, string $type = 'global'): void
66
    {
67 2
        $middleware = $this->buildMiddleware($middleware, $type);
68
69 2
        if (!empty($middleware)) {
70 2
            $this->queue[$type][] = $middleware;
71 2
            $this->queue[$type]   = array_unique($this->queue[$type], SORT_REGULAR);
72
        }
73 2
    }
74
75
    /**
76
     * 注册路由中间件
77
     * @access public
78
     * @param mixed $middleware
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
79
     * @return void
80
     */
81
    public function route($middleware): void
82
    {
83
        $this->add($middleware, 'route');
84
    }
85
86
    /**
87
     * 注册控制器中间件
88
     * @access public
89
     * @param mixed $middleware
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
90
     * @return void
91
     */
92 1
    public function controller($middleware): void
93
    {
94 1
        $this->add($middleware, 'controller');
95 1
    }
96
97
    /**
98
     * 注册中间件到开始位置
99
     * @access public
100
     * @param mixed  $middleware
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
101
     * @param string $type 中间件类型
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 1 found
Loading history...
102
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
103 1
    public function unshift($middleware, string $type = 'global')
104
    {
105 1
        $middleware = $this->buildMiddleware($middleware, $type);
106
107 1
        if (!empty($middleware)) {
108 1
            if (!isset($this->queue[$type])) {
109
                $this->queue[$type] = [];
110
            }
111
112 1
            array_unshift($this->queue[$type], $middleware);
113
        }
114 1
    }
115
116
    /**
117
     * 获取注册的中间件
118
     * @access public
119
     * @param string $type 中间件类型
120
     * @return array
121
     */
122 1
    public function all(string $type = 'global'): array
123
    {
124 1
        return $this->queue[$type] ?? [];
125
    }
126
127
    /**
128
     * 调度管道
129
     * @access public
130
     * @param string $type 中间件类型
131
     * @return Pipeline
132
     */
133 2
    public function pipeline(string $type = 'global')
134
    {
135 2
        return (new Pipeline())
136
            ->through(array_map(function ($middleware) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
137
                return function ($request, $next) use ($middleware) {
138 1
                    [$call, $params] = $middleware;
139 1
                    if (is_array($call) && is_string($call[0])) {
140 1
                        $call = [$this->app->make($call[0]), $call[1]];
141
                    }
142 1
                    $response = call_user_func($call, $request, $next, ...$params);
143
144 1
                    if (!$response instanceof Response) {
145
                        throw new LogicException('The middleware must return Response instance');
146
                    }
147 1
                    return $response;
148 1
                };
149 2
            }, $this->sortMiddleware($this->queue[$type] ?? [])))
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
150 2
            ->whenException([$this, 'handleException']);
151
    }
152
153
    /**
154
     * 结束调度
155
     * @param Response $response
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
156
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
157 2
    public function end(Response $response)
158
    {
159 2
        foreach ($this->queue as $queue) {
160 1
            foreach ($queue as $middleware) {
161 1
                [$call] = $middleware;
162 1
                if (is_array($call) && is_string($call[0])) {
163 1
                    $instance = $this->app->make($call[0]);
164 1
                    if (method_exists($instance, 'end')) {
165 1
                        $instance->end($response);
166
                    }
167
                }
168
            }
169
        }
170 2
    }
171
172
    /**
173
     * 异常处理
174
     * @param Request   $passable
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
175
     * @param Throwable $e
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
176
     * @return Response
177
     */
178 1
    public function handleException($passable, Throwable $e)
179
    {
180
        /** @var Handle $handler */
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...
181 1
        $handler = $this->app->make(Handle::class);
182
183 1
        $handler->report($e);
184
185 1
        return $handler->render($passable, $e);
186
    }
187
188
    /**
189
     * 解析中间件
190
     * @access protected
191
     * @param mixed  $middleware
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
192
     * @param string $type 中间件类型
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 1 found
Loading history...
193
     * @return array
194
     */
195 2
    protected function buildMiddleware($middleware, string $type): array
196
    {
197 2
        if (is_array($middleware)) {
198 1
            [$middleware, $params] = $middleware;
199
        }
200
201 2
        if ($middleware instanceof Closure) {
202 2
            return [$middleware, $params ?? []];
203
        }
204
205 2
        if (!is_string($middleware)) {
206
            throw new InvalidArgumentException('The middleware is invalid');
207
        }
208
209
        //中间件别名检查
210 2
        $alias = $this->app->config->get('middleware.alias', []);
211
212 2
        if (isset($alias[$middleware])) {
213 1
            $middleware = $alias[$middleware];
214
        }
215
216 2
        if (is_array($middleware)) {
217 1
            $this->import($middleware, $type);
218 1
            return [];
219
        }
220
221 2
        return [[$middleware, 'handle'], $params ?? []];
222
    }
223
224
    /**
225
     * 中间件排序
226
     * @param array $middlewares
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
227
     * @return array
228
     */
229 2
    protected function sortMiddleware(array $middlewares)
230
    {
231 2
        $priority = $this->app->config->get('middleware.priority', []);
232
        uasort($middlewares, function ($a, $b) use ($priority) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
233 1
            $aPriority = $this->getMiddlewarePriority($priority, $a);
234 1
            $bPriority = $this->getMiddlewarePriority($priority, $b);
235 1
            return $bPriority - $aPriority;
236 2
        });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
237
238 2
        return $middlewares;
239
    }
240
241
    /**
242
     * 获取中间件优先级
243
     * @param $priority
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
244
     * @param $middleware
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
245
     * @return int
246
     */
247 1
    protected function getMiddlewarePriority($priority, $middleware)
248
    {
249 1
        [$call] = $middleware;
250 1
        if (is_array($call) && is_string($call[0])) {
251 1
            $index = array_search($call[0], array_reverse($priority));
252 1
            return false === $index ? -1 : $index;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false === $index ? -1 : $index also could return the type string which is incompatible with the documented return type integer.
Loading history...
253
        }
254 1
        return -1;
255
    }
256
257
}
258