Test Failed
Pull Request — master (#53)
by Alexander
05:41 queued 03:18
created

getSolution()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 65
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 65
ccs 21
cts 21
cp 1
rs 8.9381
eloc 57
cc 2
nc 2
nop 0
crap 2

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