Completed
Push — master ( 79dcd5...ac1fa0 )
by Neomerx
02:10
created

MakeCommand   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 794
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 44
lcom 1
cbo 6
dl 0
loc 794
ccs 282
cts 282
cp 1
rs 8
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A getName() 0 4 1
A getDescription() 0 4 1
A getHelp() 0 4 1
B getArguments() 0 25 1
A getOptions() 0 4 1
A execute() 0 4 1
A composeMigration() 0 21 1
A composeSeed() 0 21 1
B composeModel() 0 24 1
A composeSchema() 0 21 1
A composeApi() 0 23 1
B composeAuthorization() 0 25 1
C run() 0 87 7
B composeValidationRules() 0 24 1
A composeJsonValidationOnCreateRules() 0 21 1
A composeJsonValidationOnUpdateRules() 0 21 1
A composeQueryValidationOnReadRules() 0 22 1
A composeJsonController() 0 22 1
A composeJsonRoute() 0 21 1
A composeWebValidationOnCreateRules() 0 20 1
A composeWebValidationOnUpdateRules() 0 20 1
B composeWebController() 0 24 1
A composeWebRoute() 0 21 1
D createTemplates() 0 36 9
A composeTemplateContent() 0 13 2
A isValidShortClassName() 0 4 2
A getTemplatePath() 0 4 1
A filterOutFolderMask() 0 9 1

How to fix   Complexity   

Complex Class

Complex classes like MakeCommand often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MakeCommand, and based on these observations, apply Extract Interface, too.

1
<?php namespace Limoncello\Application\Commands;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Limoncello\Application\Exceptions\InvalidArgumentException;
20
use Limoncello\Contracts\Commands\CommandInterface;
21
use Limoncello\Contracts\Commands\IoInterface;
22
use Limoncello\Contracts\FileSystem\FileSystemInterface;
23
use Limoncello\Contracts\Settings\Packages\AuthorizationSettingsInterface;
24
use Limoncello\Contracts\Settings\Packages\DataSettingsInterface;
25
use Limoncello\Contracts\Settings\Packages\FluteSettingsInterface;
26
use Limoncello\Contracts\Settings\SettingsProviderInterface;
27
use Psr\Container\ContainerExceptionInterface;
28
use Psr\Container\ContainerInterface;
29
use Psr\Container\NotFoundExceptionInterface;
30
31
/**
32
 * @package Limoncello\Application
33
 *
34
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
35
 */
