Completed
Push — master ( c365cd...202f3c )
by Tom
05:19
created

N98/Magento/Command/Installer/InstallCommand.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace N98\Magento\Command\Installer;
4
5
use Composer\Composer;
6
use Composer\Package\CompletePackage;
7
use Exception;
8
use InvalidArgumentException;
9
use N98\Magento\Command\AbstractMagentoCommand;
10
use N98\Util\Database as DatabaseUtils;
11
use N98\Util\Filesystem;
12
use N98\Util\OperatingSystem;
13
use N98\Util\BinaryString;
14
use PDO;
15
use PDOException;
16
use RuntimeException;
17
use Symfony\Component\Console\Input\InputInterface;
18
use Symfony\Component\Console\Input\InputOption;
19
use Symfony\Component\Console\Input\StringInput;
20
use Symfony\Component\Console\Output\OutputInterface;
21
use Symfony\Component\Finder\Finder;
22
use N98\Util\Exec;
23
24
/**
25
 * Class InstallCommand
26
 *
27
 * @codeCoverageIgnore  - Travis server uses installer to create a new shop. If it not works complete build fails.
28
 * @package N98\Magento\Command\Installer
29
 */
30
class InstallCommand extends AbstractMagentoCommand
31
{
32
    const EXEC_STATUS_OK = 0;
33
    /**
34
     * @var array
35
     */
36
    protected $config;
37
38
    /**
39
     * @var array
40
     */
41
    protected $_argv;
42
43
    /**
44
     * @var array
45
     */
46
    protected $commandConfig;
47
48
    /**
49
     * @var \Closure
50
     */
51
    protected $notEmptyCallback;
52
53
    protected function configure()
54
    {
55
        $this
56
            ->setName('install')
57
            ->addOption('magentoVersion', null, InputOption::VALUE_OPTIONAL, 'Magento version')
58
            ->addOption('magentoVersionByName', null, InputOption::VALUE_OPTIONAL, 'Magento version name instead of order number')
59
            ->addOption('installationFolder', null, InputOption::VALUE_OPTIONAL, 'Installation folder')
60
            ->addOption('dbHost', null, InputOption::VALUE_OPTIONAL, 'Database host')
61
            ->addOption('dbUser', null, InputOption::VALUE_OPTIONAL, 'Database user')
62
            ->addOption('dbPass', null, InputOption::VALUE_OPTIONAL, 'Database password')
63
            ->addOption('dbName', null, InputOption::VALUE_OPTIONAL, 'Database name')
64
            ->addOption('dbPort', null, InputOption::VALUE_OPTIONAL, 'Database port', 3306)
65
            ->addOption('dbPrefix', null, InputOption::VALUE_OPTIONAL, 'Table prefix', '')
66
            ->addOption('installSampleData', null, InputOption::VALUE_OPTIONAL, 'Install sample data')
67
            ->addOption('useDefaultConfigParams', null, InputOption::VALUE_OPTIONAL, 'Use default installation parameters defined in the yaml file')
68
            ->addOption('baseUrl', null, InputOption::VALUE_OPTIONAL, 'Installation base url')
69
            ->addOption('replaceHtaccessFile', null, InputOption::VALUE_OPTIONAL, 'Generate htaccess file (for non vhost environment)')
70
            ->addOption(
71
                'noDownload',
72
                null,
73
                InputOption::VALUE_NONE,
74
                'If set skips download step. Used when installationFolder is already a Magento installation that has ' .
75
                'to be installed on the given database.'
76
            )
77
            ->addOption(
78
                'only-download',
79
                null,
80
                InputOption::VALUE_NONE,
81
                'Downloads (and extracts) source code'
82
            )
83
            ->addOption('forceUseDb', null, InputOption::VALUE_OPTIONAL, 'If --noDownload passed, force to use given database if it already exists.')
84
            ->setDescription('Install magento')
85
        ;
86
87
        $help = <<<HELP
88
* Download Magento by a list of git repos and zip files (mageplus, magelte, official community packages).
89
* Try to create database if it does not exist.
90
* Installs Magento sample data if available (since version 1.2.0).
91
* Starts Magento installer
92
* Sets rewrite base in .htaccess file
93
94
Example of an unattended Magento CE 1.7.0.2 installation:
95
96
   $ n98-magerun.phar install --dbHost="localhost" --dbUser="mydbuser" --dbPass="mysecret" --dbName="magentodb" --installSampleData=yes --useDefaultConfigParams=yes --magentoVersionByName="magento-ce-1.7.0.2" --installationFolder="magento" --baseUrl="http://magento.localdomain/"
97
98
Additionally, with --noDownload option you can install Magento working copy already stored in --installationFolder on
99
the given database.
100
101
See it in action: http://youtu.be/WU-CbJ86eQc
102
103
HELP;
104
        $this->setHelp($help);
105
106
        $this->notEmptyCallback = function($input)
107
        {
108
            if (empty($input)) {
109
                throw new InvalidArgumentException('Please enter a value');
110
            }
111
            return $input;
112
        };
113
    }
114
115
    /**
116
     * @return bool
117
     */
118
    public function isEnabled()
119
    {
120
        return Exec::allowed();
121
    }
122
123
    /**
124
     * @param InputInterface $input
125
     * @param OutputInterface $output
126
     * @throws RuntimeException
127
     * @return int|null|void
128
     */
129
    protected function execute(InputInterface $input, OutputInterface $output)
130
    {
131
        $this->commandConfig = $this->getCommandConfig();
132
        $this->writeSection($output, 'Magento Installation');
133
134
        $this->precheckPhp();
135
136
        if (!$input->getOption('noDownload')) {
137
            $this->selectMagentoVersion($input, $output);
138
        }
139
140
        $this->chooseInstallationFolder($input, $output);
141
142
        if (!$input->getOption('noDownload')) {
143
            $result = $this->downloadMagento($input, $output);
144
145
            if ($result === false) {
146
                return 1;
147
            }
148
        }
149
150
        if ($input->getOption('only-download')) {
151
            return 0;
152
        }
153
154
        $this->createDatabase($input, $output);
155
156
        if (!$input->getOption('noDownload')) {
157
            $this->installSampleData($input, $output);
158
        }
159
160
        $this->removeEmptyFolders();
161
        $this->setDirectoryPermissions($output);
162
        $this->installMagento($input, $output, $this->config['installationFolder']);
163
    }
164
165
    /**
166
     * Check PHP environment agains minimal required settings modules
167
     */
168
    protected function precheckPhp()
169
    {
170
        $extensions = $this->commandConfig['installation']['pre-check']['php']['extensions'];
171
        $missingExtensions = array();
172
        foreach ($extensions as $extension) {
173
            if (!extension_loaded($extension)) {
174
                $missingExtensions[] = $extension;
175
            }
176
        }
177
178
        if (count($missingExtensions) > 0) {
179
            throw new RuntimeException(
180
                'The following PHP extensions are required to start installation: ' . implode(',', $missingExtensions)
181
            );
182
        }
183
    }
184
185
    /**
186
     * @param InputInterface  $input
187
     * @param OutputInterface $output
188
     *
189
     * @throws InvalidArgumentException
190
     */
191
    protected function selectMagentoVersion(InputInterface $input, OutputInterface $output)
192
    {
193
        if ($input->getOption('magentoVersion') == null && $input->getOption('magentoVersionByName') == null) {
194
            $question = array();
195
            foreach ($this->commandConfig['magento-packages'] as $key => $package) {
196
                $question[] = '<comment>' . str_pad('[' . ($key + 1) . ']', 4, ' ') . '</comment> ' . $package['name'] . "\n";
197
            }
198
            $question[] = "<question>Choose a magento version:</question> ";
199
200
            $commandConfig = $this->commandConfig;
201
202
203
            $type = $this->getHelper('dialog')->askAndValidate($output, $question, function($typeInput) use ($commandConfig) {
204
                if (!in_array($typeInput, range(1, count($commandConfig['magento-packages'])))) {
205
                    throw new InvalidArgumentException('Invalid type');
206
                }
207
208
                return $typeInput;
209
            });
210
        } else {
211
            $type = null;
212
213
            if ($input->getOption('magentoVersion')) {
214
                $type = $input->getOption('magentoVersion');
215
                if ($type !== (string) (int) $type) {
216
                    $type = $this->getPackageNumberByName($type);
217
                }
218
            } elseif ($input->getOption('magentoVersionByName')) {
219
                $type = $this->getPackageNumberByName($input->getOption('magentoVersionByName'));
220
            }
221
222
            if ($type == null) {
223
                throw new InvalidArgumentException('Unable to locate Magento version');
224
            }
225
        }
226
227
        $magentoPackages = $this->commandConfig['magento-packages'];
228
229
        $index = $type - 1;
230
        if (!isset($magentoPackages[$index])) {
231
            throw new InvalidArgumentException(
232
                sprintf(
233
                    'Invalid Magento package number %s, must be from 1 to %d.', var_export($type, true),
234
                    count($magentoPackages)
235
                )
236
            );
237
        }
238
239
        $this->config['magentoVersionData'] = $magentoPackages[$index];
240
    }
241
242
243
    /**
244
     * @param $name
245
     *
246
     * @return int 1 or greater as the one-based package number, null on failure to resolve the name
247
     */
248
    private function getPackageNumberByName($name)
249
    {
250
        // directly filter integer strings
251
        if ($name === (string) (int) $name) {
252
            return (int) $name;
253
        }
254
255
        $magentoPackages = $this->commandConfig['magento-packages'];
256
257
        foreach ($magentoPackages as $key => $package) {
258
            if ($package['name'] === $name) {
259
                return $key + 1;
260
            }
261
        }
262
263
        return null;
264
    }
265
266
    /**
267
     * @param InputInterface $input
268
     * @param OutputInterface $output
269
     * @return bool
270
     */
271
    public function downloadMagento(InputInterface $input, OutputInterface $output)
272
    {
273
        try {
274
            $package = $this->createComposerPackageByConfig($this->config['magentoVersionData']);
275
            $this->config['magentoPackage'] = $package;
276
277
            if (file_exists($this->config['installationFolder'] . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'Mage.php')) {
278
                $output->writeln('<error>A magento installation already exists in this folder </error>');
279
                return false;
280
            }
281
282
            $composer = $this->getComposer($input, $output);
283
            $targetFolder = $this->getTargetFolderByType($composer, $package, $this->config['installationFolder']);
284
            $this->config['magentoPackage'] = $this->downloadByComposerConfig(
285
                $input,
286
                $output,
287
                $package,
288
                $targetFolder,
289
                true
290
            );
291
292
            if ($this->isSourceTypeRepository($package->getSourceType())) {
293
                $filesystem = new \N98\Util\Filesystem;
294
                $filesystem->recursiveCopy($targetFolder, $this->config['installationFolder'], array('.git', '.hg'));
295
            } else {
296
                $filesystem = new \Composer\Util\Filesystem();
297
                $filesystem->copyThenRemove(
298
                    $this->config['installationFolder'] . '/_n98_magerun_download', $this->config['installationFolder']
299
                );
300
            }
301
302
            if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
303
                // Patch installer
304
                $this->patchMagentoInstallerForPHP54($this->config['installationFolder']);
305
            }
306
        } catch (Exception $e) {
307
            $output->writeln('<error>' . $e->getMessage() . '</error>');
308
            return false;
309
        }
310
311
        return true;
312
    }
