Completed
Pull Request — master (#164)
by Paul
08:29
created

ModuleCreateCommand::askQuestions()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
c 8
b 0
f 0
dl 0
loc 21
rs 8.7624
cc 5
eloc 11
nc 12
nop 2
1
<?php
2
/**
3
 * This file is part of the PPI Framework.
4
 *
5
 * @copyright  Copyright (c) 2011-2016 Paul Dragoonis <[email protected]>
6
 * @license    http://opensource.org/licenses/mit-license.php MIT
7
 *
8
 * @link       http://www.ppi.io
9
 */
10
11
namespace PPI\Framework\Console\Command;
12
13
use Symfony\Component\Console\Input\InputArgument;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Symfony\Component\Console\Output\OutputInterface;
17
use Symfony\Component\Console\Question\ChoiceQuestion;
18
use Symfony\Component\Console\Question\ConfirmationQuestion;
19
20
/**
21
 * Module Command.
22
 *
23
 * @author      Paul Dragoonis <[email protected]>
24
 * @author      Vítor Brandão <[email protected]>
25
 */
26
class ModuleCreateCommand extends AbstractCommand
27
{
28
    const TPL_ENGINE_LATTE = 'latte';
29
    const TPL_ENGINE_PLATES = 'plates';
30
    const TPL_ENGINE_PHP = 'php';
31
    const TPL_ENGINE_TWIG = 'twig';
32
    const TPL_ENGINE_SMARTY = 'smarty';
33
34
    const ROUTING_ENGINE_SYMFONY = 'symfony';
35
    const ROUTING_ENGINE_AURA = 'aura';
36
    const ROUTING_ENGINE_LARAVEL = 'laravel';
37
    const ROUTING_ENGINE_FASTROUTE = 'fastroute';
38
    const ROUTING_ENGINE_NULL = 'NullRouter';
39
40
    protected $skeletonModuleDir;
41
    protected $modulesDir;
42
    protected $moduleDir;
43
    protected $moduleName;
44
    protected $tplEngine;
45
    protected $routingEngine;
46
    protected $configEnabledTemplatingEngines = [];
47
48
    /**
49
     * @var array
50
     */
51
    protected $coreDirs = [
52
        'src',
53
        'src/Controller',
54
        'tests',
55
        'resources',
56
        'resources/config'
57
    ];
58
59
    /**
60
     * @var array
61
     */
62
    protected $coreFiles = [
63
        'Module.php',
64
        'resources/config/config.php',
65
    ];
66
67
    protected $tplEngineCoreFiles = [
68
        'resources/views',
69
        'resources/views/index'
70
    ];
71
72
    protected $routingEngineCoreFiles = [
73
        'resources/routes'
74
    ];
75
76
    /**
77
     * @var array
78
     */
79
    protected $tplEngineFilesMap = [
80
        self::TPL_ENGINE_LATTE => [
81
            'resources/views/index/index.html.latte',
82
        ],
83
        self::TPL_ENGINE_PLATES => [
84
            'resources/views/index/index.html.plates',
85
        ],
86
        self::TPL_ENGINE_PHP => [
87
            'resources/views/index/index.html.php',
88
        ],
89
        self::TPL_ENGINE_TWIG => [
90
            'resources/views/index/base.html.twig',
91
            'resources/views/index/index.html.twig',
92
        ],
93
        self::TPL_ENGINE_SMARTY => [
94
            'resources/views/index/base.html.smarty',
95
            'resources/views/index/index.html.smarty',
96
        ],
97
    ];
98
99
    protected $routingEngineFilesMap = [
100
        self::ROUTING_ENGINE_SYMFONY => [
101
            'src/Controller/Index.php',
102
            'src/Controller/Shared.php',
103
            'resources/routes/symfony.yml'
104
        ],
105
        self::ROUTING_ENGINE_AURA => [
106
            'src/Controller/Index.php',
107
            'src/Controller/Shared.php',
108
            'resources/routes/aura.php',
109
        ],
110
        self::ROUTING_ENGINE_LARAVEL => [
111
            'src/Controller/Index.php',
112
            'src/Controller/Shared.php',
113
            'resources/routes/laravel.php',
114
        ],
115
        self::ROUTING_ENGINE_FASTROUTE => [
116
            'src/Controller/IndexInvoke.php',
117
            'src/Controller/Shared.php',
118
            'resources/routes/fastroute.php',
119
        ],
120
    ];
121
122
    protected $routingEngineTokenMap = [
123
        self::ROUTING_ENGINE_AURA => [
124
            '[ROUTING_LOAD_METHOD]' => 'loadAuraRoutes',
125
            '[ROUTING_DEF_FILE]' => 'aura.php',
126
            '[ROUTING_GETROUTES_RETVAL]' => '\Aura\Router\Router',
127
        ],
128
        self::ROUTING_ENGINE_LARAVEL => [
129
            '[ROUTING_LOAD_METHOD]' => 'loadLaravelRoutes',
130
            '[ROUTING_DEF_FILE]' => 'laravel.php',
131
            '[ROUTING_GETROUTES_RETVAL]' => '\Illuminate\Routing\Router',
132
        ],
133
        self::ROUTING_ENGINE_FASTROUTE => [
134
            '[ROUTING_LOAD_METHOD]' => 'loadFastRouteRoutes',
135
            '[ROUTING_DEF_FILE]' => 'fastroute.php',
136
            '[ROUTING_GETROUTES_RETVAL]' => '\PPI\FastRoute\Wrapper\FastRouteWrapper',
137
        ],
138
    ];
139
140
    /**
141
     * @param string $moduleDir
142
     */
143
    public function setSkeletonModuleDir($moduleDir)
144
    {
145
        $this->skeletonModuleDir = realpath($moduleDir);
146
    }
147
148
    /**
149
     * @param string $moduleDir
150
     *
151
     * @return void
152
     */
153
    public function setTargetModuleDir($moduleDir)
154
    {
155
        $this->modulesDir = realpath($moduleDir);
156
    }
157
158
    /**
159
     * @param array $tplEngines
160
     *
161
     * @return void
162
     */
163
    public function setEnabledTemplatingEngines(array $tplEngines)
164
    {
165
        $this->configEnabledTemplatingEngines = $tplEngines;
166
    }
167
168
    /**
169
     * @return void
170
     */
171
    protected function configure()
172
    {
173
        $this->setName('module:create')
174
            ->setDescription('Create a module')
175
            ->addArgument('name', InputArgument::REQUIRED, 'What is your module name?')
176
            ->addOption('dir', null, InputOption::VALUE_OPTIONAL, 'Specify the modules directory')
177
            ->addOption('tpl', null, InputOption::VALUE_OPTIONAL, 'Specify the templating engine')
178
            ->addOption('routing', null, InputOption::VALUE_OPTIONAL, 'Specify the routing engine');
179
    }
180
181
    /**
182
     * @param InputInterface $input
183
     * @param OutputInterface $output
184
     * @throws \Exception
185
     * @return void
186
     */
187
    protected function execute(InputInterface $input, OutputInterface $output)
188
    {
189
        $this->moduleName = $input->getArgument('name');
190
        $this->moduleDir = $this->modulesDir . DIRECTORY_SEPARATOR . $this->moduleName;
191
192
        // Acquire Module Information
193
        $this->askQuestions($input, $output);
194
        $this->createModuleStructure($this->moduleDir, $this->moduleName);
195
        $output->writeln("<info>Created module successfully</info>");
196
        $output->writeln("Name: <info>{$this->moduleName}</info>");
197
        $output->writeln(sprintf("Path: <info>%s</info>", $this->moduleDir));
198
199
        // Copy the core files
200
        $this->copyFiles($this->skeletonModuleDir, $this->moduleDir, $this->coreFiles);
201
202
        $tokenizedFiles = $this->getTokenizedCoreFiles();
203
        $tokens = [];
204
205
        $tokens['[MODULE_NAME]'] = $this->moduleName;
206
207
        if(null !== $this->tplEngine && $this->isValidTemplatingEngine($this->tplEngine)) {
208
            $this->processTemplatingFiles();
209
            $output->writeln(sprintf("Templating: <info>%s</info>", $this->tplEngine));
210
        }
211
212
        if($this->isValidRoutingEngine($this->routingEngine)) {
213
            $this->processRoutingFiles($tokenizedFiles, $tokens);
214
            $output->writeln(sprintf("Router: <info>%s</info>", $this->routingEngine));
215
        } else {
216
            $tokens['[ROUTING_TRAIT]'] = '';
217
        }
218
219
        // replace tokens from specified tokenizable files
220
        $this->replaceTokensInFiles($this->moduleDir, $tokenizedFiles, $tokens);
221
222
        $output->writeln("<comment>This module is not enabled. Enable it in <info>config[modules]</info> key</comment>");
223
224
        $this->checkEnabledTemplatingEngines($input, $output);
225
        $this->checkEnabledRouters($input, $output);
226
    }
227
228
    protected function isValidTemplatingEngine($tplEngine)
229
    {
230
        return in_array($tplEngine, [
231
            self::TPL_ENGINE_LATTE,
232
            self::TPL_ENGINE_PLATES,
233
            self::TPL_ENGINE_PHP,
234
            self::TPL_ENGINE_SMARTY,
235
            self::TPL_ENGINE_TWIG,
236
        ]);
237
    }
238
239
    protected function isValidRoutingEngine($routingEngine)
240
    {
241
        return in_array($routingEngine, [
242
            self::ROUTING_ENGINE_SYMFONY,
243
            self::ROUTING_ENGINE_AURA,
244
            self::ROUTING_ENGINE_LARAVEL,
245
            self::ROUTING_ENGINE_FASTROUTE,
246
            self::ROUTING_ENGINE_NULL,
247
        ]);
248
    }
249
250
    /**
251
     * @param string $moduleDir
252
     * @param array  $files
253
     * @param array  $tokens
254
     */
255
    protected function replaceTokensInFiles($moduleDir, $files, $tokens)
256
    {
257
        foreach ($files as $file) {
258
            $file = $moduleDir . DIRECTORY_SEPARATOR . $file;
259
            if (!is_writeable($file)) {
260
                throw new \InvalidArgumentException(sprintf('File %s is not writeable', $file));
261
            }
262
            file_put_contents($file, str_replace(array_keys($tokens), array_values($tokens), file_get_contents($file)));
263
        }
264
    }
265
266
    /**
267
     * @param string $skeletonDir
268
     * @param string $moduleDir
269
     * @param array  $files
270
     *
271
     * @throws \InvalidArgumentException When a file path being created already exists
272
     */
273
    protected function copyFiles($skeletonDir, $moduleDir, $files)
274
    {
275
        foreach ($files as $file) {
276
            $srcFile = $skeletonDir . DIRECTORY_SEPARATOR . $file;
277
            $dstFile = $moduleDir . DIRECTORY_SEPARATOR . $file;
278
            if (!file_exists($srcFile)) {
279
                throw new \InvalidArgumentException(sprintf('File does not exist: %s', $srcFile));
280
            }
281
            if (file_exists($dstFile)) {
282
                throw new \InvalidArgumentException(sprintf('File already exists: %s', $dstFile));
283
            }
284
            copy($srcFile, $dstFile);
285
        }
286
    }
287
288
    /**
289
     * @param string $moduleDir
290
     * @param string $moduleName
291
     *
292
     * @throws \InvalidArgumentException When a dir path being created already exists
293
     */
294
    protected function createModuleStructure($moduleDir, $moduleName)
295
    {
296
        if (is_dir($moduleDir)) {
297
            throw new \InvalidArgumentException(sprintf('Unable to create module: %s it already exists at %s%s', $moduleName, $moduleDir, $moduleName));
298
        }
299
300
        @mkdir($moduleDir);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
301
302
        // Create base structure
303
        foreach ($this->coreDirs as $coreDir) {
304
            $tmpDir = $moduleDir . DIRECTORY_SEPARATOR . $coreDir;
305
            @mkdir($tmpDir);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
306
        }
307
    }
308
309
    /**
310
     * @param InputInterface  $input
311
     * @param OutputInterface $output
312
     */
313
    protected function askQuestions(InputInterface $input, OutputInterface $output)
314
    {
315
        $questionHelper = $this->getHelper('question');
316
317
        // Module DIR
318
        if ($input->getOption('dir') == null) {
319
            $modulesDirQuestion = new ChoiceQuestion('Where is the modules dir?', [1 => $this->modulesDir], $this->modulesDir);
320
            $modulesDirQuestion->setErrorMessage('Modules dir: %s is invalid.');
321
            $this->modulesDir = $questionHelper->ask($input, $output, $modulesDirQuestion);
322
        }
323
324
        if($this->askForTemplating($input, $output)) {
325
            $this->chooseTemplatingEngine($input, $output);
326
        }
327
328
        if($this->askForContoller($input, $output)) {
0 ignored issues
show
Bug introduced by
The method askForContoller() does not seem to exist on object<PPI\Framework\Con...nd\ModuleCreateCommand>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
329
            if($this->askForRouting($input, $output)) {
330
                return $this->chooseRouter($input, $output);
331
            }
332
        }
333
    }
334
335
    /**
336
     * @param InputInterface  $input
337
     * @param OutputInterface $output
338
     */
339
    private function checkEnabledRouters(InputInterface $input, OutputInterface $output)
0 ignored issues
show
Unused Code introduced by
The parameter $input is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
340
    {
341
        // Aura Check
342
        if ($this->routingEngine == self::ROUTING_ENGINE_AURA && !class_exists('\Aura\Router\Router')) {
343
            $output->writeln("<comment>Aura Router doesn't appear to be loaded. Run: <info>composer require ppi/aura-router</info></comment>");
344
        }
345
346
        // Laravel check
347
        if ($this->routingEngine == self::ROUTING_ENGINE_LARAVEL && !class_exists('\PPI\LaravelRouting\LaravelRouter')) {
348
            $output->writeln("<comment>Laravel Router doesn't appear to be loaded. Run: <info>composer require ppi/laravel-router</info></comment>");
349
        }
350
351
        if ($this->routingEngine == self::ROUTING_ENGINE_FASTROUTE && !class_exists('\PPI\FastRoute\Wrapper\FastRouteWrapper')) {
352
            $output->writeln("<comment>FastRoute Router doesn't appear to be loaded. Run: <info>composer require ppi/fast-route</info></comment>");
353
        }
354
    }
355
356
    /**
357
     * @param InputInterface  $input
358
     * @param OutputInterface $output
359
     */
360
    private function checkEnabledTemplatingEngines(InputInterface $input, OutputInterface $output)
0 ignored issues
show
Unused Code introduced by
The parameter $input is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
361
    {
362
        // PHP Templating Engine checks
363
        if ($this->tplEngine == self::TPL_ENGINE_PHP) {
364
            if (!in_array($this->tplEngine, $this->configEnabledTemplatingEngines)) {
365
                $output->writeln(sprintf("<comment>PHP is not an enabled templating engine. Add <info>%s</info> it in <info>config[framework][templating][engines]</info> key</comment>", $this->tplEngine));
366
            }
367
        }
368
369
        // Twig Checks
370 View Code Duplication
        if ($this->tplEngine == self::TPL_ENGINE_TWIG) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
371
            if (!in_array($this->tplEngine, $this->configEnabledTemplatingEngines)) {
372
                $output->writeln(sprintf("<comment>Twig is not an enabled templating engine. Add <info>%s</info> it in <info>config[framework][templating][engines]</info> key</comment>", $this->tplEngine));
373
            }
374
            if (!class_exists('\Twig_Environment')) {
375
                $output->writeln("<comment>Twig doesn't appear to be loaded. Run: <info>composer require ppi/twig-module</info></comment>");
376
            }
377
        }
378
379
        // Smarty Checks
380 View Code Duplication
        if ($this->tplEngine == self::TPL_ENGINE_SMARTY) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
381
            if (!in_array($this->tplEngine, $this->configEnabledTemplatingEngines)) {
382
                $output->writeln(sprintf("<comment>Smarty is not an enabled templating engine. Add <info>%s</info> it in <info>config[framework][templating][engines]</info> key</comment>", $this->tplEngine));
383
            }
384
            if (!class_exists('\Smarty')) {
385
                $output->writeln("<comment>Smarty doesn't appear to be loaded. Run: <info>composer require ppi/smarty-module</info></comment>");
386
            }
387
        }
388
389
        // Plates Checks
390 View Code Duplication
        if ($this->tplEngine == self::TPL_ENGINE_PLATES) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
391
            if (!in_array($this->tplEngine, $this->configEnabledTemplatingEngines)) {
392
                $output->writeln(sprintf("<comment>Plates is not an enabled templating engine. Add <info>%s</info> it in <info>config[framework][templating][engines]</info> key</comment>", $this->tplEngine));
393
            }
394
            if (!class_exists('\PPI\PlatesModule\Wrapper\PlatesWrapper')) {
395
                $output->writeln("<comment>Plates doesn't appear to be loaded. Run: <info>composer require ppi/plates-module</info></comment>");
396
            }
397
        }