36
class MakeCommand implements CommandInterface
37
{
38
    /**
39
     * Command name.
40
     */
41
    const NAME = 'l:make';
42
43
    /** Argument name */
44
    const ARG_ITEM = 'item';
45
46
    /** Argument name */
47
    const ARG_SINGULAR = 'singular';
48
49
    /** Argument name */
50
    const ARG_PLURAL = 'plural';
51
52
    /** Command action */
53
    const ITEM_DATA_RESOURCE = 'data-resource';
54
55
    /** Command action */
56
    const ITEM_WEB_RESOURCE = 'web-resource';
57
58
    /** Command action */
59
    const ITEM_JSON_API_RESOURCE = 'json-resource';
60
61
    /** Command action */
62
    const ITEM_FULL_RESOURCE = 'resource';
63
64
    /**
65
     * Taken from http://php.net/manual/en/language.oop5.basic.php
66
     */
67
    protected const VALID_CLASS_NAME_REGEX = '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/';
68
69
    /**
70
     * @inheritdoc
71
     */
72 1
    public static function getName(): string
73
    {
74 1
        return static::NAME;
75
    }
76
77
    /**
78
     * @inheritdoc
79
     */
80 1
    public static function getDescription(): string
81
    {
82 1
        return 'Creates necessary classes for models, migrations and data seeds.';
83
    }
84
85
    /**
86
     * @inheritdoc
87
     */
88 1
    public static function getHelp(): string
89
    {
90 1
        return 'This command creates necessary classes for models, migrations and data seeds.';
91
    }
92
93
    /**
94
     * @inheritdoc
95
     */
96 1
    public static function getArguments(): array
97
    {
98 1
        $data     = static::ITEM_DATA_RESOURCE;
99 1
        $web      = static::ITEM_WEB_RESOURCE;
100 1
        $json     = static::ITEM_JSON_API_RESOURCE;
101 1
        $resource = static::ITEM_FULL_RESOURCE;
102
103
        return [
104
            [
105 1
                static::ARGUMENT_NAME        => static::ARG_ITEM,
106 1
                static::ARGUMENT_DESCRIPTION => "Action such as `$data`, `$web`, `$json` or `$resource`.",
107 1
                static::ARGUMENT_MODE        => static::ARGUMENT_MODE__REQUIRED,
108
            ],
109
            [
110 1
                static::ARGUMENT_NAME        => static::ARG_SINGULAR,
111 1
                static::ARGUMENT_DESCRIPTION => 'Singular name in camel case (e.g. `Post`).',
112 1
                static::ARGUMENT_MODE        => static::ARGUMENT_MODE__REQUIRED,
113
            ],
114
            [
115 1
                static::ARGUMENT_NAME        => static::ARG_PLURAL,
116 1
                static::ARGUMENT_DESCRIPTION => 'Plural name in camel case (e.g. `Posts`).',
117 1
                static::ARGUMENT_MODE        => static::ARGUMENT_MODE__REQUIRED,
118
            ],
119
        ];
120
    }
121
122
    /**
123
     * @inheritdoc
124
     */
125 1
    public static function getOptions(): array
126
    {
127 1
        return [];
128
    }
129
130
    /**
131
     * @inheritdoc
132
     */
133 10
    public static function execute(ContainerInterface $container, IoInterface $inOut): void
134
    {
135 10
        (new static())->run($container, $inOut);
136
    }
137
138
    /**
139
     * @param ContainerInterface $container
140
     * @param IoInterface        $inOut
141
     *
142
     * @return void
143
     *
144
     * @throws ContainerExceptionInterface
145
     * @throws NotFoundExceptionInterface
146
     */
147 10
    protected function run(ContainerInterface $container, IoInterface $inOut): void
148
    {
149 10
        $item     = $inOut->getArguments()[static::ARG_ITEM];
150 10
        $singular = $inOut->getArguments()[static::ARG_SINGULAR];
151 10
        $plural   = $inOut->getArguments()[static::ARG_PLURAL];
152
153 10
        if ($this->isValidShortClassName($singular) === false) {
154 1
            throw new InvalidArgumentException("`$singular` is not a valid class name.");
155
        }
156 9
        if ($this->isValidShortClassName($plural) === false) {
157 1
            throw new InvalidArgumentException("`$plural` is not a valid class name.");
158
        }
159
160
        /** @var FileSystemInterface $fileSystem */
161 8
        $fileSystem = $container->get(FileSystemInterface::class);
162
        /** @var SettingsProviderInterface $settingsProvider */
163 8
        $settingsProvider = $container->get(SettingsProviderInterface::class);
164
165
        $dataTemplates = function () use ($settingsProvider, $fileSystem, $singular, $plural) : array {
166
            return [
167 7
                $this->composeMigration($settingsProvider, $fileSystem, $singular, $plural),
168 7
                $this->composeSeed($settingsProvider, $fileSystem, $singular, $plural),
169 7
                $this->composeModel($settingsProvider, $fileSystem, $singular, $plural),
170
            ];
171 8
        };
172
173
        $basicTemplates = function () use ($settingsProvider, $fileSystem, $singular, $plural) : array {
174
            return [
175 3
                $this->composeSchema($settingsProvider, $fileSystem, $singular, $plural),
176 3
                $this->composeAuthorization($settingsProvider, $fileSystem, $singular, $plural),
177 3
                $this->composeApi($settingsProvider, $fileSystem, $singular, $plural),
178 3
                $this->composeValidationRules($settingsProvider, $fileSystem, $singular, $plural),
179 3
                $this->composeQueryValidationOnReadRules($settingsProvider, $fileSystem, $singular, $plural),
180
            ];
181 8
        };
182
183
        $webTemplates = function () use ($settingsProvider, $fileSystem, $singular, $plural) : array {
184
            return [
185 2
                $this->composeWebValidationOnCreateRules($settingsProvider, $fileSystem, $singular),
186 2
                $this->composeWebValidationOnUpdateRules($settingsProvider, $fileSystem, $singular),
187 2
                $this->composeWebController($settingsProvider, $fileSystem, $singular, $plural),
188 2
                $this->composeWebRoute($settingsProvider, $fileSystem, $singular, $plural),
189
            ];
190 8
        };
191
192
        $jsonTemplates = function () use ($settingsProvider, $fileSystem, $singular, $plural) : array {
193
            return [
194 2
                $this->composeJsonValidationOnCreateRules($settingsProvider, $fileSystem, $singular),
195 2
                $this->composeJsonValidationOnUpdateRules($settingsProvider, $fileSystem, $singular),
196 2
                $this->composeJsonController($settingsProvider, $fileSystem, $singular, $plural),
197 2
                $this->composeJsonRoute($settingsProvider, $fileSystem, $singular, $plural),
198
            ];
199 8
        };
200
201
        switch ($item) {
202 8
            case static::ITEM_DATA_RESOURCE:
203 4
                $this->createTemplates($fileSystem, array_merge(
204 4
                    $dataTemplates()
205
                ));
206 1
                break;
207 4
            case static::ITEM_WEB_RESOURCE:
208 1
                $this->createTemplates($fileSystem, array_merge(
209 1
                    $dataTemplates(),
210 1
                    $basicTemplates(),
211 1
                    $webTemplates()
212
                ));
213 1
                break;
214 3
            case static::ITEM_JSON_API_RESOURCE:
215 1
                $this->createTemplates($fileSystem, array_merge(
216 1
                    $dataTemplates(),
217 1
                    $basicTemplates(),
218 1
                    $jsonTemplates()
219
                ));
220 1
                break;
221 2
            case static::ITEM_FULL_RESOURCE:
222 1
                $this->createTemplates($fileSystem, array_merge(
223 1
                    $dataTemplates(),
224 1
                    $basicTemplates(),
225 1
                    $webTemplates(),
226 1
                    $jsonTemplates()
227
                ));
228 1
                break;
229
            default:
230 1
                $inOut->writeError("Unsupported item type `$item`." . PHP_EOL);
231 1
                break;
232
        }
233
    }
234
235
    /**
236
     * @param SettingsProviderInterface $settingsProvider
237
     * @param FileSystemInterface       $fileSystem
238
     * @param string                    $singular
239
     * @param string                    $plural
240
     *
241
     * @return TemplateOutput
242
     */
243 7
    private function composeMigration(
244
        SettingsProviderInterface $settingsProvider,
245
        FileSystemInterface $fileSystem,
246
        string $singular,
247
        string $plural
248
    ): TemplateOutput {
249 7
        $folder = $settingsProvider->get(DataSettingsInterface::class)[DataSettingsInterface::KEY_MIGRATIONS_FOLDER];
250
251 7
        $outputRootFolder = $this->filterOutFolderMask($folder);
252 7
        $outputFileName   = $plural . 'Migration.php';
253 7
        $outputContent    = $this->composeTemplateContent(
254 7
            $fileSystem,
255 7
            $this->getTemplatePath('Migration.txt'),
256
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ...LURAL_CC%}' => $plural) is of type array<string,string,{"{%...PLURAL_CC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
257 7
                '{%SINGULAR_CC%}' => $singular,
258 7
                '{%PLURAL_CC%}'   => $plural,
259
            ]
260
        );
