Completed
Push — master ( badc34...6d34b5 )
by Rasmus
01:45
created

Dispatcher::resolve()   B

Complexity

Conditions 7
Paths 2

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 7.0283

Importance

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