Completed
Push — master ( b9d832...421ea6 )
by Phil
06:19 queued 04:14
created

RouteCollection::prepRoutes()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 30
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 7

Importance

Changes 6
Bugs 1 Features 0
Metric Value
c 6
b 1
f 0
dl 0
loc 30
ccs 19
cts 19
cp 1
rs 6.7272
cc 7
eloc 15
nc 5
nop 1
crap 7
1
<?php
2
3
namespace League\Route;
4
5
use FastRoute\DataGenerator;
6
use FastRoute\DataGenerator\GroupCountBased as GroupCountBasedDataGenerator;
7
use FastRoute\RouteCollector;
8
use FastRoute\RouteParser;
9
use FastRoute\RouteParser\Std as StdRouteParser;
10
use Interop\Container\ContainerInterface;
11
use InvalidArgumentException;
12
use League\Container\Container;
13
use League\Route\Strategy\RequestResponseStrategy;
14
use League\Route\Strategy\StrategyAwareInterface;
15
use League\Route\Strategy\StrategyAwareTrait;
16
use Psr\Http\Message\ResponseInterface;
17
use Psr\Http\Message\ServerRequestInterface;
18
19
class RouteCollection extends RouteCollector implements StrategyAwareInterface, RouteCollectionInterface
20
{
21
    use RouteCollectionMapTrait;
22
    use StrategyAwareTrait;
23
24
    /**
25
     * @var \Interop\Container\ContainerInterface
26
     */
27
    protected $container;
28
29
    /**
30
     * @var \League\Route\Route[]
31
     */
32
    protected $routes = [];
33
34
    /**
35
     * @var \League\Route\Route[]
36
     */
37
    protected $namedRoutes = [];
38
39
    /**
40
     * @var \League\Route\RouteGroup[]
41
     */
42
    protected $groups = [];
43
44
    /**
45
     * @var array
46
     */
47
    protected $patternMatchers = [
48
        '/{(.+?):number}/'        => '{$1:[0-9]+}',
49
        '/{(.+?):word}/'          => '{$1:[a-zA-Z]+}',
50
        '/{(.+?):alphanum_dash}/' => '{$1:[a-zA-Z0-9-_]+}',
51
        '/{(.+?):slug}/'          => '{$1:[a-z0-9-]+}'
52
    ];
53
54
    /**
55
     * Constructor.
56
     *
57
     * @param \Interop\Container\ContainerInterface $container
58
     * @param \FastRoute\RouteParser                $parser
59
     * @param \FastRoute\DataGenerator              $generator
60
     */
61 30
    public function __construct(
62
        ContainerInterface $container = null,
63
        RouteParser        $parser    = null,
64
        DataGenerator      $generator = null
65
    ) {
66 30
        $this->container = ($container instanceof ContainerInterface) ? $container : new Container;
67
68
        // build parent route collector
69 30
        $parser    = ($parser instanceof RouteParser) ? $parser : new StdRouteParser;
70 30
        $generator = ($generator instanceof DataGenerator) ? $generator : new GroupCountBasedDataGenerator;
71 30
        parent::__construct($parser, $generator);
72 30
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77 18
    public function map($method, $path, $handler)
78
    {
79 18
        $path = sprintf('/%s', ltrim($path, '/'));
80
81 18
        $route = (new Route)->setMethods((array) $method)->setPath($path)->setCallable($handler);
82
83 18
        $this->routes[] = $route;
84
85 18
        return $route;
86
    }
87
88
    /**
89
     * Add a group of routes to the collection.
90
     *
91
     * @param string   $prefix
92
     * @param callable $group
93
     *
94
     * @return \League\Route\RouteGroup
95
     */
96 3
    public function group($prefix, callable $group)
97
    {
98 3
        $group = new RouteGroup($prefix, $group, $this);
99
100 3
        $this->groups[] = $group;
101
102 3
        return $group;
103
    }
104
105
    /**
106
     * Dispatch the route based on the request.
107
     *
108
     * @param \Psr\Http\Message\ServerRequestInterface $request
109
     * @param \Psr\Http\Message\ResponseInterface      $response
110
     *
111
     * @return \Psr\Http\Message\ResponseInterface
112
     */
113 15
    public function dispatch(ServerRequestInterface $request, ResponseInterface $response)
114
    {
115 15
        $dispatcher = $this->getDispatcher($request);
116
117 15
        return $dispatcher->handle($request, $response);
118
    }
119
120
    /**
121
     * Return a fully configured dispatcher.
122
     *
123
     * @param \Psr\Http\Message\ServerRequestInterface $request
124
     *
125
     * @return \League\Route\Dispatcher
126
     */
127 18
    public function getDispatcher(ServerRequestInterface $request)
128
    {
129 18
        if (is_null($this->getStrategy())) {
130 12
            $this->setStrategy(new RequestResponseStrategy);
131 12
        }
132
133 18
        $this->prepRoutes($request);
134
135 18
        return (new Dispatcher($this->getData()))->setStrategy($this->getStrategy());
0 ignored issues
show
Bug introduced by
It seems like $this->getStrategy() can be null; however, setStrategy() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
136
    }
137
138
    /**
139
     * Prepare all routes, build name index and filter out none matching
140
     * routes before being passed off to the parser.
141
     *
142
     * @param \Psr\Http\Message\ServerRequestInterface $request
143
     *
144
     * @return void
145
     */
146 18
    protected function prepRoutes(ServerRequestInterface $request)
147
    {
148 18
        $this->buildNameIndex();
149
150 18
        $routes = array_merge(array_values($this->routes), array_values($this->namedRoutes));
151
152 18
        foreach ($routes as $key => $route) {
153
            // check for scheme condition
154 12
            if (! is_null($route->getScheme()) && $route->getScheme() !== $request->getUri()->getScheme()) {
155 3
                continue;
156
            }
157
158
            // check for domain condition
159 12
            if (! is_null($route->getHost()) && $route->getHost() !== $request->getUri()->getHost()) {
160 3
                continue;
161
            }
162
163 12
            $route->setContainer($this->container);
164
165 12
            if (is_null($route->getStrategy())) {
166 12
                $route->setStrategy($this->getStrategy());
167 12
            }
168
169 12
            $this->addRoute(
170 12
                $route->getMethods(),
171 12
                $this->parseRoutePath($route->getPath()),
172 12
                [$route, 'dispatch']
173 12
            );
174 18
        }
175 18
    }
176
177
    /**
178
     * Build an index of named routes.
179
     *
180
     * @return void
181
     */
182 27
    protected function buildNameIndex()
183
    {
184 27
        $this->processGroups();
185
186 27
        foreach ($this->routes as $key => $route) {
187 18
            if (! is_null($route->getName())) {
188 9
                unset($this->routes[$key]);
189 9
                $this->namedRoutes[$route->getName()] = $route;
190 9
            }
191 27
        }
192 27
    }
193
194
    /**
195
     * Process all groups.
196
     *
197
     * @return void
198
     */
199 27
    protected function processGroups()
200
    {
201 27
        foreach ($this->groups as $key => $group) {
202 3
            unset($this->groups[$key]);
203 3
            $group();
204 27
        }
205 27
    }
206
207
    /**
208
     * Get named route.
209
     *
210
     * @param string $name
211
     *
212
     * @return \League\Route\Route
213
     */
214 9
    public function getNamedRoute($name)
215
    {
216 9
        $this->buildNameIndex();
217
218 9
        if (array_key_exists($name, $this->namedRoutes)) {
219 6
            return $this->namedRoutes[$name];
220
        }
221
222 3
        throw new InvalidArgumentException(sprintf('No route of the name (%s) exists', $name));
223
    }
224
225
    /**
226
     * Add a convenient pattern matcher to the internal array for use with all routes.
227
     *
228
     * @param string $alias
229
     * @param string $regex
230
     *
231
     * @return void
232
     */
233 3
    public function addPatternMatcher($alias, $regex)
234
    {
235 3
        $pattern = '/{(.+?):' . $alias . '}/';
236 3
        $regex   = '{$1:' . $regex . '}';
237
238 3
        $this->patternMatchers[$pattern] = $regex;
239 3
    }
240
241
    /**
242
     * Convenience method to convert pre-defined key words in to regex strings.
243
     *
244
     * @param string $path
245
     *
246
     * @return string
247
     */
248 12
    protected function parseRoutePath($path)
249
    {
250 12
        return preg_replace(array_keys($this->patternMatchers), array_values($this->patternMatchers), $path);
251
    }
252
}
253