261
262 7
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent);
263
    }
264
265
    /**
266
     * @param SettingsProviderInterface $settingsProvider
267
     * @param FileSystemInterface       $fileSystem
268
     * @param string                    $singular
269
     * @param string                    $plural
270
     *
271
     * @return TemplateOutput
272
     */
273 7
    private function composeSeed(
274
        SettingsProviderInterface $settingsProvider,
275
        FileSystemInterface $fileSystem,
276
        string $singular,
277
        string $plural
278
    ): TemplateOutput {
279 7
        $folder = $settingsProvider->get(DataSettingsInterface::class)[DataSettingsInterface::KEY_SEEDS_FOLDER];
280
281 7
        $outputRootFolder = $this->filterOutFolderMask($folder);
282 7
        $outputFileName   = $plural . 'Seed.php';
283 7
        $outputContent    = $this->composeTemplateContent(
284 7
            $fileSystem,
285 7
            $this->getTemplatePath('Seed.txt'),
286
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ...LURAL_CC%}' => $plural) is of type array<string,string,{"{%...PLURAL_CC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
287 7
                '{%SINGULAR_CC%}' => $singular,
288 7
                '{%PLURAL_CC%}'   => $plural,
289
            ]
290
        );
291
292 7
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent);
293
    }
294
295
    /**
296
     * @param SettingsProviderInterface $settingsProvider
297
     * @param FileSystemInterface       $fileSystem
298
     * @param string                    $singular
299
     * @param string                    $plural
300
     *
301
     * @return TemplateOutput
302
     */