313
314
    /**
315
     * construct a folder to where magerun will download the source to, cache git/hg repositories under COMPOSER_HOME
316
     *
317
     * @param Composer $composer
318
     * @param CompletePackage $package
319
     * @param $installationFolder
320
     *
321
     * @return string
322
     */
323
    protected function getTargetFolderByType(Composer $composer, CompletePackage $package, $installationFolder)
324
    {
325
        $type = $package->getSourceType();
326
        if ($this->isSourceTypeRepository($type)) {
327
            $targetPath = sprintf(
328
                '%s/%s/%s/%s',
329
                $composer->getConfig()->get('cache-dir'),
330
                '_n98_magerun_download',
331
                $type,
332
                preg_replace('{[^a-z0-9.]}i', '-', $package->getSourceUrl())
333
            );
334
        } else {
335
            $targetPath = sprintf(
336
                '%s/%s',
337
                $installationFolder,
338
                '_n98_magerun_download'
339
            );
340
        }
341
        return $targetPath;
342
    }
343
344
    /**
345
     * @param string $magentoFolder
346
     */
347
    protected function patchMagentoInstallerForPHP54($magentoFolder)
348
    {
349
        $installerConfig = $magentoFolder
350
            . DIRECTORY_SEPARATOR
351
            . 'app/code/core/Mage/Install/etc/config.xml';
352
        if (file_exists($installerConfig)) {
353
            $xml = file_get_contents($installerConfig);
354
            file_put_contents($installerConfig, str_replace('<pdo_mysql/>', '<pdo_mysql>1</pdo_mysql>', $xml));
355
        }
356
    }
