GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — 3.x (#1906)
by
unknown
03:46
created

Router::createRoute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 3
1
<?php
2
/**
3
 * Slim Framework (http://slimframework.com)
4
 *
5
 * @link      https://github.com/slimphp/Slim
6
 * @copyright Copyright (c) 2011-2016 Josh Lockhart
7
 * @license   https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License)
8
 */
9
namespace Slim;
10
11
use FastRoute\Dispatcher;
12
use InvalidArgumentException;
13
use RuntimeException;
14
use Psr\Http\Message\ServerRequestInterface;
15
use FastRoute\RouteCollector;
16
use FastRoute\RouteParser;
17
use FastRoute\RouteParser\Std as StdParser;
18
use FastRoute\DataGenerator;
19
use Slim\Interfaces\RouteGroupInterface;
20
use Slim\Interfaces\RouterInterface;
21
use Slim\Interfaces\RouteInterface;
22
23
/**
24
 * Router
25
 *
26
 * This class organizes Slim application route objects. It is responsible
27
 * for registering route objects, assigning names to route objects,
28
 * finding routes that match the current HTTP request, and creating
29
 * URLs for a named route.
30
 */
31
class Router implements RouterInterface
32
{
33
    /**
34
     * Parser
35
     *
36
     * @var \FastRoute\RouteParser
37
     */
38
    protected $routeParser;
39
40
    /**
41
     * Base path used in pathFor()
42
     *
43
     * @var string
44
     */
45
    protected $basePath = '';
46
47
    /**
48
     * Path to fast route cache file. Set to false to disable route caching
49
     *
50
     * @var string|False
51
     */
52
    protected $cacheFile = false;
53
54
    /**
55
     * Routes
56
     *
57
     * @var Route[]
58
     */
59
    protected $routes = [];
60
61
    /**
62
     * Route counter incrementer
63
     * @var int
64
     */
65
    protected $routeCounter = 0;
66
67
    /**
68
     * Route groups
69
     *
70
     * @var RouteGroup[]
71
     */
72
    protected $routeGroups = [];
73
74
    /**
75
     * @var \FastRoute\Dispatcher
76
     */
77
    protected $dispatcher;
78
79
    /**
80
     * Create new router
81
     *
82
     * @param RouteParser   $parser
83
     */
84
    public function __construct(RouteParser $parser = null)
85
    {
86
        $this->routeParser = $parser ?: new StdParser;
87
    }
88
89
    /**
90
     * Set the base path used in pathFor()
91
     *
92
     * @param string $basePath
93
     *
94
     * @return self
95
     */
96
    public function setBasePath($basePath)
97
    {
98
        if (!is_string($basePath)) {
99
            throw new InvalidArgumentException('Router basePath must be a string');
100
        }
101
102
        $this->basePath = $basePath;
103
104
        return $this;
105
    }
106
107
    /**
108
     * Set path to fast route cache file. If this is false then route caching is disabled.
109
     *
110
     * @param string|false $cacheFile
111
     *
112
     * @return self
113
     */
114
    public function setCacheFile($cacheFile)
115
    {
116
        if (!is_string($cacheFile) && $cacheFile !== false) {
117
            throw new InvalidArgumentException('Router cacheFile must be a string or false');
118
        }
119
120
        $this->cacheFile = $cacheFile;
121
122
        if ($cacheFile !== false && !is_writable(dirname($cacheFile))) {
123
            throw new RuntimeException('Router cacheFile directory must be writable');
124
        }
125
126
127
        return $this;
128
    }
129
130
    /**
131
     * Add route
132
     *
133
     * @param  string[] $methods Array of HTTP methods
134
     * @param  string   $pattern The route pattern
135
     * @param  callable $handler The route callable
136
     *
137
     * @return RouteInterface
138
     *
139
     * @throws InvalidArgumentException if the route pattern isn't a string
140
     */
141
    public function map($methods, $pattern, $handler)
142
    {
143
        if (!is_string($pattern)) {
144
            throw new InvalidArgumentException('Route pattern must be a string');
145
        }
146
147
        // Prepend parent group pattern(s)
148
        if ($this->routeGroups) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->routeGroups of type Slim\RouteGroup[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
149
            $pattern = $this->processGroups() . $pattern;
150
        }
151
152
        // According to RFC methods are defined in uppercase (See RFC 7231)
153
        $methods = array_map("strtoupper", $methods);
154
155
        // Add route
156
        $route = $this->createRoute($methods, $pattern, $handler);
157
        $this->routes[$route->getIdentifier()] = $route;
158
        $this->routeCounter++;
159
160
        return $route;
161
    }
162
163
    /**
164
     * Dispatch router for HTTP request
165
     *
166
     * @param  ServerRequestInterface $request The current HTTP request object
167
     *
168
     * @return array
169
     *
170
     * @link   https://github.com/nikic/FastRoute/blob/master/src/Dispatcher.php
171
     */
172
    public function dispatch(ServerRequestInterface $request)
173
    {
174
        $uri = '/' . ltrim($request->getUri()->getPath(), '/');
175
176
        return $this->createDispatcher()->dispatch(
177
            $request->getMethod(),
178
            $uri
179
        );
180
    }
181
182
    /**
183
     * @return Slim\Route
184
     */
185
    protected function createRoute($methods, $pattern, $callable)
186
    {
187
        return new Route($methods, $pattern, $callable, $this->routeGroups, $this->routeCounter);
188
    }
189
190
    /**
191
     * @return \FastRoute\Dispatcher
192
     */
193
    protected function createDispatcher()
194
    {
195
        if ($this->dispatcher) {
196
            return $this->dispatcher;
197
        }
198
199
        $routeDefinitionCallback = function (RouteCollector $r) {
200
            foreach ($this->getRoutes() as $route) {
201
                $r->addRoute($route->getMethods(), $route->getPattern(), $route->getIdentifier());
202
            }
203
        };
204
205
        if ($this->cacheFile) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cacheFile of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
206
            $this->dispatcher = \FastRoute\cachedDispatcher($routeDefinitionCallback, [
207
                'routeParser' => $this->routeParser,
208
                'cacheFile' => $this->cacheFile,
209
            ]);
210
        } else {
211
            $this->dispatcher = \FastRoute\simpleDispatcher($routeDefinitionCallback, [
212
                'routeParser' => $this->routeParser,
213
            ]);
214
        }
215
216
        return $this->dispatcher;
217
    }
218
219
    /**
220
     * @param \FastRoute\Dispatcher $dispatcher
221
     */
222
    public function setDispatcher(Dispatcher $dispatcher)
223
    {
224
        $this->dispatcher = $dispatcher;
225
    }
226
227
    /**
228
     * Get route objects
229
     *
230
     * @return Route[]
231
     */
232
    public function getRoutes()
233
    {
234
        return $this->routes;
235
    }
236
237
    /**
238
     * Get named route object
239
     *
240
     * @param string $name        Route name
241
     *
242
     * @return Route
243
     *
244
     * @throws RuntimeException   If named route does not exist
245
     */
246
    public function getNamedRoute($name)
247
    {
248
        foreach ($this->routes as $route) {
249
            if ($name == $route->getName()) {
250
                return $route;
251
            }
252
        }
253
        throw new RuntimeException('Named route does not exist for name: ' . $name);
254
    }
255
256
    /**
257
     * Remove named route
258
     *
259
     * @param string $name        Route name
260
     *
261
     * @throws RuntimeException   If named route does not exist
262
     */
263
    public function removeNamedRoute($name)
264
    {
265
        $route = $this->getNamedRoute($name);
266
267
        // no exception, route exists, now remove by id
268
        unset($this->routes[$route->getIdentifier()]);
269
    }
270
271
    /**
272
     * Process route groups
273
     *
274
     * @return string A group pattern to prefix routes with
275
     */
276
    protected function processGroups()
277
    {
278
        $pattern = "";
279
        foreach ($this->routeGroups as $group) {
280
            $pattern .= $group->getPattern();
281
        }
282
        return $pattern;
283
    }
284
285
    /**
286
     * Add a route group to the array
287
     *
288
     * @param string   $pattern
289
     * @param callable $callable
290
     *
291
     * @return RouteGroupInterface
292
     */
293
    public function pushGroup($pattern, $callable)
294
    {
295
        $group = new RouteGroup($pattern, $callable);
296
        array_push($this->routeGroups, $group);
297
        return $group;
298
    }
299
300
    /**
301
     * Removes the last route group from the array
302
     *
303
     * @return RouteGroup|bool The RouteGroup if successful, else False
304
     */
305
    public function popGroup()
306
    {
307
        $group = array_pop($this->routeGroups);
308
        return $group instanceof RouteGroup ? $group : false;
0 ignored issues
show
Bug Compatibility introduced by
The expression $group instanceof \Slim\...Group ? $group : false; of type Slim\RouteGroup|false adds the type Slim\RouteGroup to the return on line 308 which is incompatible with the return type declared by the interface Slim\Interfaces\RouterInterface::popGroup of type boolean.
Loading history...
309
    }
310
311
    /**
312
     * @param $identifier
313
     * @return \Slim\Interfaces\RouteInterface
314
     */
315
    public function lookupRoute($identifier)
316
    {
317
        if (!isset($this->routes[$identifier])) {
318
            throw new RuntimeException('Route not found, looks like your route cache is stale.');
319
        }
320
        return $this->routes[$identifier];
321
    }
322
323
    /**
324
     * Build the path for a named route excluding the base path
325
     *
326
     * @param string $name        Route name
327
     * @param array  $data        Named argument replacement data
328
     * @param array  $queryParams Optional query string parameters
329
     *
330
     * @return string
331
     *
332
     * @throws RuntimeException         If named route does not exist
333
     * @throws InvalidArgumentException If required data not provided
334
     */
335
    public function relativePathFor($name, array $data = [], array $queryParams = [])
336
    {
337
        $route = $this->getNamedRoute($name);
338
        $pattern = $route->getPattern();
339
340
        $routeDatas = $this->routeParser->parse($pattern);
341
        // $routeDatas is an array of all possible routes that can be made. There is
342
        // one routedata for each optional parameter plus one for no optional parameters.
343
        //
344
        // The most specific is last, so we look for that first.
345
        $routeDatas = array_reverse($routeDatas);
346
347
        $segments = [];
348
        foreach ($routeDatas as $routeData) {
349
            foreach ($routeData as $item) {
350
                if (is_string($item)) {
351
                    // this segment is a static string
352
                    $segments[] = $item;
353
                    continue;
354
                }
355
356
                // This segment has a parameter: first element is the name
357
                if (!array_key_exists($item[0], $data)) {
358
                    // we don't have a data element for this segment: cancel
359
                    // testing this routeData item, so that we can try a less
360
                    // specific routeData item.
361
                    $segments = [];
362
                    $segmentName = $item[0];
363
                    break;
364
                }
365
                $segments[] = $data[$item[0]];
366
            }
367
            if (!empty($segments)) {
368
                // we found all the parameters for this route data, no need to check
369
                // less specific ones
370
                break;
371
            }
372
        }
373
374
        if (empty($segments)) {
375
            throw new InvalidArgumentException('Missing data for URL segment: ' . $segmentName);
376
        }
377
        $url = implode('', $segments);
378
379
        if ($queryParams) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $queryParams of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
380
            $url .= '?' . http_build_query($queryParams);
381
        }
382
383
        return $url;
384
    }
