Passed
Push — master ( 14d352...7a1eb4 )
by Rasmus
12:23 queued 11:08
created

Dispatcher::process()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 2
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 1
rs 10
c 0
b 0
f 0
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|null middleware resolver
20
     */
21
    private $resolver;
22
23
    /**
24
     * @var mixed[] unresolved middleware stack
25
     */
26
    private $stack;
27
28
    /**
29
     * @param (callable|MiddlewareInterface|mixed)[] $stack middleware stack (with at least one middleware component)
30
     * @param callable|null $resolver optional middleware resolver function: receives an element from the
31
     *                                middleware stack, resolves it and returns a `callable|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
    }
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 1
        };
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
                    ? ($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 1
                    $type = readable::typeof($middleware);
96 1
                    $value = readable::value($middleware);
97
98 1
                    throw new LogicException("unsupported middleware type: {$type} ({$value})");
99
                }
100
101 1
                if (! $result instanceof ResponseInterface) {
102 1
                    $given = readable::value($result);
103 1
                    $source = readable::callback($middleware);
104
105 1
                    throw new LogicException("unexpected middleware result: {$given} returned by: {$source}");
106
                }
107
108 1
                return $result;
109 1
            });
110
        }
111
112 1
        return new Delegate(function () {
113 1
            throw new LogicException("unresolved request: middleware stack exhausted with no result");
114 1
        });
115
    }
116
}
117