Completed
Push — master ( ad0611...abd676 )
by Tom
09:08 queued 04:30
created

MagentoHelper::detect()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 20
rs 8.8571
cc 5
eloc 12
nc 4
nop 2
1
<?php
2
3
namespace N98\Util\Console\Helper;
4
5
use N98\Magento\Application;
6
use N98\Util\BinaryString;
7
use RuntimeException;
8
use Symfony\Component\Console\Helper\Helper as AbstractHelper;
9
use Symfony\Component\Console\Input\ArgvInput;
10
use Symfony\Component\Console\Output\ConsoleOutput;
11
use Symfony\Component\Finder\Finder;
12
use Symfony\Component\Console\Output\OutputInterface;
13
use Symfony\Component\Console\Input\InputInterface;
14
15
/**
16
 * Class MagentoHelper
17
 *
18
 * @package N98\Util\Console\Helper
19
 */
20
class MagentoHelper extends AbstractHelper
21
{
22
    /**
23
     * @var string
24
     */
25
    protected $_magentoRootFolder = null;
26
27
    /**
28
     * @var int
29
     */
30
    protected $_magentoMajorVersion = \N98\Magento\Application::MAGENTO_MAJOR_VERSION_1;
31
32
    /**
33
     * @var bool
34
     */
35
    protected $_magentoEnterprise = false;
36
37
    /**
38
     * @var bool
39
     */
40
    protected $_magerunStopFileFound = false;
41
42
    /**
43
     * @var string
44
     */
45
    protected $_magerunStopFileFolder = null;
46
47
    /**
48
     * @var InputInterface
49
     */
50
    protected $input;
51
52
    /**
53
     * @var OutputInterface
54
     */
55
    protected $output;
56
57
    /**
58
     * @var array
59
     */
60
    protected $baseConfig = array();
61
62
    /**
63
     * @var string
64
     */
65
    protected $_customConfigFilename = 'n98-magerun2.yaml';
66
67
    /**
68
     * Returns the canonical name of this helper.
69
     *
70
     * @return string The canonical name
71
     *
72
     * @api
73
     */
74
    public function getName()
75
    {
76
        return 'magento';
77
    }
78
79
    /**
80
     * @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...
81
     * @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...
82
     */
83
    public function __construct(InputInterface $input = null, OutputInterface $output = null)
84
    {
85
        if (null === $input) {
86
            $input = new ArgvInput();
87
        }
88
89
        if (null === $output) {
90
            $output = new ConsoleOutput();
91
        }
92
93
        $this->input = $input;
94
        $this->output = $output;
95
    }
96
97
    /**
98
     * Start Magento detection
99
     *
100
     * @param string $folder
101
     * @param array $subFolders [optional] sub-folders to check
102
     * @return bool
103
     */
104
    public function detect($folder, array $subFolders = array())
105
    {
106
        $folders = $this->splitPathFolders($folder);
107
        $folders = $this->checkMagerunFile($folders);
108
        $folders = $this->checkModman($folders);
109
        $folders = array_merge($folders, $subFolders);
110
111
        foreach (array_reverse($folders) as $searchFolder) {
112
            if (!is_dir($searchFolder) || !is_readable($searchFolder)) {
113
                continue;
114
            }
115
116
            $found = $this->_search($searchFolder);
117
            if ($found) {
118
                return true;
119
            }
120
        }
121
122
        return false;
123
    }
124
125
126
    /**
127
     * @return string
128
     */
129
    public function getRootFolder()
130
    {
131
        return $this->_magentoRootFolder;
132
    }
133
134
    public function getEdition()
135
    {
136
        return $this->_magentoMajorVersion;
137
    }
138
139
    /**
140
     * @return bool
141
     */
142
    public function isEnterpriseEdition()
143
    {
144
        return $this->_magentoEnterprise;
145
    }
146
147
    /**
148
     * @return int
149
     */
150
    public function getMajorVersion()
151
    {
152
        return $this->_magentoMajorVersion;
153
    }
154
155
    /**
156
     * @return boolean
157
     */
158
    public function isMagerunStopFileFound()
159
    {
160
        return $this->_magerunStopFileFound;
161
    }
162
163
    /**
164
     * @return string
165
     */
166
    public function getMagerunStopFileFolder()
167
    {
168
        return $this->_magerunStopFileFolder;
169
    }
170
171
    /**
172
     * @param string $folder
173
     *
174
     * @return array
175
     */
176
    protected function splitPathFolders($folder)
177
    {
178
        $folders = array();
179
180
        $folderParts = explode('/', $folder);
181
        foreach ($folderParts as $key => $part) {
182
            $explodedFolder = implode('/', array_slice($folderParts, 0, $key + 1));
183
            if ($explodedFolder !== '') {
184
                $folders[] = $explodedFolder;
185
            }
186
        }
187
188
        return $folders;
189
    }
190
191
    /**
192
     * Check for modman file and .basedir
193
     *
194
     * @param array $folders
195
     *
196
     * @return array
197
     */
198
    protected function checkModman(array $folders)
199
    {
200
        foreach (array_reverse($folders) as $searchFolder) {
201 View Code Duplication
            if (!is_readable($searchFolder)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
202
                if (OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity()) {
203
                    $this->output->writeln(
204
                        '<debug>Folder <info>' . $searchFolder . '</info> is not readable. Skip.</debug>'
205
                    );
206
                }
207
                continue;
208
            }
209
210
            $finder = Finder::create();
211
            $finder
212
                ->files()
213
                ->ignoreUnreadableDirs(true)
214
                ->depth(0)
215
                ->followLinks()
216
                ->ignoreDotFiles(false)
217
                ->name('.basedir')
218
                ->in($searchFolder);
219
220
            $count = $finder->count();
221
            if ($count > 0) {
222
                $baseFolderContent = trim(file_get_contents($searchFolder . '/.basedir'));
223
                if (OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity()) {
224
                    $this->output->writeln(
225
                        '<debug>Found modman .basedir file with content <info>' . $baseFolderContent . '</info></debug>'
226
                    );
227
                }
228
229
                if (!empty($baseFolderContent)) {
230
                    array_push($folders, $searchFolder . '/../' . $baseFolderContent);
231
                }
232
            }
233
        }
234
235
        return $folders;
236
    }
237
238
    /**
239
     * Check for magerun stop-file
240
     *
241
     * @param array $folders
242
     *
243
     * @return array
244
     */
245
    protected function checkMagerunFile(array $folders)
246
    {
247
        foreach (array_reverse($folders) as $searchFolder) {
248 View Code Duplication
            if (!is_readable($searchFolder)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
249
                if (OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity()) {
250
                    $this->output->writeln(
251
                        sprintf('<debug>Folder <info>%s</info> is not readable. Skip.</debug>', $searchFolder)
252
                    );
253
                }
254
                continue;
255
            }
256
            $stopFile = '.' . pathinfo($this->_customConfigFilename, PATHINFO_FILENAME);
257
            $finder = Finder::create();
258
            $finder
259
                ->files()
260
                ->ignoreUnreadableDirs(true)
261
                ->depth(0)
262
                ->followLinks()
263
                ->ignoreDotFiles(false)
264
                ->name($stopFile)
265
                ->in($searchFolder);
266
267
            $count = $finder->count();
268
            if ($count > 0) {
269
                $this->_magerunStopFileFound  = true;
270
                $this->_magerunStopFileFolder = $searchFolder;
271
                $magerunFilePath              = $searchFolder . '/' . $stopFile;
272
                $magerunFileContent           = trim(file_get_contents($magerunFilePath));
273
                if (OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity()) {
274
                    $message = sprintf(
275
                        '<debug>Found stopfile \'%s\' file with content <info>%s</info></debug>',
276
                        $stopFile,
277
                        $magerunFileContent
278
                    );
279
                    $this->output->writeln($message);
280
                }
281
282
                array_push($folders, $searchFolder . '/' . $magerunFileContent);
283
            }
284
        }
285
286
        return $folders;
287
    }
288
289
    /**
290
     * @param string $searchFolder
291
     *
292
     * @return bool
293
     */
294
    protected function _search($searchFolder)
295
    {
296
        if (OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity()) {
297
            $this->output->writeln('<debug>Search for Magento in folder <info>' . $searchFolder . '</info></debug>');
298
        }
299
300
        if (!is_dir($searchFolder . '/app')) {
301
            return false;
302
        }
303
304
        $finder = Finder::create();
305
        $finder
306
            ->ignoreUnreadableDirs(true)
307
            ->depth(0)
308
            ->followLinks()
309
            ->name('Mage.php')
310
            ->name('bootstrap.php')
311
            ->name('autoload.php')
312
            ->in($searchFolder . '/app');
313
314
        if ($finder->count() > 0) {
315
            $files = iterator_to_array($finder, false);
316
            /* @var $file \SplFileInfo */
317
318
            $hasMageFile = false;
319
            foreach ($files as $file) {
320
                if ($file->getFilename() == 'Mage.php') {
321
                    $hasMageFile = true;
322
                }
323
            }
324
325
            $this->_magentoRootFolder = $searchFolder;
326
327
            // Magento 2 does not have a god class and thus if this file is not there it is version 2
328
            if ($hasMageFile == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
329
                $this->_magentoMajorVersion = Application::MAGENTO_MAJOR_VERSION_2;
330
            } else {
331
                $this->_magentoMajorVersion = Application::MAGENTO_MAJOR_VERSION_1;
332
            }
333
334
            if (OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity()) {
335
                $this->output->writeln(
336
                    '<debug>Found Magento in folder <info>' . $this->_magentoRootFolder . '</info></debug>'
337
                );
338
            }
339
340
            return true;
341
        }
342
343
        return false;
344
    }
345
346
    /**
347
     * @return array
348
     * @throws RuntimeException
349
     */
350
    public function getBaseConfig()
351
    {
352
        if (!$this->baseConfig) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->baseConfig of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
353
            $this->initBaseConfig();
354
        }
355
356
        return $this->baseConfig;
357
    }