357
358
    /**
359
     * @param InputInterface  $input
360
     * @param OutputInterface $output
361
     *
362
     * @throws InvalidArgumentException
363
     */
364
    protected function createDatabase(InputInterface $input, OutputInterface $output)
365
    {
366
        $dbOptions = array('--dbHost', '--dbUser', '--dbPass', '--dbName');
367
        $dbOptionsFound = 0;
368
        foreach ($dbOptions as $dbOption) {
369
            foreach ($this->getCliArguments() as $definedCliOption) {
370
                if (BinaryString::startsWith($definedCliOption, $dbOption)) {
371
                    $dbOptionsFound++;
372
                }
373
            }
374
        }
375
376
        $hasAllOptions = $dbOptionsFound == 4;
377
378
        // if all database options were passed in at cmd line
379
        if ($hasAllOptions) {
380
            $this->config['db_host'] = $input->getOption('dbHost');
381
            $this->config['db_user'] = $input->getOption('dbUser');
382
            $this->config['db_pass'] = $input->getOption('dbPass');
383
            $this->config['db_name'] = $input->getOption('dbName');
384
            $this->config['db_port'] = $input->getOption('dbPort');
385
            $this->config['db_prefix'] = $input->getOption('dbPrefix');
386
            $db = $this->validateDatabaseSettings($output, $input);
387
388
            if ($db === false) {
389
                throw new InvalidArgumentException("Database configuration is invalid");
390
            }
391
392
        } else {
393
            $dialog = $this->getHelperSet()->get('dialog');
394
            do {
395
                $dbHostDefault = $input->getOption('dbHost') ? $input->getOption('dbHost') : 'localhost';
396
                $this->config['db_host'] = $dialog->askAndValidate($output, '<question>Please enter the database host</question> <comment>[' . $dbHostDefault . ']</comment>: ', $this->notEmptyCallback, false, $dbHostDefault);
397
398
                $dbUserDefault = $input->getOption('dbUser') ? $input->getOption('dbUser') : 'root';
399
                $this->config['db_user'] = $dialog->askAndValidate($output, '<question>Please enter the database username</question> <comment>[' . $dbUserDefault . ']</comment>: ', $this->notEmptyCallback, false, $dbUserDefault);
400
401
                $dbPassDefault = $input->getOption('dbPass') ? $input->getOption('dbPass') : '';
402
                $this->config['db_pass'] = $dialog->ask($output, '<question>Please enter the database password</question> <comment>[' . $dbPassDefault . ']</comment>: ', $dbPassDefault);
403
404
                $dbNameDefault = $input->getOption('dbName') ? $input->getOption('dbName') : 'magento';
405
                $this->config['db_name'] = $dialog->askAndValidate($output, '<question>Please enter the database name</question> <comment>[' . $dbNameDefault . ']</comment>: ', $this->notEmptyCallback, false, $dbNameDefault);
406
407
                $dbPortDefault = $input->getOption('dbPort') ? $input->getOption('dbPort') : 3306;
408
                $this->config['db_port'] = $dialog->askAndValidate($output, '<question>Please enter the database port </question> <comment>[' . $dbPortDefault . ']</comment>: ', $this->notEmptyCallback, false, $dbPortDefault);
409
410
                $dbPrefixDefault = $input->getOption('dbPrefix') ? $input->getOption('dbPrefix') : '';
411
                $this->config['db_prefix'] = $dialog->ask($output, '<question>Please enter the table prefix</question> <comment>[' . $dbPrefixDefault . ']</comment>:', $dbPrefixDefault);
412
                $db = $this->validateDatabaseSettings($output, $input);
413
            } while ($db === false);
414
        }
415
416
        $this->config['db'] = $db;
417
    }
