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 (#2586)
by
unknown
01:46
created

Router::isCacheFileWritable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
/**
3
 * Slim Framework (https://slimframework.com)
4
 *
5
 * @link      https://github.com/slimphp/Slim
6
 * @copyright Copyright (c) 2011-2017 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 Psr\Container\ContainerInterface;
13
use InvalidArgumentException;
14
use RuntimeException;
15
use Psr\Http\Message\ServerRequestInterface;
16
use FastRoute\RouteCollector;
17
use FastRoute\RouteParser;
18
use FastRoute\RouteParser\Std as StdParser;
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
     * Container Interface
35
     *
36
     * @var ContainerInterface
37
     */
38
    protected $container;
39
40
    /**
41
     * Parser
42
     *
43
     * @var \FastRoute\RouteParser
44
     */
45
    protected $routeParser;
46
47
    /**
48
     * Base path used in pathFor()
49
     *
50
     * @var string
51
     */
52
    protected $basePath = '';
53
54
    /**
55
     * Path to fast route cache file. Set to false to disable route caching
56
     *
57
     * @var string|False
58
     */
59
    protected $cacheFile = false;
60
61
    /**
62
     * Routes
63
     *
64
     * @var Route[]
65
     */
66
    protected $routes = [];
67
68
    /**
69
     * Route counter incrementer
70
     * @var int
71
     */
72
    protected $routeCounter = 0;
73
74
    /**
75
     * Route groups
76
     *
77
     * @var RouteGroup[]
78
     */
79
    protected $routeGroups = [];
80
81
    /**
82
     * @var \FastRoute\Dispatcher
83
     */
84
    protected $dispatcher;
85
86
    /**
87
     * Create new router
88
     *
89
     * @param RouteParser   $parser
90
     */
91
    public function __construct(RouteParser $parser = null)
92
    {
93
        $this->routeParser = $parser ?: new StdParser;
94
    }
95
96
    /**
97
     * Set the base path used in pathFor()
98
     *
99
     * @param string $basePath
100
     *
101
     * @return self
102
     */
103
    public function setBasePath($basePath)
104
    {
105
        if (!is_string($basePath)) {
106
            throw new InvalidArgumentException('Router basePath must be a string');
107
        }
108
109
        $this->basePath = $basePath;
110
111
        return $this;
112
    }
113
114
    /**
115
     * Get the base path used in pathFor()
116
     *
117
     * @return string
118
     */
119
    public function getBasePath()
120
    {
121
        return $this->basePath;
122
    }
123
124
    /**
125
     * Set path to fast route cache file. If this is false then route caching is disabled.
126
     *
127
     * @param string|false $cacheFile
128
     *
129
     * @return self
130
     */
131
    public function setCacheFile($cacheFile)
132
    {
133
        $this->cacheFile = $cacheFile;
134
135
        if (!is_string($cacheFile) && $cacheFile !== false) {
136
            throw new InvalidArgumentException('Router cache file must be a string or false');
137
        }
138
139 View Code Duplication
        if ($cacheFile && file_exists($cacheFile) && !$this->isCacheFileWritable($cacheFile)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $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...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
            throw new RuntimeException(sprintf('Router cache file `%s` is not writable', $cacheFile));
141
        }
142
143 View Code Duplication
        if ($cacheFile && !file_exists($cacheFile) && !$this->isCacheFileWritable(dirname($cacheFile))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $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...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
144
            throw new RuntimeException(sprintf('Router cache file directory `%s` is not writable', dirname($cacheFile)));
145
        }
146
147
        return $this;
148
    }
149
150
    /**
151
     * This method is in place to facilitate unit tests
152
     * @param $cacheFile
153
     *
154
     * @return bool
155
     */
156
    protected function isCacheFileWritable($cacheFile)
157
    {
158
        return is_writable($cacheFile);
159
    }
160
161
    /**
162
     * @param ContainerInterface $container
163
     */
164
    public function setContainer(ContainerInterface $container)
165
    {
166
        $this->container = $container;
167
    }
168
169
    /**
170
     * Add route
171
     *
172
     * @param  string[] $methods Array of HTTP methods
173
     * @param  string   $pattern The route pattern
174
     * @param  callable $handler The route callable
175
     *
176
     * @return RouteInterface
177
     *
178
     * @throws InvalidArgumentException if the route pattern isn't a string
179
     */
180
    public function map($methods, $pattern, $handler)
181
    {
182
        if (!is_string($pattern)) {
183
            throw new InvalidArgumentException('Route pattern must be a string');
184
        }
185
186
        // Prepend parent group pattern(s)
187
        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...
188
            $pattern = $this->processGroups() . $pattern;
189
        }
190
191
        // According to RFC methods are defined in uppercase (See RFC 7231)
192
        $methods = array_map("strtoupper", $methods);
193
194
        /** @var \Slim\Route */
195
        $route = $this->createRoute($methods, $pattern, $handler);
196
        // Add route
197
        $this->routes[$route->getIdentifier()] = $route;
198
        $this->routeCounter++;
199
200
        return $route;
201
    }
