Completed
Push — master ( bd5921...d9293e )
by Christian
17:54 queued 08:59
created

src/N98/Magento/Command/AbstractMagentoCommand.php (1 issue)

Labels
Severity

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;
4
5
use Composer\Package\PackageInterface;
6
use InvalidArgumentException;
7
use N98\Util\OperatingSystem;
8
use RuntimeException;
9
use Symfony\Component\Console\Command\Command;
10
use Symfony\Component\Console\Input\InputInterface;
11
use Symfony\Component\Console\Output\OutputInterface;
12
use Composer\Package\Loader\ArrayLoader as PackageLoader;
13
use Composer\Factory as ComposerFactory;
14
use Composer\IO\ConsoleIO;
15
use N98\Util\Console\Helper\MagentoHelper;
16
17
/**
18
 * Class AbstractMagentoCommand
19
 *
20
 * @package N98\Magento\Command
21
 *
22
 * @method \N98\Magento\Application getApplication() getApplication()
23
 */
24
abstract class AbstractMagentoCommand extends Command
25
{
26
    /**
27
     * @var int
28
     */
29
    const MAGENTO_MAJOR_VERSION_1 = 1;
30
31
    /**
32
     * @var int
33
     */
34
    const MAGENTO_MAJOR_VERSION_2 = 2;
35
36
    /**
37
     * @var string
38
     */
39
    protected $_magentoRootFolder = null;
40
41
    /**
42
     * @var int
43
     */
44
    protected $_magentoMajorVersion = self::MAGENTO_MAJOR_VERSION_1;
45
46
    /**
47
     * @var bool
48
     */
49
    protected $_magentoEnterprise = false;
50
51
    /**
52
     * @var array
53
     */
54
    protected $_deprecatedAlias = array();
55
56
    /**
57
     * @var array
58
     */
59
    protected $_websiteCodeMap = array();
60
61
    /**
62
     * Initializes the command just after the input has been validated.
63
     *
64
     * This is mainly useful when a lot of commands extends one main command
65
     * where some things need to be initialized based on the input arguments and options.
66
     *
67
     * @param InputInterface  $input  An InputInterface instance
68
     * @param OutputInterface $output An OutputInterface instance
69
     */
70
    protected function initialize(InputInterface $input, OutputInterface $output)
71
    {
72
        $this->checkDeprecatedAliases($input, $output);
73
    }
74
75
    private function _initWebsites()
76
    {
77
        $this->_websiteCodeMap = array();
78
        /** @var \Mage_Core_Model_Website[] $websites */
79
        $websites = \Mage::app()->getWebsites(false);
80
        foreach ($websites as $website) {
81
            $this->_websiteCodeMap[$website->getId()] = $website->getCode();
82
        }
83
    }
84
85
    /**
86
     * @param int $websiteId
87
     * @return string
88
     */
89
    protected function _getWebsiteCodeById($websiteId)
90
    {
91
        if (empty($this->_websiteCodeMap)) {
92
            $this->_initWebsites();
93
        }
94
95
        if (isset($this->_websiteCodeMap[$websiteId])) {
96
            return $this->_websiteCodeMap[$websiteId];
97
        }
98
99
        return '';
100
    }
101
102
    /**
103
     * @param string $websiteCode
104
     * @return int
105
     */
106
    protected function _getWebsiteIdByCode($websiteCode)
107
    {
108
        if (empty($this->_websiteCodeMap)) {
109
            $this->_initWebsites();
110
        }
111
        $websiteMap = array_flip($this->_websiteCodeMap);
112
113
        return $websiteMap[$websiteCode];
114
    }
115
116
    /**
117
     * @param string|null $commandClass
118
     * @return array
119
     */
120
    protected function getCommandConfig($commandClass = null)
121
    {
122
        if (null === $commandClass) {
123
            $commandClass = get_class($this);
124
        }
125
126
        $configArray = $this->getApplication()->getConfig();
127
        if (isset($configArray['commands'][$commandClass])) {
128
            return $configArray['commands'][$commandClass];
129
        }
130
131
        return null;
132
    }
133
134
    /**
135
     * @param OutputInterface $output
136
     * @param string $text
137
     * @param string $style
138
     */
139
    protected function writeSection(OutputInterface $output, $text, $style = 'bg=blue;fg=white')
140
    {
141
        $output->writeln(array(
142
            '',
143
            $this->getHelperSet()->get('formatter')->formatBlock($text, $style, true),
144
            '',
145
        ));
146
    }
147
148
    /**
149
     * Bootstrap magento shop
150
     *
151
     * @param bool $soft
152
     * @return bool
153
     */
154
    protected function initMagento($soft = false)
155
    {
156
        $init = $this->getApplication()->initMagento($soft);
157
        if ($init) {
158
            $this->_magentoRootFolder = $this->getApplication()->getMagentoRootFolder();
159
        }
160
161
        return $init;
162
    }
163
164
    /**
165
     * Search for magento root folder
166
     *
167
     * @param OutputInterface $output
168
     * @param bool $silent print debug messages
169
     * @throws RuntimeException
170
     */
171
    public function detectMagento(OutputInterface $output, $silent = true)
172
    {
173
        $this->getApplication()->detectMagento();
174
175
        $this->_magentoEnterprise = $this->getApplication()->isMagentoEnterprise();
176
        $this->_magentoRootFolder = $this->getApplication()->getMagentoRootFolder();
177
        $this->_magentoMajorVersion = $this->getApplication()->getMagentoMajorVersion();
178
179
        if (!$silent) {
180
            $editionString = ($this->_magentoEnterprise ? ' (Enterprise Edition) ' : '');
181
            $output->writeln('<info>Found Magento ' . $editionString . 'in folder "' . $this->_magentoRootFolder . '"</info>');
182
        }
183
184
        if (!empty($this->_magentoRootFolder)) {
185
            return;
186
        }
187
188
        throw new RuntimeException('Magento folder could not be detected');
189
    }
190
191
    /**
192
     * Die if not Enterprise
193
     */
194
    protected function requireEnterprise(OutputInterface $output)
195
    {
196
        if (!$this->_magentoEnterprise) {
197
            $output->writeln('<error>Enterprise Edition is required but was not detected</error>');
198
            exit;
199
        }
200
    }
201
202
    /**
203
     * @return \Mage_Core_Helper_Data
204
     */
205
    protected function getCoreHelper()
206
    {
207
        if ($this->_magentoMajorVersion == self::MAGENTO_MAJOR_VERSION_2) {
208
            return \Mage::helper('Mage_Core_Helper_Data');
209
        }
210
        return \Mage::helper('core');
211
    }
212
213
    /**
214
     * @param InputInterface $input
215
     * @param OutputInterface $output
216
     * @return \Composer\Downloader\DownloadManager
217
     */
218
    protected function getComposerDownloadManager($input, $output)
219
    {
220
        return $this->getComposer($input, $output)->getDownloadManager();
221
    }
222
223
    /**
224
     * @param array|PackageInterface $config
225
     * @return \Composer\Package\CompletePackage
226
     */
227
    protected function createComposerPackageByConfig($config)
228
    {
229
        $packageLoader = new PackageLoader();
230
        return $packageLoader->load($config);
0 ignored issues
show
It seems like $config defined by parameter $config on line 227 can also be of type object<Composer\Package\PackageInterface>; however, Composer\Package\Loader\ArrayLoader::load() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
231
    }
232
233
    /**
234
     * @param InputInterface $input
235
     * @param OutputInterface $output
236
     * @param array|PackageInterface $config
237
     * @param string $targetFolder
238
     * @param bool $preferSource
239
     * @return \Composer\Package\CompletePackage
240
     */
241
    protected function downloadByComposerConfig(InputInterface $input, OutputInterface $output, $config, $targetFolder,
242
        $preferSource = true
243
    ) {
244
        $dm = $this->getComposerDownloadManager($input, $output);
245
        if (!$config instanceof PackageInterface) {
246
            $package = $this->createComposerPackageByConfig($config);
247
        } else {
248
            $package = $config;
249
        }
250
251
        $helper = new \N98\Util\Console\Helper\MagentoHelper();
252
        $helper->detect($targetFolder);
253
        if ($this->isSourceTypeRepository($package->getSourceType()) && $helper->getRootFolder() == $targetFolder) {
254
            $package->setInstallationSource('source');
255
            $this->checkRepository($package, $targetFolder);
256
            $dm->update($package, $package, $targetFolder);
257
        } else {
258
            $dm->download($package, $targetFolder, $preferSource);
259
        }
260
261
        return $package;
262
    }
263
264
    /**
265
     * brings locally cached repository up to date if it is missing the requested tag
266
     *
267
     * @param PackageInterface $package
268
     * @param string $targetFolder
269
     */
270
    protected function checkRepository($package, $targetFolder)
271
    {
272
        if ($package->getSourceType() == 'git') {
273
            $command = sprintf(
274
                'cd %s && git rev-parse refs/tags/%s',
275
                escapeshellarg($this->normalizePath($targetFolder)),
276
                escapeshellarg($package->getSourceReference())
277
            );
278
            $existingTags = shell_exec($command);
279
            if (!$existingTags) {
280
                $command = sprintf('cd %s && git fetch', escapeshellarg($this->normalizePath($targetFolder)));
281
                shell_exec($command);
282
            }
283
        } elseif ($package->getSourceType() == 'hg') {
284
            $command = sprintf(
285
                'cd %s && hg log --template "{tags}" -r %s',
286
                escapeshellarg($targetFolder),
287
                escapeshellarg($package->getSourceReference())
288
            );
289
            $existingTag = shell_exec($command);
290
            if ($existingTag === $package->getSourceReference()) {
291
                $command = sprintf('cd %s && hg pull', escapeshellarg($targetFolder));
292
                shell_exec($command);
293
            }
294
        }
295
    }
296
297
    /**
298
     * normalize paths on windows / cygwin / msysgit
299
     *
300
     * when using a path value that has been created in a cygwin shell but then PHP uses it inside a cmd shell it needs
301
     * to be filtered.
302
     *
303
     * @param  string $path
304
     * @return string
305
     */
306
    protected function normalizePath($path)
307
    {
308
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
309
            $path = strtr($path, '/', '\\');
310
        }
311
        return $path;
312
    }