385
386
387
    /**
388
     * Build the path for a named route including the base path
389
     *
390
     * @param string $name        Route name
391
     * @param array  $data        Named argument replacement data
392
     * @param array  $queryParams Optional query string parameters
393
     *
394
     * @return string
395
     *
396
     * @throws RuntimeException         If named route does not exist
397
     * @throws InvalidArgumentException If required data not provided
398
     */
399
    public function pathFor($name, array $data = [], array $queryParams = [])
400
    {
401
        $url = $this->relativePathFor($name, $data, $queryParams);
402
403
        if ($this->basePath) {
404
            $url = $this->basePath . $url;
405
        }
406
407
        return $url;
408
    }
409
410
    /**
411
     * Build the path for a named route.
412
     *
413
     * This method is deprecated. Use pathFor() from now on.
414
     *
415
     * @param string $name        Route name
416
     * @param array  $data        Named argument replacement data
417
     * @param array  $queryParams Optional query string parameters
418
     *
419
     * @return string
420
     *
421
     * @throws RuntimeException         If named route does not exist
422
     * @throws InvalidArgumentException If required data not provided
423
     */
424
    public function urlFor($name, array $data = [], array $queryParams = [])
425
    {
426
        trigger_error('urlFor() is deprecated. Use pathFor() instead.', E_USER_DEPRECATED);
427
        return $this->pathFor($name, $data, $queryParams);
428
    }
429
}
430