202
203
    /**
204
     * Dispatch router for HTTP request
205
     *
206
     * @param  ServerRequestInterface $request The current HTTP request object
207
     *
208
     * @return array
209
     *
210
     * @link   https://github.com/nikic/FastRoute/blob/master/src/Dispatcher.php
211
     */
212
    public function dispatch(ServerRequestInterface $request)
213
    {
214
        $uri = '/' . ltrim($request->getUri()->getPath(), '/');
215
216
        return $this->createDispatcher()->dispatch(
217
            $request->getMethod(),
218
            $uri
219
        );
220
    }
221
222
    /**
223
     * Create a new Route object
224
     *
225
     * @param  string[] $methods Array of HTTP methods
226
     * @param  string   $pattern The route pattern
227
     * @param  callable $callable The route callable
228
     *
229
     * @return \Slim\Interfaces\RouteInterface
230
     */
231
    protected function createRoute($methods, $pattern, $callable)
232
    {
233
        $route = new Route($methods, $pattern, $callable, $this->routeGroups, $this->routeCounter);
234
        if (!empty($this->container)) {
235
            $route->setContainer($this->container);
236
        }
237
238
        return $route;
239
    }
240
241
    /**
242
     * @return \FastRoute\Dispatcher
243
     */
244
    protected function createDispatcher()
245
    {
246
        if ($this->dispatcher) {
247
            return $this->dispatcher;
248
        }
249
250
        $routeDefinitionCallback = function (RouteCollector $r) {
251
            foreach ($this->getRoutes() as $route) {
252
                $r->addRoute($route->getMethods(), $route->getPattern(), $route->getIdentifier());
253
            }
254
        };
255
256
        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...
257
            $this->dispatcher = \FastRoute\cachedDispatcher($routeDefinitionCallback, [
258
                'routeParser' => $this->routeParser,
259
                'cacheFile' => $this->cacheFile,
260
            ]);
261
        } else {
262
            $this->dispatcher = \FastRoute\simpleDispatcher($routeDefinitionCallback, [
263
                'routeParser' => $this->routeParser,
264
            ]);
265
        }
266
267
        return $this->dispatcher;
268
    }
269
270
    /**
271
     * @param \FastRoute\Dispatcher $dispatcher
272
     */
273
    public function setDispatcher(Dispatcher $dispatcher)
274
    {
275
        $this->dispatcher = $dispatcher;
276
    }
277
278
    /**
279
     * Get route objects
280
     *
281
     * @return Route[]
282
     */
283
    public function getRoutes()
284
    {
285
        return $this->routes;
286
    }
287
288
    /**
289
     * Get named route object
290
     *
291
     * @param string $name        Route name
292
     *
293
     * @return Route
294
     *
295
     * @throws RuntimeException   If named route does not exist
296
     */
297
    public function getNamedRoute($name)
298
    {
299
        foreach ($this->routes as $route) {
300
            if ($name == $route->getName()) {
301
                return $route;
302
            }
303
        }
304
        throw new RuntimeException('Named route does not exist for name: ' . $name);
305
    }
306
307
    /**
308
     * Remove named route
309
     *
310
     * @param string $name        Route name
311
     *
312
     * @throws RuntimeException   If named route does not exist
313
     */
314
    public function removeNamedRoute($name)
315
    {
316
        $route = $this->getNamedRoute($name);
317
318
        // no exception, route exists, now remove by id
319
        unset($this->routes[$route->getIdentifier()]);
320
    }
321
322
    /**
323
     * Process route groups
324
     *
325
     * @return string A group pattern to prefix routes with
326
     */
327
    protected function processGroups()
328
    {
329
        $pattern = "";
330
        foreach ($this->routeGroups as $group) {
331
            $pattern .= $group->getPattern();
332
        }
333
        return $pattern;
334
    }
335
336
    /**
337
     * Add a route group to the array
338
     *
339
     * @param string   $pattern
340
     * @param callable $callable
341
     *
342
     * @return RouteGroupInterface
343
     */