313
314
    /**
315
     * obtain composer
316
     *
317
     * @param InputInterface  $input
318
     * @param OutputInterface $output
319
     *
320
     * @return \Composer\Composer
321
     */
322
    protected function getComposer(InputInterface $input, OutputInterface $output)
323
    {
324
        $io = new ConsoleIO($input, $output, $this->getHelperSet());
325
        return ComposerFactory::create($io, array());
326
    }
327
328
    /**
329
     * @param string $alias
330
     * @param string $message
331
     * @return AbstractMagentoCommand
332
     */
333
    protected function addDeprecatedAlias($alias, $message)
334
    {
335
        $this->_deprecatedAlias[$alias] = $message;
336
337
        return $this;
338
    }
339
340
    /**
341
     * @param InputInterface $input
342
     * @param OutputInterface $output
343
     */
344
    protected function checkDeprecatedAliases(InputInterface $input, OutputInterface $output)
345
    {
346
        if (isset($this->_deprecatedAlias[$input->getArgument('command')])) {
347
            $output->writeln('<error>Deprecated:</error> <comment>' . $this->_deprecatedAlias[$input->getArgument('command')] . '</comment>');
348
        }
349
    }
350
351
    /**
352
     * Magento 1 / 2 switches
353
     *
354
     * @param string $mage1code Magento 1 class code
355
     * @param string $mage2class Magento 2 class name
356
     * @return \Mage_Core_Model_Abstract
357
     */