358
359
    private function initBaseConfig()
360
    {
361
        $this->baseConfig = [];
362
363
        $application = $this->getApplication();
364
365
        $configFiles = [
366
            'app/etc/config.php',
367
            'app/etc/env.php'
368
        ];
369
370
        foreach ($configFiles as $configFileName) {
371
            $this->addBaseConfig($application->getMagentoRootFolder(), $configFileName);
372
        }
373
    }
374
375
    /**
376
     * private getter for application that has magento detected
377
     *
378
     * @return Application|\Symfony\Component\Console\Application
379
     */
380
    private function getApplication()
381
    {
382
        $command = $this->getHelperSet()->getCommand();
383
        if ($command == null) {
384
            $application = new Application();
385
        } else {
386
            $application = $command->getApplication(); /* @var $application Application */
387
        }
388
        $application->detectMagento();
389
390
        return $application;
391
    }
392
393
    private function addBaseConfig($root, $configFileName)
394
    {
395
        $configFile = $root . '/' . $configFileName;
396
        if (!(is_file($configFile) && is_readable($configFile))) {
397
            throw new RuntimeException(sprintf('%s is not readable', $configFileName));
398
        }
399
400
        $config = @include $configFile;
401
402
        if (!is_array($config)) {
403
            throw new RuntimeException(sprintf('%s is corrupted. Please check it.', $configFileName));
404
        }
405
406
        $this->baseConfig = array_merge($this->baseConfig, $config);
407
    }
408
}
409