Passed
Push — luminateonedev-resolve-optiona... ( f12986 )
by Phil
02:57
created

Route::resolvePath()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
eloc 14
c 0
b 0
f 0
dl 0
loc 19
ccs 0
cts 14
cp 0
rs 9.4888
cc 5
nc 5
nop 1
crap 30
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 99
    public function __construct(string $method, string $path, $handler)
51
    {
52 99
        $this->method  = $method;
53 99
        $this->path    = $path;
54 99
        $this->handler = $handler;
55 99
    }
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
    public function resolvePath(array $replacements = []): string
95
    {
96
        $parser = new Std();
97
        $routeData = $parser->parse($this->path);
98
        $longestPossibleRoute = end($routeData);
99
        $result = [];
100
        foreach ($longestPossibleRoute as $routeSegment) {
101
            if (is_string($routeSegment)) {
102
                $result[] = $routeSegment;
103
            } else if (is_array($routeSegment)) {
104
                $wildcard = $routeSegment[0];
105
                if (array_key_exists($wildcard, $replacements)) {
106
                    $result[] = $replacements[$wildcard];
107
                } else {
108
                    break;
109
                }
110
            }
111
        }
112
        return rtrim(implode($result), '/');
113
    }
114
115 66
    public function getPath(?array $replacements = null): string
116
    {
117 66
        if (null === $replacements) {
118 48
            return $this->path;
119
        }
120
121 18
        $hasReplacementRegex = '/{(' . implode('|', array_keys($replacements)) . ')(:.*)?}/';
122
123 18
        preg_match_all('/\[\/{(?<keys>.*?)}/', $this->path, $matches);
124 18
        $isOptionalRegex = '/{(' . implode('|', $matches['keys']) . ')(:.*)?}/';
125
126 18
        $toReplace = [];
127
128 18
        foreach ($replacements as $wildcard => $actual) {
129 12
            $toReplace['/{' . preg_quote($wildcard, '/') . '(:.*)?}/'] = $actual;
130
        }
131
132 18
        $segments = [];
133
134 18
        foreach (array_filter(explode('/', $this->path)) as $segment) {
135
            // remove square brackets from end of segment only
136 18
            $segment = preg_replace(['/\[$/', '/\]+$/'], '', $segment);
137
138
            // non wildcard segment or wildcard with a replacement
139 18
            if (!preg_match('/{(.*?)}/', $segment) || preg_match($hasReplacementRegex, $segment)) {
140 18
                $segments[] = $segment;
141 18
                continue;
142
            }
143
144
            // required wildcard segment still gets added without replacement
145 9
            if (!preg_match($isOptionalRegex, $segment)) {
146
                $segments[] = $segment;
147
            }
148
        }
149
150 18
        return preg_replace(array_keys($toReplace), array_values($toReplace), '/' . implode('/', $segments));
151
    }
152
153 33
    public function getVars(): array
154
    {
155 33
        return $this->vars;
156
    }
157
158 33
    public function process(
159
        ServerRequestInterface $request,
160
        RequestHandlerInterface $handler
161
    ): ResponseInterface {
162 33
        $strategy = $this->getStrategy();
163
164 33
        if (!($strategy instanceof StrategyInterface)) {
165 3
            throw new RuntimeException('A strategy must be set to process a route');
166
        }
167
168 30
        return $strategy->invokeRouteCallable($this, $request);
169
    }
170
171 15
    public function setParentGroup(RouteGroup $group): self
172
    {
173 15
        $this->group = $group;
174 15
        $prefix      = $this->group->getPrefix();
175 15
        $path        = $this->getPath();
176
177 15
        if (strcmp($prefix, substr($path, 0, strlen($prefix))) !== 0) {
178 3
            $path = $prefix . $path;
179 3
            $this->path = $path;
180
        }
181
182 15
        return $this;
183
    }
184
185 33
    public function setVars(array $vars): self
186
    {
187 33
        $this->vars = $vars;
188 33
        return $this;
189
    }
190
191 9
    protected function resolve(string $class, ?ContainerInterface $container = null)
192
    {
193 9
        if ($container instanceof ContainerInterface && $container->has($class)) {
194 3
            return $container->get($class);
195
        }
196
197 6
        if (class_exists($class)) {
198 3
            return new $class();
199
        }
200
201 3
        return $class;
202
    }
203
}
204