398
399
        // Plates Checks
400 View Code Duplication
        if ($this->tplEngine == self::TPL_ENGINE_LATTE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
401
            if (!in_array($this->tplEngine, $this->configEnabledTemplatingEngines)) {
402
                $output->writeln(sprintf("<comment>Latte is not an enabled templating engine. Add <info>%s</info> it in <info>config[framework][templating][engines]</info> key</comment>", $this->tplEngine));
403
            }
404
            if (!class_exists('\PPI\LatteModule\Wrapper\LatteWrapper')) {
405
                $output->writeln("<comment>Latte doesn't appear to be loaded. Run: <info>composer require ppi/latte-module</info></comment>");
406
            }
407
        }
408
    }
409
410
    /**
411
     * @param InputInterface $input
412
     * @param OutputInterface $output
413
     * @return boolean
414
     */
415 View Code Duplication
    private function askForTemplating(InputInterface $input, OutputInterface $output)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
416
    {
417
        $questionHelper = $this->getHelper('question');
418
        $question = new ConfirmationQuestion("Do you need templates? (yes/no):\n", false);
419
420
        return $questionHelper->ask($input, $output, $question);
421
    }
422
423 View Code Duplication
    private function askForRouting(InputInterface $input, OutputInterface $output)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
424
    {
425
        $questionHelper = $this->getHelper('question');
426
        $question = new ConfirmationQuestion("Which router do you want? (yes/no):\n", false);
427
428
        return $questionHelper->ask($input, $output, $question);
429
    }