358
    protected function _getModel($mage1code, $mage2class)
359
    {
360
        if ($this->_magentoMajorVersion == self::MAGENTO_MAJOR_VERSION_2) {
361
            return \Mage::getModel($mage2class);
362
        } else {
363
            return \Mage::getModel($mage1code);
364
        }
365
    }
366
367
    /**
368
     * Magento 1 / 2 switches
369
     *
370
     * @param string $mage1code Magento 1 class code
371
     * @param string $mage2class Magento 2 class name
372
     * @return \Mage_Core_Model_Abstract
373
     */
374
    protected function _getResourceModel($mage1code, $mage2class)
375
    {
376
        if ($this->_magentoMajorVersion == self::MAGENTO_MAJOR_VERSION_2) {
377
            return \Mage::getResourceModel($mage2class);
378
        } else {
379
            return \Mage::getResourceModel($mage1code);
380
        }
381
    }
382
383
    /**
384
     * Magento 1 / 2 switches
385
     *
386
     * @param string $mage1code Magento 1 class code
387
     * @param string $mage2class Magento 2 class name
388
     * @return \Mage_Core_Model_Abstract
389
     */
390
    protected function _getResourceSingleton($mage1code, $mage2class)
391
    {
392
        if ($this->_magentoMajorVersion == self::MAGENTO_MAJOR_VERSION_2) {
393
            return \Mage::getResourceSingleton($mage2class);
394
        } else {
395
            return \Mage::getResourceSingleton($mage1code);
396
        }
397
    }
398
399
    /**
400
     * @param string $value
401
     * @return bool
402
     */
403
    protected function _parseBoolOption($value)
404
    {
405
        return in_array(strtolower($value), array('y', 'yes', 1, 'true'));
406
    }
407
408
    /**
409
     * @param string $value
410
     * @return string
411
     */
412
    protected function formatActive($value)
413
    {
414
        if (in_array($value, array(1, 'true'))) {
415
            return 'active';
416
        }
417
418
        return 'inactive';
419
    }
420
421
    /**
422
     * @param InputInterface  $input
423
     * @param OutputInterface $output
424
     *
425
     * @return int
426
     */
427
    public function run(InputInterface $input, OutputInterface $output)
428
    {
429
        $this->getHelperSet()->setCommand($this);
430
431
        return parent::run($input, $output);
432
    }
433
434
    /**
435
     * @param InputInterface $input
436
     * @param OutputInterface $output
437
     */
