Passed
Pull Request — 5.x (#318)
by
unknown
20:32
created

Route::getRouteTarget()   B

Complexity

Conditions 7
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

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