Completed
Pull Request — master (#149)
by
unknown
23:57
created

RouteCollection::isRoutesPrepared()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 1
cts 1
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace League\Route;
4
5
use Exception;
6
use FastRoute\DataGenerator;
7
use FastRoute\DataGenerator\GroupCountBased as GroupCountBasedDataGenerator;
8
use FastRoute\RouteCollector;
9
use FastRoute\RouteParser;
10
use FastRoute\RouteParser\Std as StdRouteParser;
11
use InvalidArgumentException;
12
use League\Container\Container;
13
use League\Route\Middleware\ExecutionChain;
14
use League\Route\Middleware\StackAwareInterface as MiddlewareAwareInterface;
15
use League\Route\Middleware\StackAwareTrait as MiddlewareAwareTrait;
16
use League\Route\Strategy\ApplicationStrategy;
17
use League\Route\Strategy\StrategyAwareInterface;
18
use League\Route\Strategy\StrategyAwareTrait;
19
use Psr\Container\ContainerInterface;
20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
23
class RouteCollection extends RouteCollector implements
24
    MiddlewareAwareInterface,
25
    RouteCollectionInterface,
26
    StrategyAwareInterface
27
{
28
    use MiddlewareAwareTrait;
29
    use RouteCollectionMapTrait;
30
    use StrategyAwareTrait;
31
32
    /**
33
     * @var \Psr\Container\ContainerInterface
34
     */
35
    protected $container;
36
37
    /**
38
     * @var \League\Route\Route[]
39
     */
40
    protected $routes = [];
41
42
    /**
43
     * @var \League\Route\Route[]
44
     */
45
    protected $namedRoutes = [];
46
47
    /**
48
     * @var \League\Route\RouteGroup[]
49
     */
50
    protected $groups = [];
51
52
    /**
53
     * @var array
54
     */
55
    protected $patternMatchers = [
56
        '/{(.+?):number}/'        => '{$1:[0-9]+}',
57
        '/{(.+?):word}/'          => '{$1:[a-zA-Z]+}',
58
        '/{(.+?):alphanum_dash}/' => '{$1:[a-zA-Z0-9-_]+}',
59
        '/{(.+?):slug}/'          => '{$1:[a-z0-9-]+}',
60
        '/{(.+?):uuid}/'          => '{$1:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}+}'
61
    ];
62
63
    /**
64
     * Constructor.
65
     *
66
     * @param \Psr\Container\ContainerInterface $container
67
     * @param \FastRoute\RouteParser            $parser
68
     * @param \FastRoute\DataGenerator          $generator
69
     */
70 45
    public function __construct(
71 3
        ContainerInterface $container = null,
72
        RouteParser        $parser    = null,
73
        DataGenerator      $generator = null
74
    ) {
75 45
        $this->container = ($container instanceof ContainerInterface) ? $container : new Container;
76
77
        // build parent route collector
78 45
        $parser    = ($parser instanceof RouteParser) ? $parser : new StdRouteParser;
79 45
        $generator = ($generator instanceof DataGenerator) ? $generator : new GroupCountBasedDataGenerator;
80 45
        parent::__construct($parser, $generator);
81 45
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86 30
    public function map($method, $path, $handler)
87
    {
88 30
        $path  = sprintf('/%s', ltrim($path, '/'));
89 30
        $route = (new Route)->setMethods((array) $method)->setPath($path)->setCallable($handler);
90
91 30
        $this->routes[] = $route;
92
93 30
        return $route;
94
    }
95
96
    /**
97
     * Add a group of routes to the collection.
98
     *
99
     * @param string   $prefix
100
     * @param callable $group
101
     *
102
     * @return \League\Route\RouteGroup
103
     */
104 6
    public function group($prefix, callable $group)
105
    {
106 6
        $group          = new RouteGroup($prefix, $group, $this);
107 6
        $this->groups[] = $group;
108
109 6
        return $group;
110
    }
111
112
    /**
113
     * Dispatch the route based on the request.
114
     *
115
     * @param \Psr\Http\Message\ServerRequestInterface $request
116
     * @param \Psr\Http\Message\ResponseInterface      $response
117
     *
118
     * @return \Psr\Http\Message\ResponseInterface
119
     */
120 24
    public function dispatch(ServerRequestInterface $request, ResponseInterface $response)
121
    {
122 24
        $dispatcher = $this->getDispatcher($request);
123 24
        $execChain  = $dispatcher->handle($request);
124
125 18
        foreach ($this->getMiddlewareStack() as $middleware) {
126
            $execChain->middleware($middleware);
127 18
        }
128
129
        try {
130 18
            return $execChain->execute($request, $response);
131 9
        } catch (Exception $exception) {
132 9
            $middleware = $this->getStrategy()->getExceptionDecorator($exception);
133 6
            return (new ExecutionChain)->middleware($middleware)->execute($request, $response);
134
        }
135
    }
136
137
    /**
138
     * Return a fully configured dispatcher.
139
     *
140
     * @param \Psr\Http\Message\ServerRequestInterface $request
141
     *
142
     * @return \League\Route\Dispatcher
143
     */
144 27
    public function getDispatcher(ServerRequestInterface $request)
145
    {
146 27
        if (is_null($this->getStrategy())) {
147 15
            $this->setStrategy(new ApplicationStrategy);
148 15
        }
149
150 27
        if( !$this->isRoutesPrepared() )
151
        {
152 27
            $this->prepRoutes($request);
153
        }
154
155
        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...
156
    }
157
158
    /**
159
     * @return bool
160
     */
161
    public function isRoutesPrepared()
162
    {
163 27
        return !empty($this->dataGenerator->getData()[0]);
164
    }
165 27
166
    /**
167 27
     * Prepare all routes, build name index and filter out none matching
168
     * routes before being passed off to the parser.
169 27
     *
170
     * @param \Psr\Http\Message\ServerRequestInterface $request
171 21
     *
172 3
     * @return void
173
     */
174
    protected function prepRoutes(ServerRequestInterface $request)
175
    {
176 21
        $this->buildNameIndex();
177 3
178
        $routes = array_merge(array_values($this->routes), array_values($this->namedRoutes));
179
180 21
        foreach ($routes as $key => $route) {
181
            // check for scheme condition
182 21
            if (! is_null($route->getScheme()) && $route->getScheme() !== $request->getUri()->getScheme()) {
183 21
                continue;
184 21
            }
185
186 21
            // check for domain condition
187 21
            if (! is_null($route->getHost()) && $route->getHost() !== $request->getUri()->getHost()) {
188 21
                continue;
189 21
            }
190 21
191 27
            $route->setContainer($this->container);
192 27
193
            if (is_null($route->getStrategy())) {
194
                $route->setStrategy($this->getStrategy());
195
            }
196
197
            $this->addRoute(
198
                $route->getMethods(),
199 36
                $this->parseRoutePath($route->getPath()),
200
                [$route, 'getExecutionChain']
201 36
            );
202
        }
203 36
    }
204 27
205 6
    /**
206 6
     * Build an index of named routes.
207 6
     *
208 36
     * @return void
209 36
     */
210
    protected function buildNameIndex()
211
    {
212
        $this->processGroups();
213
214
        foreach ($this->routes as $key => $route) {
215
            if (! is_null($route->getName())) {
216 36
                unset($this->routes[$key]);
217
                $this->namedRoutes[$route->getName()] = $route;
218 36
            }
219 3
        }
220 3
    }
221 36
222 36
    /**
223
     * Process all groups.
224
     *
225
     * @return void
226
     */
227
    protected function processGroups()
228
    {
229
        foreach ($this->groups as $key => $group) {
230
            unset($this->groups[$key]);
231 9
            $group();
232
        }
233 9
    }
234
235 9
    /**
236 6
     * Get named route.
237
     *
238
     * @param string $name
239 3
     *
240
     * @return \League\Route\Route
241
     */
242
    public function getNamedRoute($name)
243
    {
244
        $this->buildNameIndex();
245
246
        if (array_key_exists($name, $this->namedRoutes)) {
247
            return $this->namedRoutes[$name];
248
        }
249
250 3
        throw new InvalidArgumentException(sprintf('No route of the name (%s) exists', $name));
251
    }
252 3
253 3
    /**
254
     * Add a convenient pattern matcher to the internal array for use with all routes.
255 3
     *
256 3
     * @param string $alias
257
     * @param string $regex
258
     *
259
     * @return void
260
     */
261
    public function addPatternMatcher($alias, $regex)
262
    {
263
        $pattern = '/{(.+?):' . $alias . '}/';
264
        $regex   = '{$1:' . $regex . '}';
265 21
266
        $this->patternMatchers[$pattern] = $regex;
267 21
    }
268
269
    /**
270
     * Convenience method to convert pre-defined key words in to regex strings.
271
     *
272
     * @param string $path
273
     *
274
     * @return string
275
     */
276
    protected function parseRoutePath($path)
277
    {
278
        return preg_replace(array_keys($this->patternMatchers), array_values($this->patternMatchers), $path);
279
    }
280
}
281