418
419
    /**
420
     * @param OutputInterface $output
421
     * @param InputInterface  $input
422
     *
423
     * @return bool|PDO
424
     */
425
    protected function validateDatabaseSettings(OutputInterface $output, InputInterface $input)
426
    {
427
        try {
428
            $dsn = sprintf("mysql:host=%s;port=%s", $this->config['db_host'], $this->config['db_port']);
429
            $db = new PDO($dsn, $this->config['db_user'], $this->config['db_pass']);
430
            if (!$db->query('USE ' . $this->config['db_name'])) {
431
                $db->query("CREATE DATABASE `" . $this->config['db_name'] . "`");
432
                $output->writeln('<info>Created database ' . $this->config['db_name'] . '</info>');
433
                $db->query('USE ' . $this->config['db_name']);
434
435
                return $db;
436
            }
437
438
            if ($input->getOption('noDownload') && !$input->getOption('forceUseDb')) {
439
                $output->writeln("<error>Database {$this->config['db_name']} already exists.</error>");
440
441
                return false;
442
            }
443
444
            return $db;
445
        } catch (PDOException $e) {
446
            $output->writeln('<error>' . $e->getMessage() . '</error>');
447
        }
448
449
        return false;
450
    }
451
452
    /**
453
     * @param InputInterface $input
454
     * @param OutputInterface $output
455
     */
456
    protected function installSampleData(InputInterface $input, OutputInterface $output)
