Completed
Pull Request — master (#23)
by Rasmus
56s
created

Dispatcher::dispatch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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