Issues (120)

src/Traits/ResolverTrait.php (6 issues)

1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of Flight Routing.
5
 *
6
 * PHP version 8.0 and above required
7
 *
8
 * @author    Divine Niiquaye Ibok <[email protected]>
9
 * @copyright 2019 Divine Niiquaye Ibok (https://divinenii.com/)
10
 * @license   https://opensource.org/licenses/BSD-3-Clause License
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 */
15
16
namespace Flight\Routing\Traits;
17
18
use Flight\Routing\Exceptions\{MethodNotAllowedException, UriHandlerException};
19
use Psr\Http\Message\UriInterface;
20
21
/**
22
 * The default implementation for route match.
23
 *
24
 * @author Divine Niiquaye Ibok <[email protected]>
25
 */
26
trait ResolverTrait
27
{
28
    /** @var array<int|string,mixed> */
29
    private array $optimized = [];
30
31
    /**
32
     * @param array<string,mixed>                  $route
33
     * @param array<int,array<string,bool|string>> $errors
34
     */
35 58
    protected function assertRoute(string $method, UriInterface $uri, array &$route, array &$errors): bool
36
    {
37 58
        $matched = true;
38
39 58
        if (!\array_key_exists($method, $route['methods'] ?? [])) {
40 7
            $errors[0] += $route['methods'] ?? [];
41 7
            $matched = false;
42 57
        } elseif (isset($route['hosts']) && !\array_key_exists($errors[2] ??= \rtrim($uri->getHost().':'.$uri->getPort(), ':'), $route['hosts'])) {
0 ignored issues
show
It seems like AssignCoalesceNode can also be of type array<string,boolean|string>; however, parameter $key of array_key_exists() does only seem to accept integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

42
        } elseif (isset($route['hosts']) && !\array_key_exists(/** @scrutinizer ignore-type */ $errors[2] ??= \rtrim($uri->getHost().':'.$uri->getPort(), ':'), $route['hosts'])) {
Loading history...
43 2
            $hosts = \array_keys($route['hosts'], true, true);
44 2
            [$hostsRegex, $hostVar] = $this->compiler->compile(\implode('|', $hosts), $route['placeholders'] ?? []);
45
46 2
            if ($matched = 1 === \preg_match($hostsRegex.'i', $errors[2], $matches, \PREG_UNMATCHED_AS_NULL)) {
0 ignored issues
show
It seems like $errors[2] can also be of type array<string,boolean|string>; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

46
            if ($matched = 1 === \preg_match($hostsRegex.'i', /** @scrutinizer ignore-type */ $errors[2], $matches, \PREG_UNMATCHED_AS_NULL)) {
Loading history...
47 2
                foreach ($hostVar as $key => $value) {
48 1
                    $route['arguments'][$key] = $matches[$key] ?: $route['defaults'][$key] ?? $value;
49
                }
50
            }
51 55
        } elseif (isset($route['schemes']) && !\array_key_exists($uri->getScheme(), $route['schemes'])) {
52 1
            $errors[1] += $route['schemes'] ?? [];
53 1
            $matched = false;
54
        }
55
56 58
        return $matched;
57
    }
58
59
    /**
60
     * @return null|array<string,mixed>
61
     */
62 72
    protected function resolveRoute(string $path, string $method, UriInterface $uri): ?array
63
    {
64 72
        $errors = [[], []];
65
66 72
        foreach ($this->getCollection()->getRoutes() as $i => $r) {
0 ignored issues
show
It seems like getCollection() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

66
        foreach ($this->/** @scrutinizer ignore-call */ getCollection()->getRoutes() as $i => $r) {
Loading history...
67 71
            if (isset($r['prefix']) && !\str_starts_with($path, '/'.\ltrim($r['prefix'], '/'))) {
68 11
                continue;
69
            }
70 62
            [$p, $v] = $this->optimized[$i] ??= $this->compiler->compile('/'.\ltrim($r['path'], '/'), $r['placeholders'] ?? []);
71
72 62
            if (!\preg_match($p, $path, $m, \PREG_UNMATCHED_AS_NULL) || !$this->assertRoute($method, $uri, $r, $errors)) {
73 13
                continue;
74
            }
75
76 51
            foreach ($v as $key => $value) {
77 27
                $r['arguments'][$key] = $m[$key] ?? $r['defaults'][$key] ?? $value;
78
            }
79
80 51
            return $r;
81
        }
82
83 23
        return $this->resolveError($errors, $method, $uri);
0 ignored issues
show
Are you sure the usage of $this->resolveError($errors, $method, $uri) targeting Flight\Routing\Traits\Re...erTrait::resolveError() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
84
    }
85
86
    /**
87
     * @return null|array<string,mixed>
88
     */
89 4
    protected function resolveCache(string $path, string $method, UriInterface $uri): ?array
90
    {
91 4
        $errors = [[], []];
92 4
        $routes = $this->optimized[2] ?? $this->doCache();
0 ignored issues
show
It seems like doCache() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

92
        $routes = $this->optimized[2] ?? $this->/** @scrutinizer ignore-call */ doCache();
Loading history...
93
94 4
        if (!$matched = $this->optimized[0][$path] ?? $this->optimized[1][0]($path)) {
95 4
            return null;
96
        }
97
98 4
        foreach ($matched as $match) {
99 4
            $r = $routes[$o = \intval($match['MARK'] ?? $match)] ?? $routes->getRoutes()[$o];
100
101 4
            if (!$this->assertRoute($method, $uri, $r, $errors)) {
102 4
                continue;
103
            }
104
105 4
            if (isset($match['MARK'])) {
106 4
                $i = 0;
107
108 4
                foreach ($this->optimized[1][1][$o] ?? [] as $key => $value) {
109 4
                    $r['arguments'][$key] = $match[++$i] ?: $r['defaults'][$key] ?? $value;
110
                }
111
            }
112
113 4
            return $r;
114
        }
115
116 4
        return $this->resolveError($errors, $method, $uri);
0 ignored issues
show
Are you sure the usage of $this->resolveError($errors, $method, $uri) targeting Flight\Routing\Traits\Re...erTrait::resolveError() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
117
    }
118
119
    /**
120
     * @param array<int,array<string,bool|string>> $errors
121
     */
122 27
    protected function resolveError(array $errors, string $method, UriInterface $uri)
123
    {
124 27
        if (!empty($errors[0])) {
125 7
            throw new MethodNotAllowedException(\array_keys($errors[0]), $uri->getPath(), $method);
126
        }
127
128 22
        if (!empty($errors[1])) {
129 1
            throw new UriHandlerException(
130 1
                \sprintf(
131
                    'Route with "%s" path requires request scheme(s) [%s], "%s" is invalid.',
132 1
                    $uri->getPath(),
133 1
                    \implode(', ', \array_keys($errors[1])),
134 1
                    $uri->getScheme(),
135
                ),
136
                400
137
            );
138
        }
139
140 21
        return null;
141
    }
142
}
143