Completed
Pull Request — master (#177)
by
unknown
04:33
created

AbstractMagentoCommand::run()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
dl 0
loc 8
rs 9.4285
c 1
b 0
f 1
cc 1
eloc 4
nc 1
nop 2
1
<?php
2
3
namespace N98\Magento\Command;
4
5
use Composer\Package\PackageInterface;
6
use Magento\Framework\ObjectManager\ObjectManager;
7
use N98\Magento\Command\SubCommand\ConfigBag;
8
use N98\Magento\Command\SubCommand\SubCommandFactory;
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_2 = 2;
30
31
    /**
32
     * @var string
33
     */
34
    protected $_magentoRootFolder = null;
35
36
    /**
37
     * @var int
38
     */
39
    protected $_magentoMajorVersion = self::MAGENTO_MAJOR_VERSION_2;
40
41
    /**
42
     * @var bool
43
     */
44
    protected $_magentoEnterprise = false;
45
46
    /**
47
     * @var array
48
     */
49
    protected $_deprecatedAlias = array();
50
51
    /**
52
     * @var array
53
     */
54
    protected $_websiteCodeMap = array();
55
56
    /**
57
     * @var ObjectManager
58
     */
59
    protected $_objectManager = null;
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
    /**
76
     * @return ObjectManager
77
     */
78
    protected function getObjectManager()
79
    {
80
        return $this->getApplication()->getObjectManager();
81
    }
82
83
    /**
84
     * @param array $codeArgument
85
     * @param bool  $status
86
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
87
     */
88
    protected function saveCacheStatus($codeArgument, $status)
89
    {
90
        $cacheTypes = $this->_getCacheModel()->getTypes();
0 ignored issues
show
Bug introduced by
The method _getCacheModel() does not seem to exist on object<N98\Magento\Comma...AbstractMagentoCommand>.

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

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

Loading history...
91
        $enable = \Mage::app()->useCache();
92
        foreach ($cacheTypes as $cacheCode => $cacheModel) {
93
            if (empty($codeArgument) || in_array($cacheCode, $codeArgument)) {
94
                $enable[$cacheCode] = $status ? 1 : 0;
95
            }
96
        }
97
98
        \Mage::app()->saveUseCache($enable);
99
    }
100
101
    private function _initWebsites()
102
    {
103
        $this->_websiteCodeMap = array();
104
        /** @var \Mage_Core_Model_Website[] $websites */
105
        $websites = \Mage::app()->getWebsites(false);
106
        foreach ($websites as $website) {
107
            $this->_websiteCodeMap[$website->getId()] = $website->getCode();
108
        }
109
    }
110
111
    /**
112
     * @param int $websiteId
113
     * @return string
114
     */
115
    protected function _getWebsiteCodeById($websiteId)
116
    {
117
        if (empty($this->_websiteCodeMap)) {
118
            $this->_initWebsites();
119
        }
120
121
        if (isset($this->_websiteCodeMap[$websiteId])) {
122
            return $this->_websiteCodeMap[$websiteId];
123
        }
124
125
        return '';
126
    }
127
128
    /**
129
     * @param string $websiteCode
130
     * @return int
131
     */
132
    protected function _getWebsiteIdByCode($websiteCode)
133
    {
134
        if (empty($this->_websiteCodeMap)) {
135
            $this->_initWebsites();
136
        }
137
        $websiteMap = array_flip($this->_websiteCodeMap);
138
139
        return $websiteMap[$websiteCode];
140
    }
141
142
    /**
143
     * @param string|null $commandClass
144
     * @return array
145
     */
146
    protected function getCommandConfig($commandClass = null)
147
    {
148
        if ($commandClass == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $commandClass of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
149
            $commandClass = get_class($this);
150
        }
151
        $configArray = $this->getApplication()->getConfig();
152
        if (isset($configArray['commands'][$commandClass])) {
153
            return $configArray['commands'][$commandClass];
154
        }
155
156
        return null;
157
    }
158
159
    /**
160
     * @param \Symfony\Component\Console\Output\OutputInterface $output
161
     * @param string $text
162
     * @param string $style
163
     */
164
    protected function writeSection(OutputInterface $output, $text, $style = 'bg=blue;fg=white')
165
    {
166
        $output->writeln(array(
167
            '',
168
            $this->getHelperSet()->get('formatter')->formatBlock($text, $style, true),
169
            '',
170
        ));
171
    }
172
173
    /**
174
     * Bootstrap magento shop
175
     *
176
     * @return bool
177
     */
178
    protected function initMagento()
179
    {
180
        $init = $this->getApplication()->initMagento();
181
        if ($init) {
182
            $this->_magentoRootFolder = $this->getApplication()->getMagentoRootFolder();
183
        }
184
185
        return $init;
186
    }
187
188
    /**
189
     * Search for magento root folder
190
     *
191
     * @param OutputInterface $output
192
     * @param bool $silent print debug messages
193
     * @throws \RuntimeException
194
     */
195
    public function detectMagento(OutputInterface $output, $silent = true)
196
    {
197
        $this->getApplication()->detectMagento();
198
199
        $this->_magentoEnterprise = $this->getApplication()->isMagentoEnterprise();
200
        $this->_magentoRootFolder = $this->getApplication()->getMagentoRootFolder();
201
        $this->_magentoMajorVersion = $this->getApplication()->getMagentoMajorVersion();
202
203
        if (!$silent) {
204
            $editionString = ($this->_magentoEnterprise ? ' (Enterprise Edition) ' : '');
205
            $output->writeln('<info>Found Magento '. $editionString . 'in folder "' . $this->_magentoRootFolder . '"</info>');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 126 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...
206
        }
207
208
        if (!empty($this->_magentoRootFolder)) {
209
            return;
210
        }
211
212
        throw new \RuntimeException('Magento folder could not be detected');
213
    }
214
215
    /**
216
     * Die if not Enterprise
217
     */
218
    protected function requireEnterprise(OutputInterface $output)
219
    {
220
        if (!$this->_magentoEnterprise) {
221
            $output->writeln('<error>Enterprise Edition is required but was not detected</error>');
222
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method requireEnterprise() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
223
        }
224
    }
225
226
    /**
227
     * @return \Mage_Core_Helper_Data
228
     */
229
    protected function getCoreHelper()
230
    {
231
        if ($this->_magentoMajorVersion == self::MAGENTO_MAJOR_VERSION_2) {
232
            return \Mage::helper('Mage_Core_Helper_Data');
233
        }
234
        return \Mage::helper('core');
235
    }
236
237
    /**
238
     * @param InputInterface $input
239
     * @param OutputInterface $output
240
     * @return \Composer\Downloader\DownloadManager
241
     */
242
    public function getComposerDownloadManager($input, $output)
243
    {
244
        return $this->getComposer($input, $output)->getDownloadManager();
245
    }
246
247
    /**
248
     * @param array|PackageInterface $config
249
     * @return \Composer\Package\CompletePackage
250
     */
251
    public function createComposerPackageByConfig($config)
252
    {
253
        $packageLoader = new PackageLoader();
254
        return $packageLoader->load($config);
0 ignored issues
show
Bug introduced by
It seems like $config defined by parameter $config on line 251 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...
255
    }
256
257
    /**
258
     * @param InputInterface $input
259
     * @param OutputInterface $output
260
     * @param array|PackageInterface $config
261
     * @param string $targetFolder
262
     * @param bool $preferSource
263
     * @return \Composer\Package\CompletePackage
264
     */
265
    public function downloadByComposerConfig(InputInterface $input, OutputInterface $output, $config, $targetFolder,
266
        $preferSource = true
267
    ) {
268
        $dm = $this->getComposerDownloadManager($input, $output);
269
        if (! $config instanceof PackageInterface) {
270
            $package = $this->createComposerPackageByConfig($config);
271
        } else {
272
            $package = $config;
273
        }
274
275
        $helper = new \N98\Util\Console\Helper\MagentoHelper();
276
        $helper->detect($targetFolder);
277
        if ($this->isSourceTypeRepository($package->getSourceType()) && $helper->getRootFolder() == $targetFolder) {
278
            $package->setInstallationSource('source');
279
            $this->checkRepository($package, $targetFolder);
280
            $dm->update($package, $package, $targetFolder);
281
        } else {
282
            $dm->download($package, $targetFolder, $preferSource);
283
        }
284
285
        return $package;
286
    }
287
288
    /**
289
     * brings locally cached repository up to date if it is missing the requested tag
290
     *
291
     * @param $package
292
     * @param $targetFolder
293
     */
294
    protected function checkRepository($package, $targetFolder)
295
    {
296
        if ($package->getSourceType() == 'git') {
297
            $command = sprintf(
298
                'cd %s && git rev-parse refs/tags/%s',
299
                escapeshellarg($targetFolder),
300
                escapeshellarg($package->getSourceReference())
301
            );
302
            $existingTags = shell_exec($command);
303
            if (!$existingTags) {
304
                $command = sprintf('cd %s && git fetch', escapeshellarg($targetFolder));
305
                shell_exec($command);
306
            }
307
        } elseif ($package->getSourceType() == 'hg') {
308
            $command = sprintf(
309
                'cd %s && hg log --template "{tags}" -r %s',
310
                escapeshellarg($targetFolder),
311
                escapeshellarg($package->getSourceReference())
312
            );
313
            $existingTag =  shell_exec($command);
314
            if ($existingTag === $package->getSourceReference()) {
315
                $command = sprintf('cd %s && hg pull', escapeshellarg($targetFolder));
316
                shell_exec($command);
317
            }
318
        }
319
    }
320
321
    /**
322
     * @param string $type
323
     *
324
     * @return bool
325
     */
326
    public function isSourceTypeRepository($type)
327
    {
328
        return in_array($type, array('git', 'hg'));
329
    }
330
331
    /**
332
     * obtain composer
333
     *
334
     * @param InputInterface  $input
335
     * @param OutputInterface $output
336
     *
337
     * @return \Composer\Composer
338
     */
339
    public function getComposer(InputInterface $input, OutputInterface $output)
340
    {
341
        $io = new ConsoleIO($input, $output, $this->getHelperSet());
0 ignored issues
show
Bug introduced by
It seems like $this->getHelperSet() 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...
342
        return ComposerFactory::create($io, array());
343
    }
344
345
    /**
346
     * @param string $alias
347
     * @param string $message
348
     * @return AbstractMagentoCommand
349
     */
350
    protected function addDeprecatedAlias($alias, $message)
351
    {
352
        $this->_deprecatedAlias[$alias] = $message;
353
354
        return $this;
355
    }
356
357
    /**
358
     * @param InputInterface $input
359
     * @param OutputInterface $output
360
     */
361
    protected function checkDeprecatedAliases(InputInterface $input, OutputInterface $output)
362
    {
363
        if (isset($this->_deprecatedAlias[$input->getArgument('command')])) {
364
            $output->writeln('<error>Deprecated:</error> <comment>' . $this->_deprecatedAlias[$input->getArgument('command')] . '</comment>');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 142 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...
365
        }
366
    }
367
368
    /**
369
     * @param string $value
370
     * @return bool
371
     */
372
    protected function _parseBoolOption($value)
373
    {
374
        return in_array(strtolower($value), array('y', 'yes', 1, 'true'));
375
    }
376
377
    /**
378
     * @param string $value
379
     * @return bool
380
     */
381
    public function parseBoolOption($value)
382
    {
383
        return $this->_parseBoolOption($value);
384
    }
385
386
    /**
387
     * @param string $value
388
     * @return string
389
     */
390
    public function formatActive($value)
391
    {
392
        if (in_array($value, array(1, 'true'))) {
393
            return 'active';
394
        }
395
396
        return 'inactive';
397
    }
398
399
    /**
400
     * @param InputInterface  $input
401
     * @param OutputInterface $output
402
     *
403
     * @return int
404
     */
405
    public function run(InputInterface $input, OutputInterface $output)
406
    {
407
        $this->getHelperSet()->setCommand($this);
408
409
        $this->injectObjects($output);
410
411
        return parent::run($input, $output);
412
    }
413
414
    /**
415
     * @param OutputInterface $output
416
     */
417
    public function injectObjects(OutputInterface $output)
418
    {
419
        /* @var $injectionHelper InjectionHelper */
420
        if (method_exists($this, 'inject')) {
421
            $this->detectMagento($output);
422
            $this->initMagento();
423
            $injectionHelper = $this->getHelper('injection');
424
            $injectionHelper->methodInjection(
425
                $this,
426
                'inject',
427
                $this->getObjectManager()
428
            );
429
        }
430
    }
431
432
    /**
433
     * @param InputInterface  $input
434
     * @param OutputInterface $output
435
     * @param string $baseNamespace If this is set we can use relative class names.
436
     *
437
     * @return SubCommandFactory
438
     */
439
    protected function createSubCommandFactory(
440
        InputInterface $input,
441
        OutputInterface $output,
442
        $baseNamespace = ''
443
    ) {
444
        $configBag = new ConfigBag();
445
446
        $commandConfig = $this->getCommandConfig();
447
        if (empty($commandConfig)) {
448
            $commandConfig = array();
449
        }
450
451
        return new SubCommandFactory(
452
            $this,
453
            $baseNamespace,
454
            $input,
455
            $output,
456
            $commandConfig,
457
            $configBag
458
        );
459
    }
460
}
461