Completed
Push — master ( 383d0c...3523cb )
by Tom
04:14
created

Application::checkConfigCommandAlias()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 24
rs 8.5126
cc 6
eloc 15
nc 6
nop 1
1
<?php
2
3
namespace N98\Magento;
4
5
use Composer\Autoload\ClassLoader;
6
use Magento\Framework\ObjectManager\ObjectManager;
7
use Magento\Mtf\EntryPoint\EntryPoint;
8
use N98\Magento\Application\Config;
9
use N98\Magento\Application\Console\Events;
10
use N98\Magento\Application\ConfigurationLoader;
11
use N98\Util\Console\Helper\TwigHelper;
12
use N98\Util\Console\Helper\MagentoHelper;
13
use N98\Util\OperatingSystem;
14
use Symfony\Component\Console\Application as BaseApplication;
15
use Symfony\Component\Console\Command\Command;
16
use Symfony\Component\Console\Event\ConsoleEvent;
17
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
18
use Symfony\Component\Console\Input\ArgvInput;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Input\InputOption;
21
use Symfony\Component\Console\Output\ConsoleOutput;
22
use Symfony\Component\Console\Output\NullOutput;
23
use Symfony\Component\Console\Output\OutputInterface;
24
use Symfony\Component\EventDispatcher\EventDispatcher;
25
use UnexpectedValueException;
26
27
class Application extends BaseApplication
28
{
29
    /**
30
     * @var string
31
     */
32
    const APP_NAME = 'n98-magerun2';
33
34
    /**
35
     * @var string
36
     */
37
    const APP_VERSION = '1.1.4';
38
39
    /**
40
     * @var int
41
     */
42
    const MAGENTO_MAJOR_VERSION_1 = 1;
43
44
    /**
45
     * @var int
46
     */
47
    const MAGENTO_MAJOR_VERSION_2 = 2;
48
49
    /**
50
     * @var string
51
     */
52
    private static $logo = "
53
          ____  ____                                                   ___ 
54
   ____  / __ \\( __ )      ____ ___  ____ _____ ____  _______  ______ |__ \\
55
  / __ \\/ /_/ / __  |_____/ __ `__ \\/ __ `/ __ `/ _ \\/ ___/ / / / __ \\__/ /
56
 / / / /\\__, / /_/ /_____/ / / / / / /_/ / /_/ /  __/ /  / /_/ / / / / __/
57
/_/ /_//____/\\____/     /_/ /_/ /_/\\__,_/\\__, /\\___/_/   \\__,_/_/ /_/____/
58
                                        /____/                             
59
";
60
    /**
61
     * @var ClassLoader
62
     */
63
    protected $autoloader;
64
65
    /**
66
     * @var Config
67
     */
68
    protected $config;
69
70
    /**
71
     * @var string
72
     */
73
    protected $_magentoRootFolder = null;
74
75
    /**
76
     * @var bool
77
     */
78
    protected $_magentoEnterprise = false;
79
80
    /**
81
     * @var int
82
     */
83
    protected $_magentoMajorVersion = self::MAGENTO_MAJOR_VERSION_2;
84
85
    /**
86
     * @var EntryPoint
87
     */
88
    protected $_magento2EntryPoint = null;
89
90
    /**
91
     * @var bool
92
     */
93
    protected $_isPharMode = false;
94
95
    /**
96
     * @var bool
97
     */
98
    protected $_magerunStopFileFound = false;
99
100
    /**
101
     * @var string
102
     */
103
    protected $_magerunStopFileFolder = null;
104
105
    /**
106
     * @var bool
107
     */
108
    protected $_isInitialized = false;
109
110
    /**
111
     * @var EventDispatcher
112
     */
113
    protected $dispatcher;
114
115
    /**
116
     * If root dir is set by root-dir option this flag is true
117
     *
118
     * @var bool
119
     */
120
    protected $_directRootDir = false;
121
122
    /**
123
     * @var bool
124
     */
125
    protected $_magentoDetected = false;
126
127
    /**
128
     * @var ObjectManager
129
     */
130
    protected $_objectManager = null;
131
132
    /**
133
     * @param ClassLoader $autoloader
0 ignored issues
show
Documentation introduced by
Should the type for parameter $autoloader not be ClassLoader|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
134
     */
135
    public function __construct($autoloader = null)
136
    {
137
        $this->autoloader = $autoloader;
138
        parent::__construct(self::APP_NAME, self::APP_VERSION);
139
    }
140
141
    /**
142
     * @return \Symfony\Component\Console\Input\InputDefinition|void
143
     */
144
    protected function getDefaultInputDefinition()
145
    {
146
        $inputDefinition = parent::getDefaultInputDefinition();
147
        $rootDirOption   = new InputOption(
148
            '--root-dir',
149
            '',
150
            InputOption::VALUE_OPTIONAL,
151
            'Force magento root dir. No auto detection'
152
        );
153
        $inputDefinition->addOption($rootDirOption);
154
155
        $skipExternalConfig = new InputOption(
156
            '--skip-config',
157
            '',
158
            InputOption::VALUE_OPTIONAL,
159
            'Do not load any custom config.'
160
        );
161
        $inputDefinition->addOption($skipExternalConfig);
162
163
        $skipExternalConfig = new InputOption(
164
            '--skip-root-check',
165
            '',
166
            InputOption::VALUE_OPTIONAL,
167
            'Do not check if n98-magerun runs as root'
168
        );
169
        $inputDefinition->addOption($skipExternalConfig);
170
171
        $skipMagento2CoreCommands = new InputOption(
172
            '--skip-core-commands',
173
            '',
174
            InputOption::VALUE_OPTIONAL,
175
            'Do not include Magento 2 core commands'
176
        );
177
        $inputDefinition->addOption($skipMagento2CoreCommands);
178
179
        return $inputDefinition;
180
    }
181
182
    /**
183
     * Search for magento root folder
184
     *
185
     * @param InputInterface  $input  [optional]
1 ignored issue
show
Documentation introduced by
Should the type for parameter $input not be null|InputInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
186
     * @param OutputInterface $output [optional]
0 ignored issues
show
Documentation introduced by
Should the type for parameter $output not be null|OutputInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
187
     */
188
    public function detectMagento(InputInterface $input = null, OutputInterface $output = null)
189
    {
190
        // do not detect magento twice
191
        if ($this->_magentoDetected) {
192
            return;
193
        }
194
195
        if ($this->getMagentoRootFolder() === null) {
196
            $this->_checkRootDirOption();
197
            if (function_exists('exec')) {
198
                if (OperatingSystem::isWindows()) {
199
                    $folder = exec('@echo %cd%'); // @TODO not currently tested!!!
200
                } else {
201
                    $folder = exec('pwd');
202
                }
203
            } else {
204
                $folder = getcwd();
205
            }
206
        } else {
207
            $folder = $this->getMagentoRootFolder();
208
        }
209
210
        $this->getHelperSet()->set(new MagentoHelper($input, $output), 'magento');
211
        $magentoHelper = $this->getHelperSet()->get('magento');
212
        /* @var $magentoHelper MagentoHelper */
213
        if (!$this->_directRootDir) {
214
            $subFolders = $this->config->getDetectSubFolders();
215
        } else {
216
            $subFolders = array();
217
        }
218
219
        $this->_magentoDetected = $magentoHelper->detect($folder, $subFolders);
220
        $this->_magentoRootFolder = $magentoHelper->getRootFolder();
221
        $this->_magentoEnterprise = $magentoHelper->isEnterpriseEdition();
222
        $this->_magentoMajorVersion = $magentoHelper->getMajorVersion();
223
        $this->_magerunStopFileFound = $magentoHelper->isMagerunStopFileFound();
224
        $this->_magerunStopFileFolder = $magentoHelper->getMagerunStopFileFolder();
225
    }
226
227
    /**
228
     * Add own helpers to helperset.
229
     *
230
     * @return void
231
     */
232
    protected function registerHelpers()
233
    {
234
        $helperSet = $this->getHelperSet();
235
        $config = $this->config->getConfig();
236
237
        // Twig
238
        $twigBaseDirs = array(
239
            __DIR__ . '/../../../res/twig'
240
        );
241
        if (isset($config['twig']['baseDirs']) && is_array($config['twig']['baseDirs'])) {
242
            $twigBaseDirs = array_merge(array_reverse($config['twig']['baseDirs']), $twigBaseDirs);
243
        }
244
        $helperSet->set(new TwigHelper($twigBaseDirs), 'twig');
245
246
        foreach ($config['helpers'] as $helperName => $helperClass) {
247
            if (class_exists($helperClass)) {
248
                $helperSet->set(new $helperClass(), $helperName);
249
            }
250
        }
251
    }
252
253
    /**
254
     * Try to bootstrap magento 2 and load cli application
255
     *
256
     * @param OutputInterface $output
257
     */
258
    protected function registerMagentoCoreCommands(OutputInterface $output)
259
    {
260
        if ($this->getMagentoRootFolder()) {
261
            // Magento was found -> register core cli commands
262
            require_once $this->getMagentoRootFolder() . '/app/bootstrap.php';
263
264
            $coreCliApplication = new \Magento\Framework\Console\Cli();
265
            $coreCliApplicationCommands = $coreCliApplication->all();
266
267
            foreach ($coreCliApplicationCommands as $coreCliApplicationCommand) {
268
                $this->add($coreCliApplicationCommand);
269
270
                if (OutputInterface::VERBOSITY_DEBUG <= $output->getVerbosity()) {
271
                    $output->writeln(
272
                        '<debug>Added core command </debug><comment>'
273
                        . get_class($coreCliApplicationCommand) . '</comment>'
274
                    );
275
                }
276
            }
277
        }
278
    }
279
280
    /**
281
     * Override standard command registration. We want alias support.
282
     *
283
     * @param \Symfony\Component\Console\Command\Command $command
284
     * @return \Symfony\Component\Console\Command\Command
0 ignored issues
show
Documentation introduced by
Should the return type not be Command|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...
285
     */
286
    public function add(Command $command)
287
    {
288
        if ($this->config) {
289
            $this->config->registerConfigCommandAlias($command);
290
        }
291
292
        return parent::add($command);
293
    }
294
295
    /**
296
     * @param bool $mode
297
     */
298
    public function setPharMode($mode)
299
    {
300
        $this->_isPharMode = $mode;
301
    }
302
303
    /**
304
     * @return bool
305
     */
306
    public function isPharMode()
307
    {
308
        return $this->_isPharMode;
309
    }
310
311
    /**
312
     * @TODO Move logic into "EventSubscriber"
313
     *
314
     * @param OutputInterface $output
315
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be null|false?

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...
316
     */
317
    public function checkVarDir(OutputInterface $output)
318
    {
319
        if (OutputInterface::VERBOSITY_NORMAL <= $output->getVerbosity()) {
320
            $tempVarDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'magento' . DIRECTORY_SEPARATOR . 'var';
321
322
            if (is_dir($tempVarDir)) {
323
                $this->detectMagento(null, $output);
324
                /* If magento is not installed yet, don't check */
325
                if ($this->_magentoRootFolder === null
326
                    || !file_exists($this->_magentoRootFolder . '/app/etc/local.xml')
327
                ) {
328
                    return;
329
                }
330
331
                try {
332
                    $this->initMagento();
333
                } catch (\Exception $e) {
334
                    $message = 'Cannot initialize Magento. Please check your configuration. '
335
                        . 'Some n98-magerun command will not work. Got message: ';
336
                    if (OutputInterface::VERBOSITY_VERY_VERBOSE <= $output->getVerbosity()) {
337
                        $message .= $e->getTraceAsString();
338
                    } else {
339
                        $message .= $e->getMessage();
340
                    }
341
                    $output->writeln($message);
342
343
                    return;
344
                }
345
346
                $configOptions = new \Mage_Core_Model_Config_Options();
347
                $currentVarDir = $configOptions->getVarDir();
348
349
                if ($currentVarDir == $tempVarDir) {
350
                    $output->writeln(sprintf('<warning>Fallback folder %s is used in n98-magerun</warning>',
351
                        $tempVarDir));
352
                    $output->writeln('');
353
                    $output->writeln(
354
                        'n98-magerun is using the fallback folder. If there is another folder configured for Magento, '
355
                        . 'this can cause serious problems.'
356
                    );
357
                    $output->writeln(
358
                        'Please refer to https://github.com/netz98/n98-magerun/wiki/File-system-permissions '
359
                        . 'for more information.'
360
                    );
361
                    $output->writeln('');
362
                } else {
363
                    $output->writeln(sprintf('<warning>Folder %s found, but not used in n98-magerun</warning>',
364
                        $tempVarDir));
365
                    $output->writeln('');
366
                    $output->writeln(
367
                        sprintf(
368
                            'This might cause serious problems. n98-magerun is using the configured var-folder '
369
                            . '<comment>%s</comment>', $currentVarDir
370
                        )
371
                    );
372
                    $output->writeln(
373
                        'Please refer to https://github.com/netz98/n98-magerun/wiki/File-system-permissions '
374
                        . 'for more information.'
375
                    );
376
                    $output->writeln('');
377
378
                    return false;
379
                }
380
            }
381
        }
382
    }
383
384
    public function initMagento()
385
    {
386
        if ($this->getMagentoRootFolder() !== null) {
387
            if ($this->_magentoMajorVersion == self::MAGENTO_MAJOR_VERSION_2) {
388
                $this->_initMagento2();
389
            } else {
390
                $this->_initMagento1();
391
            }
392
393
            return true;
394
        }
395
396
        return false;
397
    }
398
399
    /**
400
     * @return string
401
     */
402
    public function getHelp()
403
    {
404
        return self::$logo . parent::getHelp();
405
    }
406
407
    public function getLongVersion()
408
    {
409
        return parent::getLongVersion() . ' by <info>netz98 new media GmbH</info>';
410
    }
411
412
    /**
413
     * @return boolean
414
     */
415
    public function isMagentoEnterprise()
416
    {
417
        return $this->_magentoEnterprise;
418
    }
419
420
    /**
421
     * @return string
422
     */
423
    public function getMagentoRootFolder()
424
    {
425
        return $this->_magentoRootFolder;
426
    }
427
428
    /**
429
     * @param string $magentoRootFolder
430
     */
431
    public function setMagentoRootFolder($magentoRootFolder)
432
    {
433
        $this->_magentoRootFolder = $magentoRootFolder;
434
    }
435
436
    /**
437
     * @return int
438
     */
439
    public function getMagentoMajorVersion()
440
    {
441
        return $this->_magentoMajorVersion;
442
    }
443
444
    /**
445
     * @return ClassLoader
446
     */
447
    public function getAutoloader()
448
    {
449
        return $this->autoloader;
450
    }
451
452
    /**
453
     * @param ClassLoader $autoloader
454
     */
455
    public function setAutoloader(ClassLoader $autoloader)
456
    {
457
        $this->autoloader = $autoloader;
458
    }
459
460
    /**
461
     * @return array
462
     */
463
    public function getConfig()
464
    {
465
        return $this->config->getConfig();
466
    }
467
468
    /**
469
     * @param array $config
470
     */
471
    public function setConfig($config)
472
    {
473
        $this->config->setConfig($config);
474
    }
475
476
    /**
477
     * @return boolean
478
     */
479
    public function isMagerunStopFileFound()
480
    {
481
        return $this->_magerunStopFileFound;
482
    }
483
484
    /**
485
     * Runs the current application with possible command aliases
486
     *
487
     * @param InputInterface  $input  An Input instance
488
     * @param OutputInterface $output An Output instance
489
     *
490
     * @return integer 0 if everything went fine, or an error code
491
     */
492
    public function doRun(InputInterface $input, OutputInterface $output)
493
    {
494
        $event = new Application\Console\Event($this, $input, $output);
495
        $this->dispatcher->dispatch(Events::RUN_BEFORE, $event);
496
497
        /**
498
         * only for compatibility to old versions.
499
         */
500
        $event = new ConsoleEvent(new Command('dummy'), $input, $output);
501
        $this->dispatcher->dispatch('console.run.before', $event);
502
503
        $input = $this->config->checkConfigCommandAlias($input);
504
        if ($output instanceof ConsoleOutput) {
505
            $this->checkVarDir($output->getErrorOutput());
506
        }
507
508
        if (OutputInterface::VERBOSITY_DEBUG <= $output->getVerbosity()) {
509
            $output->writeln('DEBUG');
510
        }
511
512
        return parent::doRun($input, $output);
513
    }
514
515
    /**
516
     * @param InputInterface  $input  [optional]
1 ignored issue
show
Documentation introduced by
Should the type for parameter $input not be null|InputInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
517
     * @param OutputInterface $output [optional]
0 ignored issues
show
Documentation introduced by
Should the type for parameter $output not be null|OutputInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
518
     *
519
     * @return int
520
     */
521
    public function run(InputInterface $input = null, OutputInterface $output = null)
522
    {
523
        if (null === $input) {
524
            $input = new ArgvInput();
525
        }
526
527
        if (null === $output) {
528
            $output = new ConsoleOutput();
529
        }
530
        $this->_addOutputStyles($output);
531
        if ($output instanceof ConsoleOutput) {
532
            $this->_addOutputStyles($output->getErrorOutput());
533
        }
534
535
        $this->configureIO($input, $output);
536
537
        try {
538
            $this->init(array(), $input, $output);
539
        } catch (\Exception $e) {
540
            $output = new ConsoleOutput();
541
            $this->renderException($e, $output);
542
        }
543
544
        $return = parent::run($input, $output);
545
546
        // Fix for no return values -> used in interactive shell to prevent error output
547
        if ($return === null) {
548
            return 0;
549
        }
550
551
        return $return;
552
    }
553
554
    /**
555
     * @param array           $initConfig
556
     * @param InputInterface  $input
1 ignored issue
show
Documentation introduced by
Should the type for parameter $input not be null|InputInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
557
     * @param OutputInterface $output
0 ignored issues
show
Documentation introduced by
Should the type for parameter $output not be null|OutputInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
558
     *
559
     * @return void
560
     */
561
    public function init($initConfig = array(), InputInterface $input = null, OutputInterface $output = null)
562
    {
563
        if (!$this->_isInitialized) {
564
            // Suppress DateTime warnings
565
            date_default_timezone_set(@date_default_timezone_get());
566
567
            $loadExternalConfig = !$this->_checkSkipConfigOption();
568
569
            if ($output === null) {
570
                $output = new NullOutput();
571
            }
572
573
            if (null !== $this->config) {
574
                throw new UnexpectedValueException(sprintf('Config already initialized'));
575
            }
576
577
            $this->config = $config = new Config($initConfig, $this->isPharMode(), $output);
578
            $configLoader = $config->getLoader();
579
            $config->loadPartialConfig($loadExternalConfig);
580
            $this->detectMagento($input, $output);
581
            $configLoader->loadStageTwo($this->_magentoRootFolder, $loadExternalConfig, $this->_magerunStopFileFolder);
582
            $config->load();
583
584
            $this->dispatcher = new EventDispatcher();
585
            $this->setDispatcher($this->dispatcher);
586
587
            if ($autoloader = $this->autoloader) {
588
589
                /**
590
                 * Include commands shipped by Magento 2 core
591
                 */
592
                if (!$this->_checkSkipMagento2CoreCommandsOption()) {
593
                    $this->registerMagentoCoreCommands($output);
594
                }
595
596
                $this->config->registerCustomAutoloaders($autoloader);
597
                $this->registerEventSubscribers();
598
                $config->registerCustomCommands($this);
599
            }
600
            $this->registerHelpers();
601
602
603
            $this->_isInitialized = true;
604
        }
605
    }
606
607
    /**
608
     * @param array           $initConfig [optional]
609
     * @param InputInterface  $input      [optional]
1 ignored issue
show
Documentation introduced by
Should the type for parameter $input not be null|InputInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
610
     * @param OutputInterface $output     [optional]
0 ignored issues
show
Documentation introduced by
Should the type for parameter $output not be null|OutputInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
611
     */
612
    public function reinit($initConfig = array(), InputInterface $input = null, OutputInterface $output = null)
613
    {
614
        $this->_isInitialized = false;
615
        $this->init($initConfig, $input, $output);
616
    }
617
618
    /**
619
     * @return void
620
     */
621
    protected function registerEventSubscribers()
622
    {
623
        foreach ($this->config->getConfig()['event']['subscriber'] as $subscriberClass) {
624
            $subscriber = new $subscriberClass();
625
            $this->dispatcher->addSubscriber($subscriber);
626
        }
627
    }
628
629
    /**
630
     * @return bool
631
     */
632
    protected function _checkSkipConfigOption()
633
    {
634
        $skipConfigOption = getopt('', array('skip-config'));
635
636
        return count($skipConfigOption) > 0;
637
    }
638
639
    /**
640
     * @return bool
641
     */
642
    protected function _checkSkipMagento2CoreCommandsOption()
643
    {
644
        $skipConfigOption = getopt('', array('skip-core-commands'));
645
646
        getenv('MAGERUN_SKIP_CORE_COMMANDS') && $skipConfigOption[] = 1;
647
648
        return count($skipConfigOption) > 0;
649
    }
650
651
    /**
652
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|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...
653
     */
654
    protected function _checkRootDirOption()
655
    {
656
        $specialGlobalOptions = getopt('', array('root-dir:'));
657
658
        if (count($specialGlobalOptions) > 0) {
659
            if (isset($specialGlobalOptions['root-dir'][0])
660
                && $specialGlobalOptions['root-dir'][0] == '~'
661
            ) {
662
                $specialGlobalOptions['root-dir'] = OperatingSystem::getHomeDir() . substr($specialGlobalOptions['root-dir'],
0 ignored issues
show
Coding Style introduced by
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...
663
                        1);
664
            }
665
            $folder = realpath($specialGlobalOptions['root-dir']);
666
            $this->_directRootDir = true;
667
            if (is_dir($folder)) {
668
                chdir($folder);
669
670
                return;
671
            }
672
        }
673
    }
674
675
    protected function _initMagento1()
676
    {
677
        $magento1Hint = <<<'MAGENTO1HINT'
678
You are running a Magento 1.x instance. This version of n98-magerun is not compatible
679
with Magento 1.x. Please use n98-magerun (version 1) for this shop.
680
681
A current version of the software can be downloaded on github.
682
683
<info>Download with curl
684
------------------</info>
685
686
    <comment>curl -O https://files.magerun.net/n98-magerun.phar</comment>
687
688
<info>Download with wget
689
------------------</info>
690
691
    <comment>wget https://files.magerun.net/n98-magerun.phar</comment>
692
693
MAGENTO1HINT;
694
695
        $output = new ConsoleOutput();
696
697
        $output->writeln(array(
698
            '',
699
            $this->getHelperSet()->get('formatter')->formatBlock('Compatibility Notice', 'bg=blue;fg=white', true),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Helper\HelperInterface as the method formatBlock() does only exist in the following implementations of said interface: Symfony\Component\Console\Helper\FormatterHelper.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
700
            ''
701
        ));
702
703
        $output->writeln($magento1Hint);
704
        exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method _initMagento1() 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...
705
    }
706
707
    /**
708
     * @return void
709
     */
710
    protected function _initMagento2()
0 ignored issues
show
Coding Style introduced by
_initMagento2 uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
711
    {
712
        require_once $this->getMagentoRootFolder() . '/app/bootstrap.php';
713
714
        $params = $_SERVER;
715
        $params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE] = 'admin';
716
        $params[\Magento\Store\Model\Store::CUSTOM_ENTRY_POINT_PARAM] = true;
717
        $params['entryPoint'] = basename(__FILE__);
718
719
        $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $params);
720
        /** @var \Magento\Framework\App\Cron $app */
721
        $app = $bootstrap->createApplication('N98\Magento\Framework\App\Magerun', []);
722
        /* @var $app \N98\Magento\Framework\App\Magerun */
723
        $app->launch();
724
725
        $this->_objectManager = $app->getObjectManager();
0 ignored issues
show
Documentation Bug introduced by
It seems like $app->getObjectManager() of type object<Magento\Framework\ObjectManagerInterface> is incompatible with the declared type object<Magento\Framework...tManager\ObjectManager> of property $_objectManager.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
726
    }
727
728
    /**
729
     * @return EventDispatcher
730
     */
731
    public function getDispatcher()
732
    {
733
        return $this->dispatcher;
734
    }
735
736
    /**
737
     * @param ConfigurationLoader $configurationLoader
738
     */
739
    public function setConfigurationLoader(ConfigurationLoader $configurationLoader)
740
    {
741
        $this->config->setConfigurationLoader($configurationLoader);
742
    }
743
744
    /**
745
     * @param OutputInterface $output
746
     */
747
    protected function _addOutputStyles(OutputInterface $output)
748
    {
749
        $output->getFormatter()->setStyle('debug', new OutputFormatterStyle('magenta', 'white'));
750
        $output->getFormatter()->setStyle('warning', new OutputFormatterStyle('red', 'yellow', array('bold')));
751
    }
752
753
    /**
754
     * @return ObjectManager
755
     */
756
    public function getObjectManager()
757
    {
758
        return $this->_objectManager;
759
    }
760
}
761