Passed
Pull Request — 5.x (#307)
by
unknown
22:48
created

Route::getPath()   B

Complexity

Conditions 9
Paths 11

Size

Total Lines 42
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 9

Importance

Changes 0
Metric Value
eloc 20
c 0
b 0
f 0
dl 0
loc 42
ccs 19
cts 19
cp 1
rs 8.0555
cc 9
nc 11
nop 1
crap 9
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 84
50
    public function __construct(string $method, string $path, $handler)
51 84
    {
52 84
        $this->method  = $method;
53 84
        $this->path    = $path;
54 84
        $this->handler = $handler;
55
    }
56 51
57
    public function getCallable(?ContainerInterface $container = null): callable
58 51
    {
59
        $callable = $this->handler;
60 51
61 6
        if (is_string($callable) && strpos($callable, '::') !== false) {
62
            $callable = explode('::', $callable);
63
        }
64 51
65 3
        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
            $callable = [$callable[0], $callable[1]];
67
        }
68 51
69 6
        if (is_array($callable) && isset($callable[0]) && is_string($callable[0])) {
70
            $callable = [$this->resolve($callable[0], $container), $callable[1]];
71
        }
72 51
73 3
        if (is_string($callable)) {
74
            $callable = $this->resolve($callable, $container);
75
        }
76 51
77 3
        if (!is_callable($callable)) {
78
            throw new RuntimeException('Could not resolve a callable for this route');
79
        }
80 48
81
        return $callable;
82
    }
83 45
84
    public function getMethod(): string
85 45
    {
86
        return $this->method;
87
    }
88 33
89
    public function getParentGroup(): ?RouteGroup
90 33
    {
91
        return $this->group;
92
    }
93 51
94
    public function getPath(?array $replacements = null): string
95 51
    {
96
        if (null === $replacements) {
97 51
            return $this->path;
98 3
        }
99
100
        $hasReplacementRegex = '/{(' . implode('|', array_keys($replacements)) . ')(:.*)?}/';
101 51
102
        preg_match_all('/\[\/{(?<keys>.*?)}/', $this->path, $matches);
103
        $isOptionalRegex = '/{(' . implode('|', $matches['keys']) . ')(:.*)?}/';
104 33
105
        $toReplace = [];
106 33
107
        foreach ($replacements as $wildcard => $actual) {
108
            $toReplace['/{' . preg_quote($wildcard, '/') . '(:.*)?}/'] = $actual;
109 33
        }
110
111
        $segments = [];
112
113 33
        foreach (array_filter(explode('/', $this->path)) as $segment) {
114
            // remove square brackets from end of segment only
115 33
            $segment = preg_replace(['/\[$/', '/\]+$/'], '', $segment);
116 3
117
            // non wildcard segment or wildcard with a replacement
118
            if (!preg_match('/{(.*?)}/', $segment) || preg_match($hasReplacementRegex, $segment)) {
119 30
                $segments[] = $segment;
120
                continue;
121
            }
122 15
123
            // required wildcard segment still gets added without replacement
124 15
            if (!preg_match($isOptionalRegex, $segment)) {
125 15
                $segments[] = $segment;
126 15
                continue;
127
            }
128 15
129 3
            // optional segment with no replacement means we break
130 3
            if (preg_match($isOptionalRegex, $segment) && !preg_match($hasReplacementRegex, $segment)) {
131
                break;
132
            }
133 15
        }
134
135
        return preg_replace(array_keys($toReplace), array_values($toReplace), '/' . implode('/', $segments));
136 33
    }
137
138 33
    public function getVars(): array
139 33
    {
140
        return $this->vars;
141
    }
142 9
143
    public function process(
144 9
        ServerRequestInterface $request,
145 3
        RequestHandlerInterface $handler
146
    ): ResponseInterface {
147
        $strategy = $this->getStrategy();
148 6
149 3
        if (!($strategy instanceof StrategyInterface)) {
150
            throw new RuntimeException('A strategy must be set to process a route');
151
        }
152 3
153
        return $strategy->invokeRouteCallable($this, $request);
154
    }
155
156
    public function setParentGroup(RouteGroup $group): self
157
    {
158
        $this->group = $group;
159
        $prefix      = $this->group->getPrefix();
160
        $path        = $this->getPath();
161
162
        if (strcmp($prefix, substr($path, 0, strlen($prefix))) !== 0) {
163
            $path = $prefix . $path;
164
            $this->path = $path;
165
        }
166
167
        return $this;
168
    }
169
170
    public function setVars(array $vars): self
171
    {
172
        $this->vars = $vars;
173
        return $this;
174
    }
175
176
    protected function resolve(string $class, ?ContainerInterface $container = null)
177
    {
178
        if ($container instanceof ContainerInterface && $container->has($class)) {
179
            return $container->get($class);
180
        }
181
182
        if (class_exists($class)) {
183
            return new $class();
184
        }
185
186
        return $class;
187
    }
188
}
189