303 7
    private function composeModel(
304
        SettingsProviderInterface $settingsProvider,
305
        FileSystemInterface $fileSystem,
306
        string $singular,
307
        string $plural
308
    ): TemplateOutput {
309 7
        $folder = $settingsProvider->get(DataSettingsInterface::class)[DataSettingsInterface::KEY_MODELS_FOLDER];
310
311 7
        $outputRootFolder = $this->filterOutFolderMask($folder);
312 7
        $outputFileName   = $singular . '.php';
313 7
        $outputContent    = $this->composeTemplateContent(
314 7
            $fileSystem,
315 7
            $this->getTemplatePath('Model.txt'),
316
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ...=> strtoupper($plural)) is of type array<string,string,{"{%...PLURAL_UC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
317 7
                '{%SINGULAR_CC%}' => $singular,
318 7
                '{%SINGULAR_LC%}' => strtolower($singular),
319 7
                '{%SINGULAR_UC%}' => strtoupper($singular),
320 7
                '{%PLURAL_LC%}'   => strtolower($plural),
321 7
                '{%PLURAL_UC%}'   => strtoupper($plural),
322
            ]
323
        );
324
325 7
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent);
326
    }
327
328
    /**
329
     * @param SettingsProviderInterface $settingsProvider
330
     * @param FileSystemInterface       $fileSystem
331
     * @param string                    $singular
332
     * @param string                    $plural
333
     *
334
     * @return TemplateOutput
335
     */
336 3
    private function composeSchema(
337
        SettingsProviderInterface $settingsProvider,
338
        FileSystemInterface $fileSystem,
339
        string $singular,
340
        string $plural
341
    ): TemplateOutput {
342 3
        $folder = $settingsProvider->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_SCHEMAS_FOLDER];
343
344 3
        $outputRootFolder = $this->filterOutFolderMask($folder);
345 3
        $outputFileName   = $singular . 'Schema.php';
346 3
        $outputContent    = $this->composeTemplateContent(
347 3
            $fileSystem,
348 3
            $this->getTemplatePath('Schema.txt'),
349
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ...=> strtolower($plural)) is of type array<string,string,{"{%...PLURAL_LC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
350 3
                '{%SINGULAR_CC%}' => $singular,
351 3
                '{%PLURAL_LC%}'   => strtolower($plural),
352
            ]
353
        );
354
355 3
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent);
356
    }
357
358
    /**
359
     * @param SettingsProviderInterface $settingsProvider
360
     * @param FileSystemInterface       $fileSystem
361
     * @param string                    $singular
362
     * @param string                    $plural
363
     *
364
     * @return TemplateOutput
365
     */
366 3
    private function composeApi(
367
        SettingsProviderInterface $settingsProvider,
368
        FileSystemInterface $fileSystem,
369
        string $singular,
370
        string $plural
371
    ): TemplateOutput {
372 3
        $folder = $settingsProvider->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_API_FOLDER];
373
374 3
        $outputRootFolder = $this->filterOutFolderMask($folder);
375 3
        $outputFileName   = $plural . 'Api.php';
376 3
        $outputContent    = $this->composeTemplateContent(
377 3
            $fileSystem,
378 3
            $this->getTemplatePath('Api.txt'),
379
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ...=> strtoupper($plural)) is of type array<string,string,{"{%...PLURAL_UC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
380 3
                '{%SINGULAR_CC%}' => $singular,
381 3
                '{%PLURAL_CC%}'   => $plural,
382 3
                '{%SINGULAR_UC%}' => strtoupper($singular),
383 3
                '{%PLURAL_UC%}'   => strtoupper($plural),
384
            ]
385
        );
386
387 3
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent);
388
    }
389
390
    /**
391
     * @param SettingsProviderInterface $settingsProvider
392
     * @param FileSystemInterface       $fileSystem
393
     * @param string                    $singular
394
     * @param string                    $plural
395
     *
396
     * @return TemplateOutput
397
     */