457
    {
458
        $magentoPackage = $this->config['magentoPackage']; /* @var $magentoPackage \Composer\Package\MemoryPackage */
459
        $extra = $magentoPackage->getExtra();
460
        if (!isset($extra['sample-data'])) {
461
            return;
462
        }
463
464
        $dialog = $this->getHelperSet()->get('dialog');
465
466
        $installSampleData = ($input->getOption('installSampleData') !== null) ? $this->_parseBoolOption($input->getOption('installSampleData')) : $dialog->askConfirmation($output, '<question>Install sample data?</question> <comment>[y]</comment>: ');
467
468
        if ($installSampleData) {
469
            $filesystem = new Filesystem();
470
471
            foreach ($this->commandConfig['demo-data-packages'] as $demoPackageData) {
472
                if ($demoPackageData['name'] == $extra['sample-data']) {
473
                    $package = $this->downloadByComposerConfig(
474
                        $input,
475
                        $output,
476
                        $demoPackageData,
477
                        $this->config['installationFolder'] . '/_temp_demo_data',
478
                        false
479
                    );
480
481
                    $this->_fixComposerExtractionBug();
482
483
                    $expandedFolder = $this->config['installationFolder']
484
                                    . '/_temp_demo_data/'
485
                                    . str_replace(array('.tar.gz', '.tar.bz2', '.zip'), '', basename($package->getDistUrl()));
486
                    if (is_dir($expandedFolder)) {
487
                        $filesystem->recursiveCopy(
488
                            $expandedFolder,
489
                            $this->config['installationFolder']
490
                        );
491
                        $filesystem->recursiveRemoveDirectory($expandedFolder);
492
                    }
493
494
                    // Remove empty folder
495 View Code Duplication
                    if (is_dir($this->config['installationFolder'] . '/vendor/composer')) {
496
                        $filesystem->recursiveRemoveDirectory($this->config['installationFolder'] . '/vendor/composer');
497
                    }
498
499
                    // Install sample data
500
                    $sampleDataSqlFile = glob($this->config['installationFolder'] . '/_temp_demo_data/magento_*sample_data*sql');
501
                    $db = $this->config['db'];
502
                    /* @var $db PDO */
503
                    if (isset($sampleDataSqlFile[0])) {
504
                        if (OperatingSystem::isProgramInstalled('mysql')) {
505
                            $exec = 'mysql '
506
                                . '-h' . escapeshellarg(strval($this->config['db_host']))
507
                                . ' '
508
                                . '-u' . escapeshellarg(strval($this->config['db_user']))
509
                                . ' '
510
                                . ($this->config['db_port'] != '3306' ? '-P' . escapeshellarg($this->config['db_port']) . ' ' : '')
511
                                . (!strval($this->config['db_pass'] == '') ? '-p' . escapeshellarg($this->config['db_pass']) . ' ' : '')
512
                                . strval($this->config['db_name'])
513
                                . ' < '
514
                                . escapeshellarg($sampleDataSqlFile[0]);
515
                            $output->writeln('<info>Importing <comment>' . $sampleDataSqlFile[0] . '</comment> with mysql cli client</info>');
516
                            Exec::run($exec);
517
                            @unlink($sampleDataSqlFile[0]);
518
                        } else {
519
                            $output->writeln('<info>Importing <comment>' . $sampleDataSqlFile[0] . '</comment> with PDO driver</info>');
520
                            // Fallback -> Try to install dump file by PDO driver
521
                            $dbUtils = new DatabaseUtils();
522
                            $dbUtils->importSqlDump($db, $sampleDataSqlFile[0]);
523
                        }
524
                    }
525
                }
526
            }
527
528 View Code Duplication
            if (is_dir($this->config['installationFolder'] . '/_temp_demo_data')) {
529
                $filesystem->recursiveRemoveDirectory($this->config['installationFolder'] . '/_temp_demo_data');
530
            }
531
        }
532
    }
533
534
    protected function _fixComposerExtractionBug()
535
    {
536
        $filesystem = new Filesystem();
537
        foreach (array('/_temp_demo_data/media' => '/media', '/_temp_demo_data/skin' => '/skin') as $wrong => $right) {
538
            $wrongFolder = $this->config['installationFolder'] . $wrong;
539
            $rightFolder = $this->config['installationFolder'] . $right;
540
            if (is_dir($wrongFolder)) {
541
                $filesystem->recursiveCopy(
542
                    $wrongFolder,
543
                    $rightFolder
544
                );
545
                $filesystem->recursiveRemoveDirectory($wrongFolder);
546
            }
547
        }
548
    }
549
550
    /**
551
     * Remove empty composer extraction folder
552
     */
553
    protected function removeEmptyFolders()
