generateSuggestion()   B
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 54
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 30
c 0
b 0
f 0
dl 0
loc 54
ccs 31
cts 31
cp 1
rs 8.8177
nc 6
nop 0
cc 6
crap 6

How to fix   Long Method   

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
namespace Yiisoft\Middleware\Dispatcher;
6
7
use InvalidArgumentException;
8
use Psr\Http\Server\MiddlewareInterface;
9
use Yiisoft\Definitions\Exception\InvalidConfigException;
10
use Yiisoft\Definitions\Helpers\DefinitionValidator;
11
use Yiisoft\FriendlyException\FriendlyExceptionInterface;
12
13
use function array_slice;
14
use function count;
15
use function gettype;
16
use function is_array;
17
use function is_bool;
18
use function is_float;
19
use function is_int;
20
use function is_object;
21
use function is_string;
22
23
final class InvalidMiddlewareDefinitionException extends InvalidArgumentException implements FriendlyExceptionInterface
24
{
25
    private string $definitionString;
26
27 21
    public function __construct(
28
        private mixed $definition
29
    ) {
30 21
        $this->definitionString = $this->convertDefinitionToString($definition);
31
32 21
        parent::__construct(
33 21
            'Parameter should be either PSR middleware class name or a callable. Got ' . $this->definitionString . '.'
34 21
        );
35
    }
36
37 1
    public function getName(): string
38
    {
39 1
        return 'Invalid middleware definition';
40
    }
41
42 6
    public function getSolution(): ?string
43
    {
44 6
        $solution = [
45 6
            <<<SOLUTION
46 6
            ## Got definition value
47
48 6
            `{$this->definitionString}`
49 6
            SOLUTION
50 6
        ];
51
52 6
        $suggestion = $this->generateSuggestion();
53 6
        if ($suggestion !== null) {
54 5
            $solution[] = '## Suggestion';
55 5
            $solution[] = $suggestion;
56
        }
57
58 6
        $solution[] = <<<SOLUTION
59
        ## Middleware definition examples
60
61
        PSR middleware class name:
62
63
        ```php
64
        Yiisoft\Session\SessionMiddleware::class
65
        ```
66
67
        PSR middleware array definition:
68
69
        ```php
70
        [
71
            'class' => MyMiddleware::class,
72
            '__construct()' => [
73
                'someVar' => 42,
74
            ],
75
        ]
76
        ```
77
78
        Closure that returns `ResponseInterface`:
79
80
        ```php
81
        static function (): ResponseInterface {
82
            return new Response(418);
83
        },
84
        ```
85
86
        Closure that returns `MiddlewareInterface`:
87
88
        ```php
89
        static function (): MiddlewareInterface {
90
            return new TestMiddleware();
91
        }
92
        ```
93
94
        Action in controller:
95
96
        ```php
97
        [App\Backend\UserController::class, 'index']
98
        ```
99
100
        ## Related links
101
102
        - [Array definition syntax](https://github.com/yiisoft/definitions#arraydefinition)
103
        - [Callable PHP documentation](https://www.php.net/manual/language.types.callable.php)
104 6
        SOLUTION;
105
106 6
        return implode("\n\n", $solution);
107
    }
108
109 6
    private function generateSuggestion(): ?string
110
    {
111 6
        if ($this->isControllerWithNonExistAction()) {
112 1
            return <<<SOLUTION
113 1
            Class `{$this->definition[0]}` exists, but does not contain method `{$this->definition[1]}()`.
114
115 1
            Try adding `{$this->definition[1]}()` action to `{$this->definition[0]}` controller:
116
117
            ```php
118 1
            public function {$this->definition[1]}(): ResponseInterface
119
            {
120
                // TODO: Implement your action
121
            }
122
            ```
123 1
            SOLUTION;
124
        }
125
126 5
        if ($this->isNotMiddlewareClassName()) {
127 1
            return sprintf(
128 1
                'Class `%s` exists, but does not implement `%s`.',
129 1
                $this->definition,
130 1
                MiddlewareInterface::class
131 1
            );
132
        }
133
134 4
        if ($this->isStringNotClassName()) {
135 1
            return sprintf(
136 1
                'Class `%s` not found. It may be needed to install a package with this middleware.',
137 1
                $this->definition
138 1
            );
139
        }
140
141 3
        if (is_array($this->definition)) {
142
            try {
143 2
                DefinitionValidator::validateArrayDefinition($this->definition);
144 1
            } catch (InvalidConfigException $e) {
145 1
                return <<<SOLUTION
146 1
                You may have an error in array definition. Array definition validation result:
147
148
                ```
149 1
                {$e->getMessage()}
150
                ```
151 1
                SOLUTION;
152
            }
153
154
            /** @psalm-suppress MixedArgument In valid array definition element "class" always is string */
155 1
            return sprintf(
156 1
                'Array definition is valid, class `%s` exists, but does not implement `%s`.',
157 1
                $this->definition['class'],
158 1
                MiddlewareInterface::class
159 1
            );
160
        }
161
162 1
        return null;
163
    }
164
165
    /**
166
     * @psalm-assert-if-true string $this->definition
167
     */
168 4
    private function isStringNotClassName(): bool
169
    {
170 4
        return is_string($this->definition)
171 4
            && !class_exists($this->definition);
172
    }
173
174
    /**
175
     * @psalm-assert-if-true class-string $this->definition
176
     */
177 5
    private function isNotMiddlewareClassName(): bool
178
    {
179 5
        return is_string($this->definition)
180 5
            && class_exists($this->definition);
181
    }
182
183
    /**
184
     * @psalm-assert-if-true array{0:class-string,1:string} $this->definition
185
     */
186 6
    private function isControllerWithNonExistAction(): bool
187
    {
188 6
        return is_array($this->definition)
189 6
            && array_keys($this->definition) === [0, 1]
190 6
            && is_string($this->definition[0])
191 6
            && class_exists($this->definition[0]);
192
    }
193
194 21
    private function convertDefinitionToString(mixed $middlewareDefinition): string
195
    {
196 21
        if (is_object($middlewareDefinition)) {
197 2
            return 'an instance of "' . $middlewareDefinition::class . '"';
198
        }
199
200 19
        if (is_string($middlewareDefinition)) {
201 5
            return '"' . $middlewareDefinition . '"';
202
        }
203
204 14
        if (is_array($middlewareDefinition)) {
205 11
            $items = [];
206
            /** @var mixed $value */
207 11
            foreach (array_slice($middlewareDefinition, 0, 2) as $key => $value) {
208 11
                $items[] = (is_string($key) ? '"' . $key . '" => ' : '') . $this->convertToString($value);
209
            }
210 11
            return '[' . implode(', ', $items) . (count($middlewareDefinition) > 2 ? ', ...' : '') . ']';
211
        }
212
213 3
        return $this->convertToString($middlewareDefinition);
214
    }
215
216 14
    private function convertToString(mixed $value): string
217
    {
218 14
        if (is_string($value)) {
219 8
            return '"' . $value . '"';
220
        }
221
222 6
        if (is_int($value) || is_float($value)) {
223 2
            return (string) $value;
224
        }
225
226 4
        if (is_bool($value)) {
227 2
            return $value ? 'true' : 'false';
228
        }
229
230 2
        if ($value === null) {
231 1
            return 'null';
232
        }
233
234 2
        if (is_object($value)) {
235 1
            return $value::class;
236
        }
237
238 1
        return gettype($value);
239
    }
240
}
241