Completed
Push — master ( 6e909e...c441c1 )
by Phil
03:17
created

src/RouteCollection.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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