554
    {
555
        if (is_dir(getcwd() . '/vendor')) {
556
            $finder = new Finder();
557
            $finder->files()->depth(3)->in(getcwd() . '/vendor');
558
            if ($finder->count() == 0) {
559
                $filesystem = new Filesystem();
560
                $filesystem->recursiveRemoveDirectory(getcwd() . '/vendor');
561
            }
562
        }
563
    }
564
565
    /**
566
     * @param InputInterface  $input
567
     * @param OutputInterface $output
568
     *
569
     * @return array
570
     * @throws InvalidArgumentException parameter mismatch (e.g. base-url components like hostname)
571
     * @throws RuntimeException
572
     */
573
    protected function installMagento(InputInterface $input, OutputInterface $output)
574
    {
575
        $this->getApplication()->setAutoExit(false);
576
        $dialog = $this->getHelperSet()->get('dialog');
577
578
        $defaults = $this->commandConfig['installation']['defaults'];
579
580
        $useDefaultConfigParams = $this->_parseBoolOption($input->getOption('useDefaultConfigParams'));
581
582
        $sessionSave = $useDefaultConfigParams ? $defaults['session_save'] : $dialog->ask(
583
            $output,
584
            '<question>Please enter the session save:</question> <comment>[' . $defaults['session_save'] . ']</comment>: ',
585
            $defaults['session_save']
586
        );
587
588
        $adminFrontname = $useDefaultConfigParams ? $defaults['admin_frontname'] : $dialog->askAndValidate(
589
            $output,
590
            '<question>Please enter the admin frontname:</question> <comment>[' . $defaults['admin_frontname'] . ']</comment> ',
591
            $this->notEmptyCallback,
592
            false,
593
            $defaults['admin_frontname']
594
        );
595
596
        $currency = $useDefaultConfigParams ? $defaults['currency'] : $dialog->askAndValidate(
597
            $output,
598
            '<question>Please enter the default currency code:</question> <comment>[' . $defaults['currency'] . ']</comment>: ',
599
            $this->notEmptyCallback,
600
            false,
601
            $defaults['currency']
602
        );
603
604
        $locale = $useDefaultConfigParams ? $defaults['locale'] : $dialog->askAndValidate(
605
            $output,
606
            '<question>Please enter the locale code:</question> <comment>[' . $defaults['locale'] . ']</comment>: ',
607
            $this->notEmptyCallback,
608
            false,
609
            $defaults['locale']
610
        );
611
612
        $timezone = $useDefaultConfigParams ? $defaults['timezone'] : $dialog->askAndValidate(
613
            $output,
614
            '<question>Please enter the timezone:</question> <comment>[' . $defaults['timezone'] . ']</comment>: ',
615
            $this->notEmptyCallback,
616
            false,
617
            $defaults['timezone']
618
        );
619
620
        $adminUsername = $useDefaultConfigParams ? $defaults['admin_username'] : $dialog->askAndValidate(
621
            $output,
622
            '<question>Please enter the admin username:</question> <comment>[' . $defaults['admin_username'] . ']</comment>: ',
623
            $this->notEmptyCallback,
624
            false,
625
            $defaults['admin_username']
626
        );
627
628
        $adminPassword = $useDefaultConfigParams ? $defaults['admin_password'] : $dialog->askAndValidate(
629
            $output,
630
            '<question>Please enter the admin password:</question> <comment>[' . $defaults['admin_password'] . ']</comment>: ',
631
            $this->notEmptyCallback,
632
            false,
633
            $defaults['admin_password']
634
        );
635
636
        $adminFirstname = $useDefaultConfigParams ? $defaults['admin_firstname'] : $dialog->askAndValidate(
637
            $output,
638
            '<question>Please enter the admin\'s firstname:</question> <comment>[' . $defaults['admin_firstname'] . ']</comment>: ',
639
            $this->notEmptyCallback,
640
            false,
641
            $defaults['admin_firstname']
642
        );
643
644
        $adminLastname = $useDefaultConfigParams ? $defaults['admin_lastname'] : $dialog->askAndValidate(
645
            $output,
646
            '<question>Please enter the admin\'s lastname:</question> <comment>[' . $defaults['admin_lastname'] . ']</comment>: ',
647
            $this->notEmptyCallback,
648
            false,
649
            $defaults['admin_lastname']
650
        );
651
652
        $adminEmail = $useDefaultConfigParams ? $defaults['admin_email'] : $dialog->askAndValidate(
653
            $output,
654
            '<question>Please enter the admin\'s email:</question> <comment>[' . $defaults['admin_email'] . ']</comment>: ',
655
            $this->notEmptyCallback,
656
            false,
657
            $defaults['admin_email']
658
        );
659
660
        $validateBaseUrl = function($input) {
661
            if (!preg_match('|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i', $input)) {
662
                throw new InvalidArgumentException('Please enter a valid URL');
663
            }
664
            if (parse_url($input, \PHP_URL_HOST) == 'localhost') {
665
                throw new InvalidArgumentException('localhost cause problems! Please use 127.0.0.1 or another hostname');
666
            }
667
            return $input;
668
        };
669
670
        $baseUrl = ($input->getOption('baseUrl') !== null) ? $input->getOption('baseUrl') : $dialog->askAndValidate(
671
            $output,
672
            '<question>Please enter the base url:</question> ',
673
            $validateBaseUrl,
674
            false
675
        );
676
        $baseUrl = rtrim($baseUrl, '/') . '/'; // normalize baseUrl
677
678
        /**
679
         * Correct session save (common mistake)
680
         */
681
        if ($sessionSave == 'file') {
682
            $sessionSave = 'files';
683
        }
684
685
        /**
686
         * Try to create session folder
687
         */
688
        $defaultSessionFolder = $this->config['installationFolder'] . DIRECTORY_SEPARATOR . 'var/session';
689
        if ($sessionSave == 'files' && !is_dir($defaultSessionFolder)) {
690
            @mkdir($defaultSessionFolder);
691
        }
692
693
        $dbHost = $this->config['db_host'];
694
        if ($this->config['db_port'] != 3306) {
695
            $dbHost .= ':' . $this->config['db_port'];
696
        }
697
698
        $argv = array(
699
            'license_agreement_accepted' => 'yes',
700
            'locale'                     => $locale,
701
            'timezone'                   => $timezone,
702
            'db_host'                    => $dbHost,
703
            'db_name'                    => $this->config['db_name'],
704
            'db_user'                    => $this->config['db_user'],
705
            'db_pass'                    => $this->config['db_pass'],
706
            'db_prefix'                  => $this->config['db_prefix'],
707
            'url'                        => $baseUrl,
708
            'use_rewrites'               => 'yes',
709
            'use_secure'                 => 'no',
710
            'secure_base_url'            => '',
711
            'use_secure_admin'           => 'no',
712
            'admin_username'             => $adminUsername,
713
            'admin_lastname'             => $adminLastname,
714
            'admin_firstname'            => $adminFirstname,
715
            'admin_email'                => $adminEmail,
716
            'admin_password'             => $adminPassword,
717
            'session_save'               => $sessionSave,
718
            'admin_frontname'            => $adminFrontname, /* magento 1 */
719
            'backend_frontname'          => $adminFrontname, /* magento 2 */
720
            'default_currency'           => $currency,
721
            'skip_url_validation'        => 'yes',
722
        );
723
        if ($useDefaultConfigParams) {
724
            if (strlen($defaults['encryption_key']) > 0) {
725
                $argv['encryption_key'] = $defaults['encryption_key'];
726
            }
727
            if (strlen($defaults['use_secure']) > 0) {
728
                $argv['use_secure'] = $defaults['use_secure'];
729
                $argv['secure_base_url'] = str_replace('http://', 'https://', $baseUrl);
730
            }
731
            if (strlen($defaults['use_rewrites']) > 0) {
732
                $argv['use_rewrites'] = $defaults['use_rewrites'];
733
            }
734
        }
735
        $installArgs = '';
736
        foreach ($argv as $argName => $argValue) {
737
            $installArgs .= '--' . $argName . ' ' . escapeshellarg($argValue) . ' ';
738
        }
739
740
        $output->writeln('<info>Start installation process.</info>');
741
742
        if (OperatingSystem::isWindows()) {
743
            $installCommand = 'php -f ' . escapeshellarg($this->getInstallScriptPath()) . ' -- ' . $installArgs;
744
        } else {
745
            $installCommand = '/usr/bin/env php -f ' . escapeshellarg($this->getInstallScriptPath()) . ' -- ' . $installArgs;
0 ignored issues
show
This line exceeds maximum limit of 120 characters; contains 125 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
746
        }
747
        $output->writeln('<comment>' . $installCommand . '</comment>');
748
        Exec::run($installCommand, $installationOutput, $returnStatus);
749
        if ($returnStatus !== self::EXEC_STATUS_OK) {
750
            $this->getApplication()->setAutoExit(true);
751
            throw new RuntimeException('Installation failed.' . $installationOutput, 1);
752
        } else {
753
            $output->writeln('<info>Successfully installed Magento</info>');
754
            $encryptionKey = trim(substr($installationOutput, strpos($installationOutput, ':') + 1));
755
            $output->writeln('<comment>Encryption Key:</comment> <info>' . $encryptionKey . '</info>');
756
        }
757
758
        $dialog = $this->getHelperSet()->get('dialog');
759
760
        /**
761
         * Htaccess file
762
         */
763
        if ($input->getOption('useDefaultConfigParams') == null || $input->getOption('replaceHtaccessFile') != null) {
764
            $replaceHtaccessFile = false;
765
766
            if ($this->_parseBoolOption($input->getOption('replaceHtaccessFile'))) {
767
                $replaceHtaccessFile = true;
768
            } elseif ($dialog->askConfirmation(
769
                $output,
770
                '<question>Write BaseURL to .htaccess file?</question> <comment>[n]</comment>: ',
771
                false)
772
            ) {
773
                $replaceHtaccessFile = true;
774
            }
775
776
            if ($replaceHtaccessFile) {
777
                $this->replaceHtaccessFile($baseUrl);
778
            }
779
        }
780
781
        \chdir($this->config['installationFolder']);
782
        $this->getApplication()->reinit();
783
        $output->writeln('<info>Reindex all after installation</info>');
784
        $this->getApplication()->run(new StringInput('index:reindex:all'), $output);
785
        $this->getApplication()->run(new StringInput('sys:check'), $output);
786
        $output->writeln('<info>Successfully installed magento</info>');
787
    }
