RouteBuilder::build()   C
last analyzed

Complexity

Conditions 16
Paths 96

Size

Total Lines 69
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 16.1479

Importance

Changes 0
Metric Value
eloc 36
dl 0
loc 69
ccs 33
cts 36
cp 0.9167
rs 5.5666
c 0
b 0
f 0
cc 16
nc 96
nop 0
crap 16.1479

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 *  This file is part of the Micro framework package.
7
 *
8
 *  (c) Stanislau Komar <[email protected]>
9
 *
10
 *  For the full copyright and license information, please view the LICENSE
11
 *  file that was distributed with this source code.
12
 */
13
14
namespace Micro\Plugin\Http\Business\Route;
15
16
use Micro\Plugin\Http\Exception\RouteInvalidConfigurationException;
17
18
/**
19
 * @author Stanislau Komar <[email protected]>
20
 */
21
class RouteBuilder implements RouteBuilderInterface
22
{
23
    private string|null $name;
24
25
    /**
26
     * @var array<class-string, string|null>|class-string|\Closure|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string, stri...ss-string|\Closure|null at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string, string|null>|class-string|\Closure|null.
Loading history...
27
     */
28
    private mixed $action;
29
30
    private string|null $uri;
31
32
    /**
33
     * @var array|string[]
34
     */
35
    private array $methods;
36
37
    /**
38
     * @param string[] $methodsByDefault
39
     */
40
    public function __construct(
41
        private readonly array $methodsByDefault = [
42
            'PUT', 'POST', 'PATCH', 'GET', 'DELETE',
43
        ],
44
    ) {
45 13
        $this->name = null;
46 13
        $this->action = null;
47 13
        $this->uri = null;
48 13
        $this->methods = $this->methodsByDefault;
49
    }
50
51
    /**
52
     * {@inheritDoc}
53
     */
54
    public function setName(string $name): self
55
    {
56 11
        $this->name = $name;
57
58 11
        return $this;
59
    }
60
61
    /**
62
     * {@inheritDoc}
63
     */
64
    public function addMethod(string $method): self
65
    {
66 3
        if (!\in_array($method, $this->methods)) {
67 3
            $this->methods[] = mb_strtoupper($method);
68
        }
69
70 3
        return $this;
71
    }
72
73
    /**
74
     * {@inheritDoc}
75
     */
76
    public function setMethods(array $methods): self
77
    {
78 3
        $this->methods = [];
79
80 3
        foreach ($methods as $method) {
81 3
            $this->addMethod($method);
82
        }
83
84 3
        return $this;
85
    }
86
87
    /**
88
     * {@inheritDoc}
89
     */
90
    public function setController(string|array|\Closure $action): self
91
    {
92 10
        $this->action = $action;
93
94 10
        return $this;
95
    }
96
97
    /**
98
     * {@inheritDoc}
99
     */
100
    public function setUri(string $uri): self
101
    {
102 11
        $this->uri = $uri;
103
104 11
        return $this;
105
    }
106
107
    /**
108
     * TODO: Move pattern builder to separate class.
109
     *
110
     * {@inheritDoc}
111
     */
112
    public function build(): RouteInterface
113
    {
114 12
        $exceptions = [];
115
116 12
        if (!$this->uri) {
117 1
            $this->uri = '';
118
119 1
            $exceptions[] = 'Uri can not be empty.';
120
        }
121
122 12
        if ($this->name && !preg_match('/^(.[aA-zZ_])/', $this->name)) {
123
            $exceptions[] = 'The route name must match "aA-zZ0-9_".';
124
        }
125
126 12
        if (!$this->action) {
127 2
            $exceptions[] = 'The route action can not be empty and should be callable.';
128
        }
129
130
        if (
131 12
            $this->action &&
132
            (
133 12
                !\is_callable($this->action) &&
134 12
                (\is_string($this->action) && (class_exists($this->action) && !$this->name))
135
            )
136
        ) {
137
            $exceptions[] = 'The route action should be callable. Examples: `[object, "method|<route_name>"], [Classname, "metnod|<routeName>"], Classname::method, Classname, function() {}` Current value: '.$this->action;
138
        }
139
140 12
        if (!\count($this->methods)) {
141
            $exceptions[] = 'The route should be contain one or more methods from %s::class.';
142
        }
143
144 12
        if (\count($exceptions)) {
145 2
            $exception = new RouteInvalidConfigurationException($this->name ?: $this->uri ?: 'Undefined', $exceptions);
146
147 2
            $this->clear();
148
149 2
            throw $exception;
150
        }
151
152 10
        $pattern = null;
153 10
        $parameters = null;
154
        /** @psalm-suppress PossiblyNullArgument */
155 10
        $isDynamic = preg_match_all('/\{\s*([^}\s]*)\s*}/', $this->uri, $matches);
156 10
        if ($isDynamic) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $isDynamic of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
157 9
            $parameters = $matches[1];
158
            /** @psalm-suppress PossiblyNullArgument */
159 9
            $pattern = '/'.addcslashes($this->uri, '/.').'$/';
160
161 9
            foreach ($matches[0] as $replaced) {
162 9
                $pattern = str_replace($replaced, '(.[aA-zZ0-9-_]+)', $pattern);
163
            }
164
        }
165
        /**
166
         * @psalm-suppress PossiblyNullArgument
167
         * @psalm-suppress MixedArgumentTypeCoercion
168
         */
169 10
        $route = new Route(
170 10
            $this->uri,
171 10
            $this->action,
172 10
            $this->methods,
173 10
            $this->name,
174 10
            $pattern,
175 10
            $parameters
176 10
        );
177
178 10
        $this->clear();
179
180 10
        return $route;
181
    }
182
183
    protected function clear(): void
184
    {
185 12
        $this->uri = null;
186 12
        $this->action = null;
187 12
        $this->methods = $this->methodsByDefault;
188 12
        $this->name = null;
189
    }
190
}
191