344
    public function pushGroup($pattern, $callable)
345
    {
346
        $group = new RouteGroup($pattern, $callable);
347
        array_push($this->routeGroups, $group);
348
        return $group;
349
    }
350
351
    /**
352
     * Removes the last route group from the array
353
     *
354
     * @return RouteGroup|bool The RouteGroup if successful, else False
355
     */
356
    public function popGroup()
357
    {
358
        $group = array_pop($this->routeGroups);
359
        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 359 which is incompatible with the return type declared by the interface Slim\Interfaces\RouterInterface::popGroup of type boolean.
Loading history...
360
    }
361
362
    /**
363
     *
364
     * @param string $identifier
365
     * @return \Slim\Interfaces\RouteInterface
366
     */
367
    public function lookupRoute($identifier)
368
    {
369
        if (!isset($this->routes[$identifier])) {
370
            throw new RuntimeException('Route not found, looks like your route cache is stale.');
371
        }
372
        return $this->routes[$identifier];
373
    }
374
375
    /**
376
     * Build the path for a named route excluding the base path
377
     *
378
     * @param string $name        Route name
379
     * @param array  $data        Named argument replacement data
380
     * @param array  $queryParams Optional query string parameters
381
     *
382
     * @return string
383
     *
384
     * @throws RuntimeException         If named route does not exist
385
     * @throws InvalidArgumentException If required data not provided
386
     */
387
    public function relativePathFor($name, array $data = [], array $queryParams = [])
388
    {
389
        $route = $this->getNamedRoute($name);
390
        $pattern = $route->getPattern();
391
392
        $routeDatas = $this->routeParser->parse($pattern);
393
        // $routeDatas is an array of all possible routes that can be made. There is
394
        // one routedata for each optional parameter plus one for no optional parameters.
395
        //
396
        // The most specific is last, so we look for that first.
397
        $routeDatas = array_reverse($routeDatas);
398
399
        $segments = [];
400
        $segmentName = '';
401
        foreach ($routeDatas as $routeData) {
402
            foreach ($routeData as $item) {
403
                if (is_string($item)) {
404
                    // this segment is a static string
405
                    $segments[] = $item;
406
                    continue;
407
                }
408
409
                // This segment has a parameter: first element is the name
410
                if (!array_key_exists($item[0], $data)) {
411
                    // we don't have a data element for this segment: cancel
412
                    // testing this routeData item, so that we can try a less
413
                    // specific routeData item.
414
                    $segments = [];
415
                    $segmentName = $item[0];
416
                    break;
417
                }
418
                $segments[] = $data[$item[0]];
419
            }
420
            if (!empty($segments)) {
421
                // we found all the parameters for this route data, no need to check
422
                // less specific ones
423
                break;
424
            }
425
        }
426
427
        if (empty($segments)) {
428
            throw new InvalidArgumentException('Missing data for URL segment: ' . $segmentName);
429
        }
430
        $url = implode('', $segments);
431
432
        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...
433
            $url .= '?' . http_build_query($queryParams);
434
        }
435
436
        return $url;
437
    }
438
439
440
    /**
441
     * Build the path for a named route including the base path
442
     *
443
     * @param string $name        Route name
444
     * @param array  $data        Named argument replacement data
445
     * @param array  $queryParams Optional query string parameters
446
     *
447
     * @return string
448
     *
449
     * @throws RuntimeException         If named route does not exist
450
     * @throws InvalidArgumentException If required data not provided
451
     */
452
    public function pathFor($name, array $data = [], array $queryParams = [])
453
    {
454
        $url = $this->relativePathFor($name, $data, $queryParams);
455
456
        if ($this->basePath) {
457
            $url = $this->basePath . $url;
458
        }
459
460
        return $url;
461
    }
462
463
    /**
464
     * Build the path for a named route.
465
     *
466
     * This method is deprecated. Use pathFor() from now on.
467
     *
468
     * @param string $name        Route name
469
     * @param array  $data        Named argument replacement data
470
     * @param array  $queryParams Optional query string parameters
471
     *
472
     * @return string
473
     *
474
     * @throws RuntimeException         If named route does not exist
475
     * @throws InvalidArgumentException If required data not provided
476
     */
477
    public function urlFor($name, array $data = [], array $queryParams = [])
478
    {
479
        trigger_error('urlFor() is deprecated. Use pathFor() instead.', E_USER_DEPRECATED);
480
        return $this->pathFor($name, $data, $queryParams);
481
    }
482
}
483