398 3
    private function composeAuthorization(
399
        SettingsProviderInterface $settingsProvider,
400
        FileSystemInterface $fileSystem,
401
        string $singular,
402
        string $plural
403
    ): TemplateOutput {
404
        $folder = $settingsProvider
405 3
                      ->get(AuthorizationSettingsInterface::class)[AuthorizationSettingsInterface::KEY_POLICIES_FOLDER];
406
407 3
        $outputRootFolder = $this->filterOutFolderMask($folder);
408 3
        $outputFileName   = $singular . 'Rules.php';
409 3
        $outputContent    = $this->composeTemplateContent(
410 3
            $fileSystem,
411 3
            $this->getTemplatePath('ApiAuthorization.txt'),
412
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ... strtoupper($singular)) is of type array<string,string,{"{%...NGULAR_UC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
413 3
                '{%SINGULAR_CC%}' => $singular,
414 3
                '{%PLURAL_CC%}'   => $plural,
415 3
                '{%SINGULAR_LC%}' => strtolower($singular),
416 3
                '{%PLURAL_UC%}'   => strtoupper($plural),
417 3
                '{%SINGULAR_UC%}' => strtoupper($singular),
418
            ]
419
        );
420
421 3
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent);
422
    }
423
424
    /**
425
     * @param SettingsProviderInterface $settingsProvider
426
     * @param FileSystemInterface       $fileSystem
427
     * @param string                    $singular
428
     * @param string                    $plural
429
     *
430
     * @return TemplateOutput
431
     */
432 3
    private function composeValidationRules(
433
        SettingsProviderInterface $settingsProvider,
434
        FileSystemInterface $fileSystem,
435
        string $singular,
436
        string $plural
437
    ): TemplateOutput {
438
        $folder = $settingsProvider
439 3
                      ->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_JSON_VALIDATION_RULES_FOLDER];
440
441 3
        $outputRootFolder = $this->filterOutFolderMask($folder);
442 3
        $outputFileName   = $singular . 'Rules.php';
443 3
        $outputContent    = $this->composeTemplateContent(
444 3
            $fileSystem,
445 3
            $this->getTemplatePath('ValidationRules.txt'),
446
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ...=> strtolower($plural)) is of type array<string,string,{"{%...PLURAL_LC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
447 3
                '{%SINGULAR_CC%}' => $singular,
448 3
                '{%PLURAL_CC%}'   => $plural,
449 3
                '{%SINGULAR_LC%}' => strtolower($singular),
450 3
                '{%PLURAL_LC%}'   => strtolower($plural),
451
            ]
452
        );
453
454 3
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent, $singular);
455
    }
456
457
    /**
458
     * @param SettingsProviderInterface $settingsProvider
459
     * @param FileSystemInterface       $fileSystem
460
     * @param string                    $singular
461
     *
462
     * @return TemplateOutput
463
     */
464 2
    private function composeJsonValidationOnCreateRules(
465
        SettingsProviderInterface $settingsProvider,
466
        FileSystemInterface $fileSystem,
467
        string $singular
468
    ): TemplateOutput {
469
        $folder = $settingsProvider
470 2
                      ->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_JSON_VALIDATORS_FOLDER];
471
472 2
        $outputRootFolder = $this->filterOutFolderMask($folder);
473 2
        $outputFileName   = $singular . 'CreateJson.php';
474 2
        $outputContent    = $this->composeTemplateContent(
475 2
            $fileSystem,
476 2
            $this->getTemplatePath('JsonRulesOnCreate.txt'),
477
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ... strtolower($singular)) is of type array<string,string,{"{%...NGULAR_LC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
478 2
                '{%SINGULAR_CC%}' => $singular,
479 2
                '{%SINGULAR_LC%}' => strtolower($singular),
480
            ]
481
        );
482
483 2
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent, $singular);
484
    }
485
486
    /**
487
     * @param SettingsProviderInterface $settingsProvider
488
     * @param FileSystemInterface       $fileSystem
489
     * @param string                    $singular
490
     *
491
     * @return TemplateOutput
492
     */
493 2
    private function composeJsonValidationOnUpdateRules(
494
        SettingsProviderInterface $settingsProvider,
495
        FileSystemInterface $fileSystem,
496
        string $singular
497
    ): TemplateOutput {
498
        $folder = $settingsProvider
499 2
                      ->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_JSON_VALIDATORS_FOLDER];
500
501 2
        $outputRootFolder = $this->filterOutFolderMask($folder);
502 2
        $outputFileName   = $singular . 'UpdateJson.php';