438
    protected function chooseInstallationFolder(InputInterface $input, OutputInterface $output)
439
    {
440
        /**
441
         * @param string $folderName
442
         *
443
         * @return string
444
         */
445
        $validateInstallationFolder = function($folderName) use ($input) {
446
447
            $folderName = rtrim(trim($folderName, ' '), '/');
448
            // resolve folder-name to current working directory if relative
449
            if (substr($folderName, 0, 1) == '.') {
450
                $cwd = OperatingSystem::getCwd();
451
                $folderName = $cwd . substr($folderName, 1);
452
            }
453
454
            if (empty($folderName)) {
455
                throw new InvalidArgumentException('Installation folder cannot be empty');
456
            }
457
458
            if (!is_dir($folderName)) {
459
                if (!@mkdir($folderName, 0777, true)) {
460
                    throw new InvalidArgumentException('Cannot create folder.');
461
                }
462
463
                return $folderName;
464
            }
465
466
            if ($input->hasOption('noDownload') && $input->getOption('noDownload')) {
467
                /** @var MagentoHelper $magentoHelper */
468
                $magentoHelper = new MagentoHelper();
469
                $magentoHelper->detect($folderName);
470
                if ($magentoHelper->getRootFolder() !== $folderName) {
471
                    throw new InvalidArgumentException(
472
                        sprintf(
473
                            'Folder %s is not a Magento working copy.',
474
                            $folderName
475
                        )
476
                    );
477
                }
478
479
                $localXml = $folderName . '/app/etc/local.xml';
480
                if (file_exists($localXml)) {
481
                    throw new InvalidArgumentException(
482
                        sprintf(
483
                            'Magento working copy in %s seems already installed. Please remove %s and retry.',
484
                            $folderName,
485
                            $localXml
486
                        )
487
                    );
488
                }
489
            }
490
491
            return $folderName;
492
        };
493
494
        if (($installationFolder = $input->getOption('installationFolder')) == null) {
495
            $defaultFolder = './magento';
496
            $question[] = "<question>Enter installation folder:</question> [<comment>" . $defaultFolder . "</comment>]";
497
498
            $installationFolder = $this->getHelper('dialog')->askAndValidate($output, $question, $validateInstallationFolder, false, $defaultFolder);
499
500
        } else {
501
            // @Todo improve validation and bring it to 1 single function
502
            $installationFolder = $validateInstallationFolder($installationFolder);
503
504
        }
505
506
        $this->config['installationFolder'] = realpath($installationFolder);
507
        \chdir($this->config['installationFolder']);
508
    }
509
510
    /**
511
     * @param string $type
512
     *
513
     * @return bool
514
     */
515
    protected function isSourceTypeRepository($type)
516
    {
517
        return in_array($type, array('git', 'hg'));
518
    }
519
520
    /**
521
     * @param string $argument
522
     * @param InputInterface $input
523
     * @param OutputInterface $output
524
     * @param string $message
525
     * @return string
526
     */
527
    protected function getOrAskForArgument($argument, InputInterface $input, OutputInterface $output, $message = null)
528
    {
529
        $inputArgument = $input->getArgument($argument);
530
        if ($inputArgument === null) {
531
532
            $message = $this->getArgumentMessage($argument, $message);
533
534
            /** @var  $dialog  \Symfony\Component\Console\Helper\DialogHelper */
535
            $dialog = $this->getHelperSet()->get('dialog');
536
            return $dialog->ask($output, $message);
537
        }
538
539
        return $inputArgument;
540
    }
541
542
    /**
543
     * @param array           $entries zero-indexed array of entries (represented by strings) to select from
544
     * @param OutputInterface $output
545
     * @param string          $question
546
     */
547
    protected function askForArrayEntry(array $entries, OutputInterface $output, $question)
548
    {
549
        $dialog = '';
550
        foreach ($entries as $key => $entry) {
551
            $dialog .= '<comment>[' . ($key + 1) . ']</comment> ' . $entry . "\n";
552
        }
553
        $dialog .= "<question>{$question}</question> ";
554
555
        $selected = $this->getHelper('dialog')->askAndValidate($output, $dialog, function($typeInput) use ($entries) {
556
            if (!in_array($typeInput, range(1, count($entries)))) {
557
                throw new InvalidArgumentException('Invalid type');
558
            }
559
560
            return $typeInput;
561
        });
562
563
        return $entries[$selected - 1];
564
    }
565
566
    /**
567
     * @param string $argument
568
     * @param string $message [optional]
569
     * @return string
570
     */
571
    protected function getArgumentMessage($argument, $message = null)
572
    {
573
        if (null === $message) {
574
            $message = ucfirst($argument);
575
        }
576
577
        return sprintf('<question>%s:</question>', $message);
578
    }
579
}
580