Test Setup Failed
Push — master ( f71949...6c6bd7 )
by Julito
55:21
created

InstallCommand::processInstallerScripts()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 13
nc 3
nop 2
dl 0
loc 20
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
namespace Chamilo\InstallerBundle\Command;
5
6
use RuntimeException;
7
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
8
use Symfony\Component\Console\Input\InputInterface;
9
use Symfony\Component\Console\Input\InputOption;
10
use Symfony\Component\Console\Output\OutputInterface;
11
use Symfony\Component\Console\Input\ArrayInput;
12
use Chamilo\InstallerBundle\CommandExecutor;
13
use Chamilo\InstallerBundle\ScriptExecutor;
14
15
/**
16
 * Class InstallCommand
17
 * Based in OroInstallBundle
18
 * @package Chamilo\InstallerBundle\Command
19
 */
20
class InstallCommand extends ContainerAwareCommand
21
{
22
    /**
23
     * @inheritdoc
24
     */
25
    protected function configure()
26
    {
27
        $this
28
            ->setName('chamilo:install')
29
            ->setDescription('Chamilo installer.')
30
            ->addOption(
31
                'force',
32
                null,
33
                InputOption::VALUE_NONE,
34
                'Force installation'
35
            )
36
            ->addOption(
37
                'timeout',
38
                null,
39
                InputOption::VALUE_OPTIONAL,
40
                'Timeout for child command execution',
41
                300
42
            )
43
            ->addOption(
44
                'drop-database',
45
                null,
46
                InputOption::VALUE_NONE,
47
                'Database will be dropped and all data will be deleted.'
48
            );
49
    }
50
51
    /**
52
     * @param InputInterface $input
53
     * @param OutputInterface $output
54
     * @return int|null|void
55
     */
56
    protected function execute(InputInterface $input, OutputInterface $output)
57
    {
58
        $forceInstall = $input->getOption('force');
59
60
        $commandExecutor = new CommandExecutor(
61
            $input->hasOption('env') ? $input->getOption('env') : null,
62
            $output,
63
            $this->getApplication()
0 ignored issues
show
Bug introduced by
It seems like $this->getApplication() can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
64
        //$this->getContainer()->get('oro_cache.oro_data_cache_manager')
65
        );
66
67
        $commandExecutor->setDefaultTimeout($input->getOption('timeout'));
68
69
        // if there is application is not installed or no --force option
70
        $isInstalled = $this->getContainer()->hasParameter('installed')
71
            && $this->getContainer()->getParameter('installed');
72
73
        if ($isInstalled && !$forceInstall) {
74
            $output->writeln(
75
                '<comment>ATTENTION</comment>: Chamilo is already installed.'
76
            );
77
            $output->writeln(
78
                'To proceed with install - run command with <info>--force</info> option:'
79
            );
80
            $output->writeln(
81
                sprintf('    <info>%s --force</info>', $this->getName())
82
            );
83
            $output->writeln(
84
                'To reinstall over existing database - run command with <info>--force --drop-database</info> options:'
85
            );
86
            $output->writeln(
87
                sprintf(
88
                    '    <info>%s --force --drop-database</info>',
89
                    $this->getName()
90
                )
91
            );
92
            $output->writeln(
93
                '<comment>ATTENTION</comment>: All data will be lost. '.
94
                'Database backup is highly recommended before executing this command.'
95
            );
96
            $output->writeln('');
97
98
            return;
99
        }
100
101
        if ($forceInstall) {
102
            // if --force option we have to clear cache and set installed to false
103
            $this->updateInstalledFlag(false);
104
            // See https://github.com/symfony/symfony/issues/12278
105
            $commandExecutor->runCommand(
106
                'cache:clear',
107
                array(
108
                    '--no-optional-warmers' => true,
109
                    '--process-isolation' => true,
110
                )
111
            );
112
        }
113
114
        $output->writeln('<info>Installing Chamilo.</info>');
115
        $output->writeln('');
116
117
        $this
118
            ->checkStep($input, $output)
119
            ->setupStep($commandExecutor, $input, $output)
120
            ->finalStep($commandExecutor, $input, $output);
121
122
        $output->writeln('');
123
        $output->writeln(
124
            sprintf(
125
                '<info>Chamilo has been successfully installed in <comment>%s</comment> mode.</info>',
126
                $input->getOption('env')
127
            )
128
        );
129
        if ('prod' != $input->getOption('env')) {
130
            $output->writeln(
131
                '<info>To run application in <comment>prod</comment> mode, '.
132
                'please run <comment>cache:clear</comment> command with <comment>--env prod</comment> parameter</info>'
133
            );
134
        }
135
    }
136
137
    /**
138
     * @param string $command
139
     * @param OutputInterface $output
140
     * @param array $arguments
141
     * @return $this
142
     * @throws \Exception
143
     */
144
    protected function runCommand(
145
        $command,
146
        OutputInterface $output,
147
        $arguments = array()
148
    ) {
149
        $arguments['command'] = $command;
150
        $input = new ArrayInput($arguments);
151
152
        $this
153
            ->getApplication()
154
            ->find($command)
155
            ->run($input, $output);
156
157
        return $this;
158
    }
159
160
    /**
161
     * @param InputInterface $input
162
     * @param OutputInterface $output
163
     * @return $this
164
     */
165
    protected function checkStep(InputInterface $input, OutputInterface $output)
166
    {
167
        $output->writeln('<info>Checking system requirements.</info>');
168
169
        require_once $this->getContainer()->getParameter('kernel.root_dir')
170
            .DIRECTORY_SEPARATOR
171
            .'ChamiloRequirements.php';
172
173
        $collection = new \ChamiloRequirements();
174
175
        $this->renderTable(
176
            $collection->getMandatoryRequirements(),
177
            'Mandatory requirements',
178
            $output
179
        );
180
        $this->renderTable(
181
            $collection->getPhpIniRequirements(),
182
            'PHP settings',
183
            $output
184
        );
185
        $this->renderTable(
186
            $collection->getChamiloRequirements(),
187
            'Chamilo specific requirements',
188
            $output
189
        );
190
        $this->renderTable(
191
            $collection->getRecommendations(),
192
            'Optional recommendations',
193
            $output
194
        );
195
196
        if (count($collection->getFailedRequirements())) {
197
            throw new \RuntimeException(
198
                'Some system requirements are not fulfilled. Please check output messages and fix them.'
199
            );
200
        }
201
202
        $output->writeln('');
203
204
        return $this;
205
    }
206
207
    /**
208
     * @param InputInterface $input
209
     * @param OutputInterface $output
210
     * @return $this
211
     */
212
    protected function setupStep(
213
        CommandExecutor $commandExecutor,
214
        InputInterface $input,
215
        OutputInterface $output
216
    ) {
217
        $output->writeln('<info>Setting up database.</info>');
218
219
        /** @var DialogHelper $dialog */
220
        $dialog = $this->getHelperSet()->get('dialog');
221
        $options = $input->getOptions();
222
223
        $input->setInteractive(false);
224
225
        $schemaDropOptions = array(
226
            '--force' => true,
227
            '--process-isolation' => true,
228
        );
229
230
        if ($input->getOption('drop-database')) {
231
            $schemaDropOptions['--full-database'] = true;
232
        }
233
234
        $commandExecutor
235
            ->runCommand(
236
                'doctrine:schema:drop',
237
                $schemaDropOptions
238
            )
239
            //->runCommand('oro:entity-config:cache:clear', array('--no-warmup' => true))
240
            //->runCommand('oro:entity-extend:cache:clear', array('--no-warmup' => true))
241
            ->runCommand(
242
                'oro:migration:load',
243
                array(
244
                    '--force' => true,
245
                    '--process-isolation' => true,
246
                )
247
            )
248
            /*->runCommand(
249
                'oro:workflow:definitions:load',
250
                array(
251
                    '--process-isolation' => true,
252
                )
253
            )*/
254
            /*->runCommand(
255
                'oro:process:configuration:load',
256
                array(
257
                    '--process-isolation' => true
258
                )
259
            )*/
260
            ->runCommand(
261
                'oro:migration:data:load',
262
                array(
263
                    '--process-isolation' => true,
264
                    '--no-interaction' => true,
265
                )
266
            );
267
        //if ($this->getHelperSet()->get('dialog')->askConfirmation($output, '<question>Load fixtures (Y/N)?</question>', false)) {
268
        $this->setupFixtures($input, $output);
269
270
        // Installing platform settings
271
        $settingsManager = $this->getContainer()->get(
272
            'chamilo.settings.manager'
273
        );
274
        $url = $this->getContainer()->get('doctrine')->getRepository(
275
            'ChamiloCoreBundle:AccessUrl'
276
        )->find(1);
277
        $settingsManager->installSchemas($url);
278
279
        $output->writeln('');
280
        $output->writeln('<info>Administration setup.</info>');
281
282
        $this->setupAdmin($output);
283
        /*
284
        $this->runCommand(
285
            'sonata:page:update-core-routes',
286
            $output,
287
            array('--site' => array('all'))
288
        );
289
        $this->runCommand(
290
            'sonata:page:create-snapshots',
291
            $output,
292
            array('--site' => array('all'))
293
        );*/
294
295
        $output->writeln('');
296
297
        return $this;
298
    }
299
300
    /**
301
     * @param InputInterface $input
302
     * @param OutputInterface $output
303
     */
304
    protected function setupDatabase(
305
        InputInterface $input,
306
        OutputInterface $output
307
    ) {
308
        $this
309
            ->runCommand('doctrine:database:create', $input, $output)
310
            ->runCommand('doctrine:schema:create', $input, $output)
311
            //->runCommand('doctrine:phpcr:repository:init', $input, $output)
312
            //->runCommand('assets:install', $input, $output)
313
            //->runCommand('assetic:dump', $input, $output)
314
        ;
315
    }
316
317
    /**
318
     * @param InputInterface $input
319
     * @param OutputInterface $output
320
     */
321
    protected function setupFixtures(
322
        InputInterface $input,
323
        OutputInterface $output
324
    ) {
325
        $this
326
            ->runCommand(
327
                'doctrine:fixtures:load',
328
                $output,
329
                array('--no-interaction' => true)
330
            )//->runCommand('doctrine:phpcr:fixtures:load', $input, $output)
331
        ;
332
    }
333
334
    /**
335
     * @param OutputInterface $output
336
     */
337
    protected function setupAdmin(OutputInterface $output)
338
    {
339
        $dialog = $this->getHelperSet()->get('dialog');
340
        $em = $this->getApplication()->getKernel()->getContainer()->get('doctrine')->getManager();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Console\Application as the method getKernel() does only exist in the following sub-classes of Symfony\Component\Console\Application: Symfony\Bundle\FrameworkBundle\Console\Application. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
341
342
        /** @var \Chamilo\UserBundle\Entity\User $user */
343
        $user = $em->getRepository('ChamiloUserBundle:User')->findOneById(1);
344
345
        $user->setUsername(
346
            $dialog->ask(
347
                $output,
348
                '<question>Username</question>(admin):',
349
                'admin'
350
            )
351
        );
352
        $user->setPlainPassword(
353
            $dialog->ask(
354
                $output,
355
                '<question>Password</question>(admin):',
356
                'admin'
357
            )
358
        );
359
        $user->setFirstname(
360
            $dialog->ask(
361
                $output,
362
                '<question>Firstname</question>(Jane):',
363
                'Jane'
364
            )
365
        );
366
        $user->setLastname(
367
            $dialog->ask($output, '<question>Lastname</question>(Doe):', 'Doe')
368
        );
369
        $user->setEmail(
370
            $dialog->ask(
371
                $output,
372
                '<question>Email</question>([email protected]):',
373
                '[email protected]'
374
            )
375
        );
376
        $user->setEnabled(true);
377
        $user->addRole('ROLE_SUPER_ADMIN');
378
379
        $manager = $this->getContainer()->get('fos_user.user_manager.default');
380
        $manager->updateUser($user, true);
381
    }
382
383
    /**
384
     * @param CommandExecutor $commandExecutor
385
     * @param InputInterface $input
386
     * @param OutputInterface $output
387
     * @return InstallCommand
388
     */
389
    protected function finalStep(
390
        CommandExecutor $commandExecutor,
391
        InputInterface $input,
392
        OutputInterface $output
393
    ) {
394
        $output->writeln('<info>Preparing application.</info>');
395
        $input->setInteractive(false);
396
        $commandExecutor
397
            /*->runCommand(
398
                'oro:navigation:init',
399
                array(
400
                    '--process-isolation' => true,
401
                )
402
            )
403
            ->runCommand(
404
                'fos:js-routing:dump',
405
                array(
406
                    '--target' => 'web/js/routes.js',
407
                    '--process-isolation' => true,
408
                )
409
            )
410
            ->runCommand('oro:localization:dump')
411
            */
412
            ->runCommand(
413
                'assets:install',
414
                array(
415
                    'target' => './',
416
                    '--symlink' => true,
417
                    '--relative' => true,
418
                )
419
            )
420
            ->runCommand(
421
                'assetic:dump',
422
                array(
423
                    '--process-isolation' => true,
424
                )
425
            )/*->runCommand(
426
                'oro:translation:dump',
427
                array(
428
                    '--process-isolation' => true,
429
                )
430
            )
431
            ->runCommand(
432
                'oro:requirejs:build',
433
                array(
434
                    '--ignore-errors' => true,
435
                    '--process-isolation' => true,
436
                )
437
            )*/
438
        ;
439
440
        // run installer scripts
441
        $this->processInstallerScripts($output, $commandExecutor);
442
        $this->updateInstalledFlag(date('c'));
443
444
        // clear the cache set installed flag in DI container
445
        $commandExecutor->runCommand(
446
            'cache:clear',
447
            array(
448
                '--process-isolation' => true,
449
            )
450
        );
451
452
        $output->writeln('');
453
454
        return $this;
455
    }
456
457
    /**
458
     * Process installer scripts
459
     *
460
     * @param OutputInterface $output
461
     * @param CommandExecutor $commandExecutor
462
     */
463
    protected function processInstallerScripts(
464
        OutputInterface $output,
465
        CommandExecutor $commandExecutor
466
    ) {
467
        $scriptExecutor = new ScriptExecutor(
468
            $output,
469
            $this->getContainer(),
470
            $commandExecutor
471
        );
472
        /** @var ScriptManager $scriptManager */
473
        $scriptManager = $this->getContainer()->get(
474
            'chamilo_installer.script_manager'
475
        );
476
        $scriptFiles = $scriptManager->getScriptFiles();
477
        if (!empty($scriptFiles)) {
478
            foreach ($scriptFiles as $scriptFile) {
479
                $scriptExecutor->runScript($scriptFile);
480
            }
481
        }
482
    }
483
484
    /**
485
     * Update installed flag in parameters.yml
486
     *
487
     * @param bool|string $installed
488
     */
489
    protected function updateInstalledFlag($installed)
490
    {
491
        $dumper = $this->getContainer()->get(
492
            'chamilo_installer.yaml_persister'
493
        );
494
        $params = $dumper->parse();
495
        $params['system']['installed'] = $installed;
496
        $dumper->dump($params);
497
    }
498
499
    /**
500
     * Render requirements table
501
     *
502
     * @param array $collection
503
     * @param string $header
504
     * @param OutputInterface $output
505
     */
506
    protected function renderTable(
507
        array $collection,
508
        $header,
509
        OutputInterface $output
510
    ) {
511
        /** @var TableHelper $table */
512
        $table = $this->getHelperSet()->get('table');
513
514
        $table
515
            ->setHeaders(array('Check  ', $header))
516
            ->setRows(array());
517
518
        /** @var \Requirement $requirement */
519
        foreach ($collection as $requirement) {
520
            if ($requirement->isFulfilled()) {
521
                $table->addRow(array('OK', $requirement->getTestMessage()));
522
            } else {
523
                $table->addRow(
524
                    array(
525
                        $requirement->isOptional() ? 'WARNING' : 'ERROR',
526
                        $requirement->getHelpText(),
527
                    )
528
                );
529
            }
530
        }
531
532
        $table->render($output);
533
    }
534
}
535