788
789
    /**
790
     * Check if we have a magento 2 or 1 installation and return path to install.php
791
     *
792
     * @return string
793
     */
794
    protected function getInstallScriptPath()
795
    {
796
        $magento1InstallScriptPath  = $this->config['installationFolder'] . DIRECTORY_SEPARATOR . 'install.php';
797
        $magento2InstallScriptPath  = $this->config['installationFolder'] . DIRECTORY_SEPARATOR . 'dev/shell/install.php';
798
        if (file_exists($magento2InstallScriptPath)) {
799
            return $magento2InstallScriptPath;
800
        }
801
802
        return $magento1InstallScriptPath;
803
    }
804
805
    /**
806
     * @param string $baseUrl
807
     */
808
    protected function replaceHtaccessFile($baseUrl)
809
    {
810
        $content = file_get_contents($this->config['installationFolder'] . DIRECTORY_SEPARATOR . '.htaccess');
811
        copy($this->config['installationFolder'] . DIRECTORY_SEPARATOR . '.htaccess', $this->config['installationFolder'] . DIRECTORY_SEPARATOR . '.htaccess.dist');
812
        $content = str_replace('#RewriteBase /magento/', 'RewriteBase ' . parse_url($baseUrl, PHP_URL_PATH), $content);
813
        file_put_contents($this->config['installationFolder'] . DIRECTORY_SEPARATOR . '.htaccess', $content);
814
    }