503 2
        $outputContent    = $this->composeTemplateContent(
504 2
            $fileSystem,
505 2
            $this->getTemplatePath('JsonRulesOnUpdate.txt'),
506
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ... strtolower($singular)) is of type array<string,string,{"{%...NGULAR_LC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
507 2
                '{%SINGULAR_CC%}' => $singular,
508 2
                '{%SINGULAR_LC%}' => strtolower($singular),
509
            ]
510
        );
511
512 2
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent, $singular);
513
    }
514
515
    /**
516
     * @param SettingsProviderInterface $settingsProvider
517
     * @param FileSystemInterface       $fileSystem
518
     * @param string                    $singular
519
     * @param string                    $plural
520
     *
521
     * @return TemplateOutput
522
     *
523
     */
524 3
    private function composeQueryValidationOnReadRules(
525
        SettingsProviderInterface $settingsProvider,
526
        FileSystemInterface $fileSystem,
527
        string $singular,
528
        string $plural
529
    ): TemplateOutput {
530
        $folder = $settingsProvider
531 3
                      ->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_JSON_VALIDATORS_FOLDER];
532
533 3
        $outputRootFolder = $this->filterOutFolderMask($folder);
534 3
        $outputFileName   = $plural . 'ReadQuery.php';
535 3
        $outputContent    = $this->composeTemplateContent(
536 3
            $fileSystem,
537 3
            $this->getTemplatePath('QueryRulesOnRead.txt'),
538
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ...LURAL_CC%}' => $plural) is of type array<string,string,{"{%...PLURAL_CC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
539 3
                '{%SINGULAR_CC%}' => $singular,
540 3
                '{%PLURAL_CC%}'   => $plural,
541
            ]
542
        );
543
544 3
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent, $singular);
545
    }
546
547
    /**
548
     * @param SettingsProviderInterface $settingsProvider
549
     * @param FileSystemInterface       $fileSystem
550
     * @param string                    $singular
551
     * @param string                    $plural
552
     *
553
     * @return TemplateOutput
554
     */
555 2
    private function composeJsonController(
556
        SettingsProviderInterface $settingsProvider,
557
        FileSystemInterface $fileSystem,
558
        string $singular,
559
        string $plural
560
    ): TemplateOutput {
561
        $folder = $settingsProvider
562 2
                      ->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_JSON_CONTROLLERS_FOLDER];
563
564 2
        $outputRootFolder = $this->filterOutFolderMask($folder);
565 2
        $outputFileName   = $plural . 'Controller.php';
566 2
        $outputContent    = $this->composeTemplateContent(
567 2
            $fileSystem,
568 2
            $this->getTemplatePath('JsonController.txt'),
569
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ...LURAL_CC%}' => $plural) is of type array<string,string,{"{%...PLURAL_CC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
570 2
                '{%SINGULAR_CC%}' => $singular,
571 2
                '{%PLURAL_CC%}'   => $plural,
572
            ]
573
        );
574
575 2
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent);
576
    }
577
578
    /**
579
     * @param SettingsProviderInterface $settingsProvider
580
     * @param FileSystemInterface       $fileSystem
581
     * @param string                    $singular
582
     * @param string                    $plural
583
     *
584
     * @return TemplateOutput
585
     */
586 2
    private function composeJsonRoute(
587
        SettingsProviderInterface $settingsProvider,
588
        FileSystemInterface $fileSystem,
589
        string $singular,
590
        string $plural
591
    ): TemplateOutput {
592 2
        $folder = $settingsProvider->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_ROUTES_FOLDER];
593
594 2
        $outputRootFolder = $this->filterOutFolderMask($folder);
595 2
        $outputFileName   = $singular . 'ApiRoutes.php';
596 2
        $outputContent    = $this->composeTemplateContent(
597 2
            $fileSystem,
598 2
            $this->getTemplatePath('JsonRoutes.txt'),
599
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ...LURAL_CC%}' => $plural) is of type array<string,string,{"{%...PLURAL_CC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
600 2
                '{%SINGULAR_CC%}' => $singular,
601 2
                '{%PLURAL_CC%}'   => $plural,
602
            ]
603
        );
604
605 2
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent);
606
    }
607
608
    /**
609
     * @param SettingsProviderInterface $settingsProvider
610
     * @param FileSystemInterface       $fileSystem
611
     * @param string                    $singular
612
     *
613
     * @return TemplateOutput
614
     */
