Issues (3627)

bundles/InstallBundle/Command/InstallCommand.php (6 issues)

1
<?php
2
3
/*
4
 * @copyright   2019 Mautic Contributors. All rights reserved
5
 * @author      Mautic
6
 *
7
 * @link        http://mautic.org
8
 *
9
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
10
 */
11
12
namespace Mautic\InstallBundle\Command;
13
14
use Mautic\InstallBundle\Configurator\Step\CheckStep;
15
use Mautic\InstallBundle\Configurator\Step\DoctrineStep;
16
use Mautic\InstallBundle\Configurator\Step\EmailStep;
17
use Mautic\InstallBundle\Install\InstallService;
18
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
19
use Symfony\Component\Console\Input\InputArgument;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Input\InputOption;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\Console\Question\ConfirmationQuestion;
24
25
/**
26
 * CLI Command to install Mautic.
27
 * Class InstallCommand.
28
 */
29
class InstallCommand extends ContainerAwareCommand
30
{
31
    /**
32
     * {@inheritdoc}
33
     */
34
    protected function configure()
35
    {
36
        $this
37
            ->setName('mautic:install')
38
            ->setDescription('Installs Mautic')
39
            ->setHelp('This command allows you to trigger the install process.')
40
            ->addArgument(
41
                'site_url',
42
                InputArgument::REQUIRED,
43
                'Site URL.',
44
                null
45
            )
46
            ->addArgument(
47
                'step',
48
                InputArgument::OPTIONAL,
49
                'Install process start index. 0 for requirements check, 1 for database, 2 for admin, 3 for configuration, 4 for final step. Each successful step will trigger the next until completion.',
50
                0
51
            )
52
            ->addOption(
53
                '--force',
54
                '-f',
55
                InputOption::VALUE_NONE,
56
                'Do not ask confirmation if recommendations triggered.',
57
                null
58
            )
59
            ->addOption(
60
                '--db_driver',
61
                null,
62
                InputOption::VALUE_REQUIRED,
63
                'Database driver.',
64
                'pdo_mysql'
65
            )
66
            ->addOption(
67
                '--db_host',
68
                null,
69
                InputOption::VALUE_REQUIRED,
70
                'Database host.',
71
                null
72
            )
73
            ->addOption(
74
                '--db_port',
75
                null,
76
                InputOption::VALUE_REQUIRED,
77
                'Database port.',
78
                null
79
            )
80
            ->addOption(
81
                '--db_name',
82
                null,
83
                InputOption::VALUE_REQUIRED,
84
                'Database name.',
85
                null
86
            )
87
            ->addOption(
88
                '--db_user',
89
                null,
90
                InputOption::VALUE_REQUIRED,
91
                'Database user.',
92
                null
93
            )
94
            ->addOption(
95
                '--db_password',
96
                null,
97
                InputOption::VALUE_REQUIRED,
98
                'Database password.',
99
                null
100
            )
101
            ->addOption(
102
                '--db_table_prefix',
103
                null,
104
                InputOption::VALUE_REQUIRED,
105
                'Database tables prefix.',
106
                null
107
            )
108
            ->addOption(
109
                '--db_backup_tables',
110
                null,
111
                InputOption::VALUE_REQUIRED,
112
                'Backup database tables if they exist; otherwise drop them.',
113
                true
114
            )
115
            ->addOption(
116
                '--db_backup_prefix',
117
                null,
118
                InputOption::VALUE_REQUIRED,
119
                'Database backup tables prefix.',
120
                'bak_'
121
            )
122
            ->addOption(
123
                '--admin_firstname',
124
                null,
125
                InputOption::VALUE_REQUIRED,
126
                'Admin first name.',
127
                'Admin'
128
            )
129
            ->addOption(
130
                '--admin_lastname',
131
                null,
132
                InputOption::VALUE_REQUIRED,
133
                'Admin last name.',
134
                'Mautic'
135
            )
136
            ->addOption(
137
                '--admin_username',
138
                null,
139
                InputOption::VALUE_REQUIRED,
140
                'Admin username.',
141
                'admin'
142
            )
143
            ->addOption(
144
                '--admin_email',
145
                null,
146
                InputOption::VALUE_REQUIRED,
147
                'Admin email.',
148
                null
149
            )
150
            ->addOption(
151
                '--admin_password',
152
                null,
153
                InputOption::VALUE_REQUIRED,
154
                'Admin user.',
155
                null
156
            )
157
            ->addOption(
158
                '--mailer_from_name',
159
                null,
160
                InputOption::VALUE_OPTIONAL,
161
                'From name for email sent from Mautic.',
162
                null
163
            )
164
            ->addOption(
165
                '--mailer_from_email',
166
                null,
167
                InputOption::VALUE_OPTIONAL,
168
                'From email sent from Mautic.',
169
                null
170
            )
171
            ->addOption(
172
                '--mailer_transport',
173
                null,
174
                InputOption::VALUE_OPTIONAL,
175
                'Mail transport.',
176
                null
177
            )
178
            ->addOption(
179
                '--mailer_host',
180
                null,
181
                InputOption::VALUE_REQUIRED,
182
                'SMTP host.',
183
                null
184
            )
185
            ->addOption(
186
                '--mailer_port',
187
                null,
188
                InputOption::VALUE_REQUIRED,
189
                'SMTP port.',
190
                null
191
            )
192
            ->addOption(
193
                '--mailer_user',
194
                null,
195
                InputOption::VALUE_REQUIRED,
196
                'SMTP username.',
197
                null
198
            )
199
            ->addOption(
200
                '--mailer_password',
201
                null,
202
                InputOption::VALUE_OPTIONAL,
203
                'SMTP password.',
204
                null
205
            )
206
            ->addOption(
207
                '--mailer_encryption',
208
                null,
209
                InputOption::VALUE_OPTIONAL,
210
                'SMTP encryption (null|tls|ssl).',
211
                null
212
            )
213
            ->addOption(
214
                '--mailer_auth_mode',
215
                null,
216
                InputOption::VALUE_OPTIONAL,
217
                'SMTP auth mode (null|plain|login|cram-md5).',
218
                null
219
            )
220
            ->addOption(
221
                '--mailer_spool_type',
222
                null,
223
                InputOption::VALUE_REQUIRED,
224
                'Spool mode (file|memory).',
225
                null
226
            )
227
            ->addOption(
228
                '--mailer_spool_path',
229
                null,
230
                InputOption::VALUE_REQUIRED,
231
                'Spool path.',
232
                null
233
            )
234
        ;
235
        parent::configure();
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241
    protected function execute(InputInterface $input, OutputInterface $output)
242
    {
243
        $container = $this->getContainer();
244
        /** @var \Mautic\InstallBundle\Install\InstallService $installer */
245
        $installer = $container->get('mautic.install.service');
246
247
        // Check Mautic is not already installed
248
        if ($installer->checkIfInstalled()) {
249
            $output->writeln('Mautic already installed');
250
251
            return 0;
252
        }
253
254
        $output->writeln([
255
            'Mautic Install',
256
            '==============',
257
            '',
258
        ]);
259
260
        // Build objects to pass to the install service from local.php or command line options
261
        $output->writeln('Parsing options and arguments...');
262
        $options = $input->getOptions();
263
264
        $dbParams   = [];
265
        $adminParam = [];
266
        $allParams  = $installer->localConfigParameters();
267
268
        // Initialize DB and admin params from local.php
269
        foreach ((array) $allParams as $opt => $value) {
270
            if (0 === strpos($opt, 'db_')) {
271
                $dbParams[substr($opt, 3)] = $value;
272
            } elseif (0 === strpos($opt, 'admin_')) {
273
                $adminParam[substr($opt, 6)] = $value;
274
            }
275
        }
276
277
        // Initialize DB and admin params from cli options
278
        foreach ($options as $opt => $value) {
279
            if (!empty($value)) {
280
                if (0 === strpos($opt, 'db_')) {
281
                    $dbParams[substr($opt, 3)] = $value;
282
                    $allParams[$opt]           = $value;
283
                } elseif (0 === strpos($opt, 'admin_')) {
284
                    $adminParam[substr($opt, 6)] = $value;
285
                } elseif (0 === strpos($opt, 'mailer_')) {
286
                    $allParams[$opt] = $value;
287
                }
288
            }
289
        }
290
291
        if (isset($allParams['site_url']) && !empty($allParams['site_url'])) {
292
            $siteUrl = $allParams['site_url'];
293
        } else {
294
            $siteUrl               = $input->getArgument('site_url');
295
            $allParams['site_url'] = $siteUrl;
296
        }
297
298
        if ((!isset($allParams['mailer_from_name']) || empty($allParams['mailer_from_name']))
299
            && isset($adminParam['firstname']) && isset($adminParam['lastname'])) {
300
            $allParams['mailer_from_name'] = $adminParam['firstname'].' '.$adminParam['lastname'];
301
        }
302
303
        if ((!isset($allParams['mailer_from_email']) || empty($allParams['mailer_from_email']))
304
            && isset($adminParam['email'])) {
305
            $allParams['mailer_from_email'] = $adminParam['email'];
306
        }
307
308
        $step = $input->getArgument('step');
309
310
        switch ($step) {
311
            default:
312
            case InstallService::CHECK_STEP:
313
                $output->writeln($step.' - Checking installation requirements...');
314
                $messages = $this->stepAction($installer, ['site_url' => $siteUrl], $step);
315
                if (is_array($messages) && !empty($messages)) {
316
                    if (isset($messages['requirements']) && !empty($messages['requirements'])) {
317
                        // Stop install if requirements not met
318
                        $output->writeln('Missing requirements:');
319
                        $this->handleInstallerErrors($output, $messages['requirements']);
320
                        $output->writeln('Install canceled');
321
322
                        return -$step;
323
                    } elseif (isset($messages['optional']) && !empty($messages['optional'])) {
324
                        $output->writeln('Missing optional settings:');
325
                        $this->handleInstallerErrors($output, $messages['optional']);
326
327
                        if (!isset($options['force'])) {
328
                            // Ask user to confirm install when optional settings missing
329
                            $helper   = $this->getHelper('question');
330
                            $question = new ConfirmationQuestion('Continue with install anyway? ', false);
331
332
                            if (!$helper->ask($input, $output, $question)) {
333
                                return -$step;
334
                            }
335
                        }
336
                    }
337
                }
338
                $output->writeln('Ready to Install!');
339
                // Keep on with next step
340
                $step = InstallService::DOCTRINE_STEP;
341
342
                // no break
343
            case InstallService::DOCTRINE_STEP:
344
                $output->writeln($step.' - Creating database...');
345
                $messages = $this->stepAction($installer, $dbParams, $step);
0 ignored issues
show
It seems like $step can also be of type string; however, parameter $index of Mautic\InstallBundle\Com...llCommand::stepAction() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

345
                $messages = $this->stepAction($installer, $dbParams, /** @scrutinizer ignore-type */ $step);
Loading history...
346
                if (is_array($messages) && !empty($messages)) {
347
                    $output->writeln('Errors in database configuration/installation:');
348
                    $this->handleInstallerErrors($output, $messages);
349
350
                    $output->writeln('Install canceled');
351
352
                    return -$step;
353
                }
354
                $step = InstallService::DOCTRINE_STEP + .1;
355
356
                $output->writeln($step.' - Creating schema...');
357
                $messages = $this->stepAction($installer, $dbParams, $step);
358
                if (is_array($messages) && !empty($messages)) {
359
                    $output->writeln('Errors in schema configuration/installation:');
360
                    $this->handleInstallerErrors($output, $messages);
361
362
                    $output->writeln('Install canceled');
363
364
                    return -InstallService::DOCTRINE_STEP;
365
                }
366
                $step = InstallService::DOCTRINE_STEP + .2;
367
368
                $output->writeln($step.' - Loading fixtures...');
369
                $messages = $this->stepAction($installer, $dbParams, $step);
370
                if (is_array($messages) && !empty($messages)) {
371
                    $output->writeln('Errors in fixtures configuration/installation:');
372
                    $this->handleInstallerErrors($output, $messages);
373
374
                    $output->writeln('Install canceled');
375
376
                    return -InstallService::DOCTRINE_STEP;
377
                }
378
                // Keep on with next step
379
                $step = InstallService::USER_STEP;
380
381
                // no break
382
            case InstallService::USER_STEP:
383
                $output->writeln($step.' - Creating admin user...');
384
                $messages = $this->stepAction($installer, $adminParam, $step);
385
                if (is_array($messages) && !empty($messages)) {
386
                    $output->writeln('Errors in admin user configuration/installation:');
387
                    $this->handleInstallerErrors($output, $messages);
388
389
                    $output->writeln('Install canceled');
390
391
                    return -$step;
392
                }
393
                // Keep on with next step
394
                $step = InstallService::EMAIL_STEP;
395
396
                // no break
397
            case InstallService::EMAIL_STEP:
398
                $output->writeln($step.' - Email configuration...');
399
                $messages = $this->stepAction($installer, $allParams, $step);
400
                if (is_array($messages) && !empty($messages)) {
401
                    $output->writeln('Errors in email configuration:');
402
                    $this->handleInstallerErrors($output, $messages);
403
404
                    $output->writeln('Install canceled');
405
406
                    return -$step;
407
                }
408
                // Keep on with next step
409
                $step = InstallService::FINAL_STEP;
410
411
                // no break
412
            case InstallService::FINAL_STEP:
413
                $output->writeln($step.' - Final steps...');
414
                $messages = $this->stepAction($installer, $allParams, $step);
415
416
                if (is_array($messages) && !empty($messages)) {
417
                    $output->writeln('Errors in final migration:');
418
                    $this->handleInstallerErrors($output, $messages);
419
420
                    $output->writeln('Install canceled');
421
422
                    return -$step;
423
                }
424
        }
425
426
        $output->writeln([
427
            '',
428
            '================',
429
            'Install complete',
430
            '================',
431
        ]);
432
433
        return 0;
434
    }
435
436
    /**
437
     * Controller action for install steps.
438
     *
439
     * @param InstallService $installer The install process
440
     * @param array          $params    The install parameters
441
     * @param int            $index     The step number to process
442
     *
443
     * @return int|array|bool
444
     *
445
     * @throws \Exception
446
     */
447
    protected function stepAction(InstallService $installer, $params, $index = 0)
448
    {
449
        if (false !== strpos($index, '.')) {
450
            list($index, $subIndex) = explode('.', $index);
451
        }
452
453
        $step = $installer->getStep($index);
0 ignored issues
show
It seems like $index can also be of type string; however, parameter $index of Mautic\InstallBundle\Ins...stallService::getStep() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

453
        $step = $installer->getStep(/** @scrutinizer ignore-type */ $index);
Loading history...
454
455
        $messages = false;
456
        switch ($index) {
457
            case InstallService::CHECK_STEP:
458
                // Check installation requirements
459
                if ($step instanceof CheckStep) {
460
                    // Set all step fields based on parameters
461
                    $step->site_url = $params['site_url'];
462
                }
463
464
                $messages                 = [];
465
                $messages['requirements'] = $installer->checkRequirements($step);
0 ignored issues
show
It seems like $step can also be of type boolean; however, parameter $step of Mautic\InstallBundle\Ins...ce::checkRequirements() does only seem to accept Mautic\CoreBundle\Config...Step\StepInterface|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

465
                $messages['requirements'] = $installer->checkRequirements(/** @scrutinizer ignore-type */ $step);
Loading history...
466
                $messages['optional']     = $installer->checkOptionalSettings($step);
0 ignored issues
show
It seems like $step can also be of type boolean; however, parameter $step of Mautic\InstallBundle\Ins...checkOptionalSettings() does only seem to accept Mautic\CoreBundle\Config...Step\StepInterface|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

466
                $messages['optional']     = $installer->checkOptionalSettings(/** @scrutinizer ignore-type */ $step);
Loading history...
467
                break;
468
469
            case InstallService::DOCTRINE_STEP:
470
                if ($step instanceof DoctrineStep) {
471
                    // Set all step fields based on parameters
472
                    foreach ($step as $key => $value) {
473
                        if (isset($params[$key])) {
474
                            $step->$key = $params[$key];
475
                        }
476
                    }
477
                }
478
                if (!isset($subIndex)) {
479
                    // Install database
480
                    $messages = $installer->createDatabaseStep($step, $params);
0 ignored issues
show
It seems like $step can also be of type boolean; however, parameter $step of Mautic\InstallBundle\Ins...e::createDatabaseStep() does only seem to accept Mautic\CoreBundle\Config...Step\StepInterface|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

480
                    $messages = $installer->createDatabaseStep(/** @scrutinizer ignore-type */ $step, $params);
Loading history...
481
                } else {
482
                    switch ((int) $subIndex) {
483
                        case 1:
484
                            // Install schema
485
                            $messages = $installer->createSchemaStep($params);
486
                            break;
487
488
                        case 2:
489
                            // Install fixtures
490
                            $messages = $installer->createFixturesStep($this->getContainer());
491
                            break;
492
                    }
493
                }
494
                break;
495
496
            case InstallService::USER_STEP:
497
                // Create admin user
498
                $messages = $installer->createAdminUserStep($params);
499
                break;
500
501
            case InstallService::EMAIL_STEP:
502
                // Save email configuration
503
                if ($step instanceof EmailStep) {
504
                    // Set all step fields based on parameters
505
                    foreach ($step as $key => $value) {
506
                        if (isset($params[$key])) {
507
                            $step->$key = $params[$key];
508
                        }
509
                    }
510
                }
511
                $messages = $installer->setupEmailStep($step, $params);
0 ignored issues
show
It seems like $step can also be of type boolean; however, parameter $step of Mautic\InstallBundle\Ins...rvice::setupEmailStep() does only seem to accept Mautic\CoreBundle\Config...Step\StepInterface|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

511
                $messages = $installer->setupEmailStep(/** @scrutinizer ignore-type */ $step, $params);
Loading history...
512
                break;
513
514
            case InstallService::FINAL_STEP:
515
                // Save final configuration
516
                $siteUrl  = $params['site_url'];
517
                $messages = $installer->createFinalConfigStep($siteUrl);
518
                if (is_bool($messages) && true === $messages) {
519
                    $installer->finalMigrationStep();
520
                }
521
                break;
522
        }
523
524
        return $messages;
525
    }
526
527
    /**
528
     * Handle install command errors.
529
     */
530
    private function handleInstallerErrors(OutputInterface $output, array $messages)
531
    {
532
        foreach ($messages as $type => $message) {
533
            $output->writeln("  - [$type] $message");
534
        }
535
    }
536
}
537