815
816
    /**
817
     * @param OutputInterface $output
818
     */
819
    protected function setDirectoryPermissions($output)
820
    {
821
        try {
822
            $varFolder = $this->config['installationFolder'] . DIRECTORY_SEPARATOR . 'var';
823
            if (!is_dir($varFolder)) {
824
                @mkdir($varFolder);
825
            }
826
            @chmod($varFolder, 0777);
827
828
            $varCacheFolder = $this->config['installationFolder'] . DIRECTORY_SEPARATOR . 'var/cache';
829
            if (!is_dir($varCacheFolder)) {
830
                @mkdir($varCacheFolder);
831
            }
832
            @chmod($varCacheFolder, 0777);
833
834
            $mediaFolder = $this->config['installationFolder'] . DIRECTORY_SEPARATOR . 'media';
835
            if (!is_dir($mediaFolder)) {
836
                @mkdir($mediaFolder);
837
            }
838
            @chmod($mediaFolder, 0777);
839
840
            $finder = Finder::create();
841
            $finder->directories()
842
                ->ignoreUnreadableDirs(true)
843
                ->in(array($varFolder, $mediaFolder));
844
            foreach ($finder as $dir) {
845
                @chmod($dir->getRealpath(), 0777);
846
            }
847
        } catch (Exception $e) {
848
            $output->writeln('<error>' . $e->getMessage() . '</error>');
849
        }
850
    }
851
852
    /**
853
     * @return array
854
     */
855
    public function getCliArguments()
856
    {
857
        if ($this->_argv === null) {
858
            $this->_argv = $_SERVER['argv'];
859
        }
860
861
        return $this->_argv;
862
    }
863
864
    /**
865
     * @param array $args
866
     */
867
    public function setCliArguments($args)
868
    {
869
        $this->_argv = $args;
870
    }
871
}
872