615 2
    private function composeWebValidationOnCreateRules(
616
        SettingsProviderInterface $settingsProvider,
617
        FileSystemInterface $fileSystem,
618
        string $singular
619
    ): TemplateOutput {
620
        $folder = $settingsProvider
621 2
                      ->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_JSON_VALIDATORS_FOLDER];
622
623 2
        $outputRootFolder = $this->filterOutFolderMask($folder);
624 2
        $outputFileName   = $singular . 'CreateForm.php';
625 2
        $outputContent    = $this->composeTemplateContent(
626 2
            $fileSystem,
627 2
            $this->getTemplatePath('WebRulesOnCreate.txt'),
628
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' => $singular) is of type array<string,string,{"{%SINGULAR_CC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
629 2
                '{%SINGULAR_CC%}' => $singular,
630
            ]
631
        );
632
633 2
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent, $singular);
634
    }
635
636
    /**
637
     * @param SettingsProviderInterface $settingsProvider
638
     * @param FileSystemInterface       $fileSystem
639
     * @param string                    $singular
640
     *
641
     * @return TemplateOutput
642
     */
643 2
    private function composeWebValidationOnUpdateRules(
644
        SettingsProviderInterface $settingsProvider,
645
        FileSystemInterface $fileSystem,
646
        string $singular
647
    ): TemplateOutput {
648
        $folder = $settingsProvider
649 2
                      ->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_JSON_VALIDATORS_FOLDER];
650
651 2
        $outputRootFolder = $this->filterOutFolderMask($folder);
652 2
        $outputFileName   = $singular . 'UpdateForm.php';
653 2
        $outputContent    = $this->composeTemplateContent(
654 2
            $fileSystem,
655 2
            $this->getTemplatePath('WebRulesOnUpdate.txt'),
656
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' => $singular) is of type array<string,string,{"{%SINGULAR_CC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
657 2
                '{%SINGULAR_CC%}' => $singular,
658
            ]
659
        );
660
661 2
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent, $singular);
662
    }
663
664
    /**
665
     * @param SettingsProviderInterface $settingsProvider
666
     * @param FileSystemInterface       $fileSystem
667
     * @param string                    $singular
668
     * @param string                    $plural
669
     *
670
     * @return TemplateOutput
671
     */
672 2
    private function composeWebController(
673
        SettingsProviderInterface $settingsProvider,
674
        FileSystemInterface $fileSystem,
675
        string $singular,
676
        string $plural
677
    ): TemplateOutput {
678
        $folder = $settingsProvider
679 2
                      ->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_WEB_CONTROLLERS_FOLDER];
680
681 2
        $outputRootFolder = $this->filterOutFolderMask($folder);
682 2
        $outputFileName   = $plural . 'Controller.php';
683 2
        $outputContent    = $this->composeTemplateContent(
684 2
            $fileSystem,
685 2
            $this->getTemplatePath('WebController.txt'),
686
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ...=> strtoupper($plural)) is of type array<string,string,{"{%...PLURAL_UC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
687 2
                '{%SINGULAR_CC%}' => $singular,
688 2
                '{%PLURAL_CC%}'   => $plural,
689 2
                '{%PLURAL_LC%}'   => strtolower($plural),
690 2
                '{%PLURAL_UC%}'   => strtoupper($plural),
691
            ]
692
        );
693
694 2
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent);
695
    }
696
697
    /**
698
     * @param SettingsProviderInterface $settingsProvider
699
     * @param FileSystemInterface       $fileSystem
700
     * @param string                    $singular
701
     * @param string                    $plural
702
     *
703
     * @return TemplateOutput
704
     */
705 2
    private function composeWebRoute(
706
        SettingsProviderInterface $settingsProvider,
707
        FileSystemInterface $fileSystem,
708
        string $singular,
709
        string $plural
710
    ): TemplateOutput {
711 2
        $folder = $settingsProvider->get(FluteSettingsInterface::class)[FluteSettingsInterface::KEY_ROUTES_FOLDER];
712
713 2
        $outputRootFolder = $this->filterOutFolderMask($folder);
714 2
        $outputFileName   = $singular . 'WebRoutes.php';
715 2
        $outputContent    = $this->composeTemplateContent(
716 2
            $fileSystem,
717 2
            $this->getTemplatePath('WebRoutes.txt'),
718
            [
0 ignored issues
show
Documentation introduced by
array('{%SINGULAR_CC%}' ...LURAL_CC%}' => $plural) is of type array<string,string,{"{%...PLURAL_CC%}":"string"}>, but the function expects a object<Limoncello\Application\Commands\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
719 2
                '{%SINGULAR_CC%}' => $singular,
720 2
                '{%PLURAL_CC%}'   => $plural,
721
            ]
722
        );
723
724 2
        return new TemplateOutput($outputRootFolder, $outputFileName, $outputContent);
725
    }