430
431
    private function chooseTemplatingEngine($input, $output)
432
    {
433
        $tplQuestion = new ChoiceQuestion('Choose your templating engine [php]',
434
            [
435
                1 => 'php',
436
                2 => 'twig',
437
                3 => 'smarty',
438
                4 => 'plates',
439
                5 => 'latte',
440
                99 => 'skip'
441
            ]
442
        );
443
        $tplQuestion->setErrorMessage('Templating engine %s is invalid.');
444
        if(99 !== ($tplEngine = $this->getHelper('question')->ask($input, $output, $tplQuestion))) {
445
            $this->tplEngine = $tplEngine;
446
        }
447
    }
448
449
    /**
450
     * @param InputInterface $input
451
     * @param OutputInterface $output
452
     *
453
     * @return void
454
     */
455
    private function chooseRouter(InputInterface $input, OutputInterface $output)
456
    {
457
        $routingQuestion = new ChoiceQuestion('Choose your routing engine:',
458
            [
459
                1 => self::ROUTING_ENGINE_SYMFONY,
460
                2 => self::ROUTING_ENGINE_AURA,
461
                3 => self::ROUTING_ENGINE_LARAVEL,
462
                4 => self::ROUTING_ENGINE_FASTROUTE,
463
                99 => 'skip'
464
            ]
465
        );
466
467
        // @todo - test question when you don't choose any option, or an invalid one (like -1)
468
        $routingQuestion->setErrorMessage('Routing engine %s is invalid.');
469
        $chosenRouter = $this->getHelper('question')->ask($input, $output, $routingQuestion);
470
        if(99 == $chosenRouter) {
471
            $chosenRouter = 'NullRouter';
472
        }
473
        $this->routingEngine = $chosenRouter;
474
    }
