Passed
Push — luminateonedev-resolve-optiona... ( f12986...34d930 )
by Phil
01:36
created

Route::process()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 11
ccs 5
cts 5
cp 1
rs 10
cc 2
nc 2
nop 2
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace League\Route;
6
7
use FastRoute\RouteParser\Std;
8
use League\Route\Middleware\{MiddlewareAwareInterface, MiddlewareAwareTrait};
9
use League\Route\Strategy\{StrategyAwareInterface, StrategyAwareTrait, StrategyInterface};
10
use Psr\Container\ContainerInterface;
11
use Psr\Http\Message\{ResponseInterface, ServerRequestInterface};
12
use Psr\Http\Server\{MiddlewareInterface, RequestHandlerInterface};
13
use RuntimeException;
14
15
class Route implements
16
    MiddlewareInterface,
17
    MiddlewareAwareInterface,
18
    RouteConditionHandlerInterface,
19
    StrategyAwareInterface
20
{
21
    use MiddlewareAwareTrait;
22
    use RouteConditionHandlerTrait;
23
    use StrategyAwareTrait;
24
25
    /**
26
     * @var callable|string
27
     */
28
    protected $handler;
29
30
    /**
31
     * @var RouteGroup
32
     */
33
    protected $group;
34
35
    /**
36
     * @var string
37
     */
38
    protected $method;
39
40
    /**
41
     * @var string
42
     */
43
    protected $path;
44
45
    /**
46
     * @var array
47
     */
48
    protected $vars = [];
49
50 102
    public function __construct(string $method, string $path, $handler)
51
    {
52 102
        $this->method  = $method;
53 102
        $this->path    = $path;
54 102
        $this->handler = $handler;
55 102
    }
56
57 51
    public function getCallable(?ContainerInterface $container = null): callable
58
    {
59 51
        $callable = $this->handler;
60
61 51
        if (is_string($callable) && strpos($callable, '::') !== false) {
62 6
            $callable = explode('::', $callable);
63
        }
64
65 51
        if (is_array($callable) && isset($callable[0]) && is_object($callable[0])) {
0 ignored issues
show
introduced by
The condition is_object($callable[0]) is always false.
Loading history...
66 3
            $callable = [$callable[0], $callable[1]];
67
        }
68
69 51
        if (is_array($callable) && isset($callable[0]) && is_string($callable[0])) {
70 6
            $callable = [$this->resolve($callable[0], $container), $callable[1]];
71
        }
72
73 51
        if (is_string($callable)) {
74 3
            $callable = $this->resolve($callable, $container);
75
        }
76
77 51
        if (!is_callable($callable)) {
78 3
            throw new RuntimeException('Could not resolve a callable for this route');
79
        }
80
81 48
        return $callable;
82
    }
83
84 45
    public function getMethod(): string
85
    {
86 45
        return $this->method;
87
    }
88
89 33
    public function getParentGroup(): ?RouteGroup
90
    {
91 33
        return $this->group;
92
    }
93
94 69
    public function getPath(?array $replacements = null): string
95
    {
96 69
        if (null === $replacements) {
97 48
            return $this->path;
98
        }
99
100 21
        $hasReplacementRegex = '/{(' . implode('|', array_keys($replacements)) . ')(:.*)?}/';
101
102 21
        preg_match_all('/\[\/{(?<keys>.*?)}/', $this->path, $matches);
103 21
        $isOptionalRegex = '/{(' . implode('|', $matches['keys']) . ')(:.*)?}/';
104
105 21
        $toReplace = [];
106
107 21
        foreach ($replacements as $wildcard => $actual) {
108 15
            $toReplace['/{' . preg_quote($wildcard, '/') . '(:.*)?}/'] = $actual;
109
        }
110
111 21
        $segments = [];
112
113 21
        foreach (array_filter(explode('/', $this->path)) as $segment) {
114
            // remove square brackets from end of segment only
115 21
            $segment = preg_replace(['/\[$/', '/\]+$/'], '', $segment);
116
117
            // non wildcard segment or wildcard with a replacement
118 21
            if (!preg_match('/{(.*?)}/', $segment) || preg_match($hasReplacementRegex, $segment)) {
119 21
                $segments[] = $segment;
120 21
                continue;
121
            }
122
123
            // required wildcard segment still gets added without replacement
124 12
            if (!preg_match($isOptionalRegex, $segment)) {
125
                $segments[] = $segment;
126
                continue;
127
            }
128
129
            // optional segment with no replacement means we break
130 12
            if (preg_match($isOptionalRegex, $segment) && !preg_match($hasReplacementRegex, $segment)) {
131 12
                break;
132
            }
133
        }
134
135 21
        return preg_replace(array_keys($toReplace), array_values($toReplace), '/' . implode('/', $segments));
136
    }
137
138 33
    public function getVars(): array
139
    {
140 33
        return $this->vars;
141
    }
142
143 33
    public function process(
144
        ServerRequestInterface $request,
145
        RequestHandlerInterface $handler
146
    ): ResponseInterface {
147 33
        $strategy = $this->getStrategy();
148
149 33
        if (!($strategy instanceof StrategyInterface)) {
150 3
            throw new RuntimeException('A strategy must be set to process a route');
151
        }
152
153 30
        return $strategy->invokeRouteCallable($this, $request);
154
    }
155
156 15
    public function setParentGroup(RouteGroup $group): self
157
    {
158 15
        $this->group = $group;
159 15
        $prefix      = $this->group->getPrefix();
160 15
        $path        = $this->getPath();
161
162 15
        if (strcmp($prefix, substr($path, 0, strlen($prefix))) !== 0) {
163 3
            $path = $prefix . $path;
164 3
            $this->path = $path;
165
        }
166
167 15
        return $this;
168
    }
169
170 33
    public function setVars(array $vars): self
171
    {
172 33
        $this->vars = $vars;
173 33
        return $this;
174
    }
175
176 9
    protected function resolve(string $class, ?ContainerInterface $container = null)
177
    {
178 9
        if ($container instanceof ContainerInterface && $container->has($class)) {
179 3
            return $container->get($class);
180
        }
181
182 6
        if (class_exists($class)) {
183 3
            return new $class();
184
        }
185
186 3
        return $class;
187
    }
188
}
189