726
727
    /**
728
     * @param FileSystemInterface $fileSystem
729
     * @param TemplateOutput[]    $templateOutputs
730
     *
731
     * @return void
732
     *
733
     * @SuppressWarnings(PHPMD.ElseExpression)
734
     */
735 7
    private function createTemplates(FileSystemInterface $fileSystem, array $templateOutputs): void
736
    {
737
        // before making any changes in the filesystem we have to check there is a good chance we can make it
738 7
        foreach ($templateOutputs as $templateOutput) {
739 7
            if ($fileSystem->exists($templateOutput->getOutputRootFolder()) === false) {
740 1
                $rootFolder = $templateOutput->getOutputRootFolder();
741 1
                throw new InvalidArgumentException("Folder `$rootFolder` do not exist.");
742
            }
743
744 7
            if ($fileSystem->exists($templateOutput->getOutputPath()) === true) {
745 1
                $filePath = $templateOutput->getOutputPath();
746 1
                throw new InvalidArgumentException("File `$filePath` already exists.");
747
            }
748
749 7
            $outFolder = $templateOutput->getOutputFolder();
750 7
            if ($fileSystem->exists($outFolder) === true) {
751
                // the folder already exist so we have to check it is writable
752 7
                if ($fileSystem->isWritable($outFolder) === false) {
753 7
                    throw new InvalidArgumentException("Folder `$outFolder` is not writable.");
754
                }
755
            } else {
756
                // it should be a root folder with not yet existing sub-folder so root should be writable
757 3
                $rootFolder = $templateOutput->getOutputRootFolder();
758 3
                if ($fileSystem->isWritable($rootFolder) === false) {
759 7
                    throw new InvalidArgumentException("Folder `$rootFolder` is not writable.");
760
                }
761
            }
762
        }
763
764 4
        foreach ($templateOutputs as $templateOutput) {
765 4
            if ($fileSystem->exists($templateOutput->getOutputFolder()) === false) {
766 3
                $fileSystem->createFolder($templateOutput->getOutputFolder());
767
            }
768 4
            $fileSystem->write($templateOutput->getOutputPath(), $templateOutput->getOutputContent());
769
        }
770
    }
771
772
    /**
773
     * @param FileSystemInterface $fileSystem
774
     * @param string              $templatePath
775
     * @param iterable            $templateParams
776
     *
777
     * @return string
778
     */
779 7
    private function composeTemplateContent(
780
        FileSystemInterface $fileSystem,
781
        string $templatePath,
782
        iterable $templateParams
783
    ): string {
784 7
        $templateContent = $fileSystem->read($templatePath);
785
786 7
        foreach ($templateParams as $key => $value) {
787 7
            $templateContent = str_replace($key, $value, $templateContent);
788
        }
789
790 7
        return $templateContent;
791
    }
792
793
    /**
794
     * @param string $name
795
     *
796
     * @return bool
797
     */
798 10
    private function isValidShortClassName(string $name): bool
799
    {
800 10
        return empty($name) === false && preg_match(static::VALID_CLASS_NAME_REGEX, $name) === 1;
801
    }
802
803
    /**
804
     * @param string $fileName
805
     *
806
     * @return string
807
     */
808 7
    private function getTemplatePath(string $fileName): string
809
    {
810 7
        return implode(DIRECTORY_SEPARATOR, [__DIR__, '..', '..', 'res', 'CodeTemplates', $fileName]);
811
    }
812
813
    /**
814
     * Folder paths might include masks such as `**`. This function tries to filter them out.
815
     *
816
     * @param string $folder
817
     *
818
     * @return string
819
     */
820 7
    private function filterOutFolderMask(string $folder): string
821
    {
822 7
        $mask = '**';
823
824 7
        $folder = str_replace($mask . DIRECTORY_SEPARATOR, '', $folder);
825 7
        $folder = str_replace($mask, '', $folder);
826
827 7
        return $folder;
828
    }
829
}
830