Completed
Push — master ( 3806c8...dda3c5 )
by Oscar
02:35
created

Dispatcher::createMiddlewareFromClosure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 1
1
<?php
2
declare(strict_types = 1);
3
4
namespace Middleland;
5
6
use Closure;
7
use Interop\Http\Server\MiddlewareInterface;
8
use Interop\Http\Server\RequestHandlerInterface;
9
use InvalidArgumentException;
10
use LogicException;
11
use Psr\Container\ContainerInterface;
12
use Psr\Http\Message\ResponseInterface;
13
use Psr\Http\Message\ServerRequestInterface;
14
15
class Dispatcher implements MiddlewareInterface, RequestHandlerInterface
16
{
17
    /**
18
     * @var MiddlewareInterface[]
19
     */
20
    private $middleware;
21
22
    /**
23
     * @var ContainerInterface|null
24
     */
25
    private $container;
26
27
    /**
28
     * @var RequestHandlerInterface|null
29
     */
30
    private $next;
31
32
    /**
33
     * @param MiddlewareInterface[] $middleware
34
     */
35
    public function __construct(array $middleware, ContainerInterface $container = null)
36
    {
37
        if (empty($middleware)) {
38
            throw new LogicException('Empty middleware queue');
39
        }
40
41
        $this->middleware = $middleware;
42
        $this->container = $container;
43
    }
44
45
    /**
46
     * Return the next available middleware frame in the queue.
47
     *
48
     * @return MiddlewareInterface|false
49
     */
50
    public function next(ServerRequestInterface $request)
51
    {
52
        next($this->middleware);
53
54
        return $this->get($request);
55
    }
56
57
    /**
58
     * Return the next available middleware frame in the middleware.
59
     *
60
     * @return MiddlewareInterface|false
61
     */
62
    private function get(ServerRequestInterface $request)
63
    {
64
        $frame = current($this->middleware);
65
66
        if ($frame === false) {
67
            return $frame;
68
        }
69
70
        if (is_array($frame)) {
71
            $conditions = $frame;
72
            $frame = array_pop($conditions);
73
74
            foreach ($conditions as $condition) {
75
                if ($condition === true) {
76
                    continue;
77
                }
78
79
                if ($condition === false) {
80
                    return $this->next($request);
81
                }
82
83
                if (is_string($condition)) {
84
                    $condition = new Matchers\Path($condition);
85
                } elseif (!is_callable($condition)) {
86
                    throw new InvalidArgumentException('Invalid matcher. Must be a boolean, string or a callable');
87
                }
88
89
                if (!$condition($request)) {
90
                    return $this->next($request);
91
                }
92
            }
93
        }
94
95
        if (is_string($frame)) {
96
            if ($this->container === null) {
97
                throw new InvalidArgumentException(sprintf('No valid middleware provided (%s)', $frame));
98
            }
99
100
            $frame = $this->container->get($frame);
101
        }
102
103
        if ($frame instanceof Closure) {
104
            return self::createMiddlewareFromClosure($frame);
105
        }
106
107
        if ($frame instanceof MiddlewareInterface) {
108
            return $frame;
109
        }
110
111
        throw new InvalidArgumentException(sprintf('No valid middleware provided (%s)', is_object($frame) ? get_class($frame) : gettype($frame)));
112
    }
113
114
    /**
115
     * Dispatch the request, return a response.
116
     */
117
    public function dispatch(ServerRequestInterface $request): ResponseInterface
118
    {
119
        reset($this->middleware);
120
121
        return $this->get($request)->process($request, $this);
122
    }
123
124
    /**
125
     * @see RequestHandlerInterface
126
     */
127
    public function handle(ServerRequestInterface $request): ResponseInterface
128
    {
129
        $frame = $this->next($request);
130
131
        if ($frame === false) {
132
            if ($this->next !== null) {
133
                return $this->next->handle($request);
134
            }
135
136
            throw new LogicException('Middleware queue exhausted');
137
        }
138
139
        return $frame->process($request, $this);
140
    }
141
142
    /**
143
     * @see MiddlewareInterface
144
     */
145
    public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
146
    {
147
        $this->next = $next;
148
149
        return $this->dispatch($request);
150
    }
151
152
    /**
153
     * Create a middleware from a closure
154
     */
155
    private static function createMiddlewareFromClosure(Closure $handler): MiddlewareInterface
156
    {
157
        return new class($handler) implements MiddlewareInterface {
158
            private $handler;
159
160
            public function __construct(Closure $handler)
161
            {
162
                $this->handler = $handler;
163
            }
164
165
            public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
166
            {
167
                return call_user_func($this->handler, $request, $next);
168
            }
169
        };
170
    }
171
}
172