475
476
    /**
477
     * @param $tplEngine
478
     *
479
     * @return void
480
     */
481
    private function getTemplatingFilesFromEngine($tplEngine)
482
    {
483
        if(!isset($this->tplEngineFilesMap[$tplEngine])) {
484
            throw new \InvalidArgumentException('Invalid templating engine specified for map files: ' . $tplEngine);
485
        }
486
    }
487
488
    /**
489
     * @return void
490
     */
491
    private function processTemplatingFiles()
492
    {
493
        $tplFiles = $this->getTemplatingFilesFromEngine($this->tplEngine);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $tplFiles is correct as $this->getTemplatingFile...ngine($this->tplEngine) (which targets PPI\Framework\Console\Co...latingFilesFromEngine()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
494
495
        // Copy core templating files over
496
        foreach($this->tplEngineCoreFiles as $coreFile) {
497
            $tplFiles[] = $coreFile;
498
        }
499
500
        // Copy templating files over relevant to the specified engine
501
        $this->copyFiles($this->skeletonModuleDir, $this->moduleDir, $tplFiles);
0 ignored issues
show
Documentation introduced by
$tplFiles is of type null, but the function expects a array.

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...
502
503
        // Setting up templating tokens
504
        $tokenizedFiles = [];
505
        foreach ($tplFiles as $tplFile) {
0 ignored issues
show
Bug introduced by
The expression $tplFiles of type null is not traversable.
Loading history...
506
            $tokenizedFiles[] = $tplFile;
507
        }
508
509
        $tokens['[TPL_ENGINE_EXT]'] = $this->tplEngine;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$tokens was never initialized. Although not strictly required by PHP, it is generally a good practice to add $tokens = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
510
511
512
        $this->replaceTokensInFiles($this->moduleDir, $tokenizedFiles, $tokens);
513
514
515
    }
516
517
    /**
518
     * @throws \Exception
519
     *
520
     * @return void
521
     */
522
    private function processRoutingFiles($tokenizedFiles, $tokens)
523
    {
524
525
        if(!isset($this->routingEngineFilesMap[$this->routingEngine])) {
526
            throw new \Exception('Routing engine not found in routing files map: ' . $this->routingEngine);
527
        }
528
529
        // Copy routing files over
530
        $routingFiles = $this->routingEngineFilesMap[$this->routingEngine];
531
532
        // If a valid routing engine and that's not null router
533
        if($this->routingEngine !== 99) {
534
            // Create core routing directories
535
            foreach($this->routingEngineCoreFiles as $coreFile) {
536
                @mkdir($this->moduleDir . DIRECTORY_SEPARATOR . $coreFile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
537
            }
538
        }
539
540
541
//var_dump(__METHOD__, __LINE__, $routingFiles); exit;
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
542
        $this->copyFiles($this->skeletonModuleDir, $this->moduleDir, $routingFiles);
543
544
        // Setting up routing tokenizable files
545
        foreach ($routingFiles as $routingFile) {
546
            $tokenizedFiles[] = $routingFile;
547
        }
548
549
        // Get all the tokens for this routing engine and the values the map to.
550
        $routingTokensMap = $this->getRoutingTokenMap($this->routingEngine);
551
        foreach ($routingTokensMap as $routingTokenKey => $routingTokenVal) {
552
            $tokens[$routingTokenKey] = $routingTokenVal;
553
        }
554
555
        // Replace tokens in all files
556
        $this->replaceTokensInFiles($this->moduleDir, $tokenizedFiles, $tokens);
557
558
        // Replace the ROUTING placeholder with this heredoc
559
560
        // Prepare the fastroute route file
561
        if ($this->routingEngine === self::ROUTING_ENGINE_FASTROUTE) {
562
            rename(
563
                $moduleDir . DIRECTORY_SEPARATOR . $routingFiles[0],
0 ignored issues
show
Bug introduced by
The variable $moduleDir does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
564
                str_replace('IndexInvoke', 'Index', $moduleDir . DIRECTORY_SEPARATOR . $routingFiles[0]
565
                ));
566
        }
567
    }
568
569
    /**
570
     * @return array
571
     */
572
    protected function getTokenizedCoreFiles()
573
    {
574
        $files = [];
575
        foreach ($this->coreFiles as $coreFile) {
576
            $files[] = $coreFile;
577
        }
578
        return $files;
579
    }
580
581
    /**
582
     * @param $routingEngine
583
     * @return array
584
     * @throws \Exception
585
     */
586
    private function getRoutingTokenMap($routingEngine) {
587
588
//        if(!isset($this->routingEngineTokenMap[$routingEngine])) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
589
//            throw new \Exception('No routing engine tokenizable files found for routing engine: ' . $this->routingEngine);
590
//        }
591
592
        $tokenMap = [];
593
594
        switch($routingEngine) {
595
            case self::ROUTING_ENGINE_SYMFONY:
596
                $tokenMap['[ROUTING_TRAIT]'] = 'use \PPI\Framework\Module\Routing\SymfonyTrait;';
597
                break;
598
599
            default:
600
                throw new \Exception('Unimplemented routing engine: ' . $routingEngine);
601
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
602
        }
603
604
        return $tokenMap;
605
    }
606
607
608
}
609