-
Notifications
You must be signed in to change notification settings - Fork 10
/
Dispatcher.php
116 lines (97 loc) · 3.64 KB
/
Dispatcher.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?php
namespace mindplay\middleman;
use InvalidArgumentException;
use LogicException;
use mindplay\readable;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
/**
* PSR-7 / PSR-15 middleware dispatcher
*/
class Dispatcher implements MiddlewareInterface, RequestHandlerInterface
{
/**
* @var callable|null middleware resolver
*/
private $resolver;
/**
* @var mixed[] unresolved middleware stack
*/
private $stack;
/**
* @param (callable|MiddlewareInterface|mixed)[] $stack middleware stack (with at least one middleware component)
* @param callable|null $resolver optional middleware resolver function: receives an element from the
* middleware stack, resolves it and returns a `callable|MiddlewareInterface`
*
* @throws InvalidArgumentException if an empty middleware stack was given
*/
public function __construct($stack, ?callable $resolver = null)
{
if (count($stack) === 0) {
throw new InvalidArgumentException("an empty middleware stack was given");
}
$this->stack = $stack;
$this->resolver = $resolver;
}
/**
* Dispatches the middleware stack and returns the resulting `ResponseInterface`.
*
* @param ServerRequestInterface $request
*
* @return ResponseInterface
*
* @throws LogicException on unexpected result from any middleware on the stack
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$resolved = $this->resolve(0);
return $resolved->handle($request);
}
/**
* @inheritdoc
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$this->stack[] = function (ServerRequestInterface $request) use ($handler) {
return $handler->handle($request);
};
$response = $this->handle($request);
array_pop($this->stack);
return $response;
}
/**
* @param int $index middleware stack index
*
* @return RequestHandlerInterface
*/
private function resolve($index): RequestHandlerInterface
{
if (isset($this->stack[$index])) {
return new Delegate(function (ServerRequestInterface $request) use ($index) {
$middleware = $this->resolver
? ($this->resolver)($this->stack[$index])
: $this->stack[$index]; // as-is
if ($middleware instanceof MiddlewareInterface) {
$result = $middleware->process($request, $this->resolve($index + 1));
} else if (is_callable($middleware)) {
$result = $middleware($request, $this->resolve($index + 1));
} else {
$type = readable::typeof($middleware);
$value = readable::value($middleware);
throw new LogicException("unsupported middleware type: {$type} ({$value})");
}
if (! $result instanceof ResponseInterface) {
$given = readable::value($result);
$source = readable::callback($middleware);
throw new LogicException("unexpected middleware result: {$given} returned by: {$source}");
}
return $result;
});
}
return new Delegate(function () {
throw new LogicException("unresolved request: middleware stack exhausted with no result");
});
}
}