Passed
Pull Request — master (#53)
by Alexander
04:32 queued 02:04
created

generateSuggestion()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 53
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 6

Importance

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