Completed
Push — develop ( ce5c3c...3abc17 )
by Tom
04:31
created

MagentoHelper::checkMagerunFile()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 33
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 2 Features 0
Metric Value
c 5
b 2
f 0
dl 0
loc 33
rs 8.8571
cc 3
eloc 25
nc 3
nop 1
1
<?php
2
3
namespace N98\Util\Console\Helper;
4
5
use ArrayIterator;
6
use CallbackFilterIterator;
7
use N98\Magento\Application;
8
use RuntimeException;
9
use Symfony\Component\Console\Helper\Helper as AbstractHelper;
10
use Symfony\Component\Console\Input\ArgvInput;
11
use Symfony\Component\Console\Input\InputInterface;
12
use Symfony\Component\Console\Output\ConsoleOutput;
13
use Symfony\Component\Console\Output\OutputInterface;
14
use Symfony\Component\Finder\Finder;
15
use Traversable;
16
17
/**
18
 * Class MagentoHelper
19
 *
20
 * @package N98\Util\Console\Helper
21
 */
22
class MagentoHelper extends AbstractHelper
23
{
24
    /**
25
     * @var string
26
     */
27
    protected $_magentoRootFolder = null;
28
29
    /**
30
     * @var int
31
     */
32
    protected $_magentoMajorVersion = \N98\Magento\Application::MAGENTO_MAJOR_VERSION_1;
33
34
    /**
35
     * @var bool
36
     */
37
    protected $_magentoEnterprise = false;
38
39
    /**
40
     * @var bool
41
     */
42
    protected $_magerunStopFileFound = false;
43
44
    /**
45
     * @var string
46
     */
47
    protected $_magerunStopFileFolder = null;
48
49
    /**
50
     * @var InputInterface
51
     */
52
    protected $input;
53
54
    /**
55
     * @var OutputInterface
56
     */
57
    protected $output;
58
59
    /**
60
     * @var array
61
     */
62
    protected $baseConfig = array();
63
64
    /**
65
     * @var string
66
     */
67
    protected $_customConfigFilename = 'n98-magerun2.yaml';
68
69
    /**
70
     * Returns the canonical name of this helper.
71
     *
72
     * @return string The canonical name
73
     *
74
     * @api
75
     */
76
    public function getName()
77
    {
78
        return 'magento';
79
    }
80
81
    /**
82
     * @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...
83
     * @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...
84
     */
85
    public function __construct(InputInterface $input = null, OutputInterface $output = null)
86
    {
87
        if (null === $input) {
88
            $input = new ArgvInput();
89
        }
90
91
        if (null === $output) {
92
            $output = new ConsoleOutput();
93
        }
94
95
        $this->input = $input;
96
        $this->output = $output;
97
    }
98
99
    /**
100
     * Start Magento detection
101
     *
102
     * @param string $folder
103
     * @param array $subFolders [optional] sub-folders to check
104
     * @return bool
105
     */
106
    public function detect($folder, array $subFolders = array())
107
    {
108
        $folders = $this->splitPathFolders($folder);
109
        $folders = $this->checkMagerunFile($folders);
110
        $folders = $this->checkModman($folders);
111
        $folders = array_merge($folders, $subFolders);
112
113
        foreach (array_reverse($folders) as $searchFolder) {
114
            if (!is_dir($searchFolder) || !is_readable($searchFolder)) {
115
                continue;
116
            }
117
118
            $found = $this->_search($searchFolder);
119
            if ($found) {
120
                return true;
121
            }
122
        }
123
124
        return false;
125
    }
126
127
128
    /**
129
     * @return string
130
     */
131
    public function getRootFolder()
132
    {
133
        return $this->_magentoRootFolder;
134
    }
135
136
    public function getEdition()
137
    {
138
        return $this->_magentoMajorVersion;
139
    }
140
141
    /**
142
     * @return bool
143
     */
144
    public function isEnterpriseEdition()
145
    {
146
        return $this->_magentoEnterprise;
147
    }
148
149
    /**
150
     * @return int
151
     */
152
    public function getMajorVersion()
153
    {
154
        return $this->_magentoMajorVersion;
155
    }
156
157
    /**
158
     * @return boolean
159
     */
160
    public function isMagerunStopFileFound()
161
    {
162
        return $this->_magerunStopFileFound;
163
    }
164
165
    /**
166
     * @return string
167
     */
168
    public function getMagerunStopFileFolder()
169
    {
170
        return $this->_magerunStopFileFolder;
171
    }
172
173
    /**
174
     * @param string $folder
175
     *
176
     * @return array
177
     */
178
    protected function splitPathFolders($folder)
179
    {
180
        $folders = array();
181
182
        $folderParts = explode('/', $folder);
183
        foreach ($folderParts as $key => $part) {
184
            $explodedFolder = implode('/', array_slice($folderParts, 0, $key + 1));
185
            if ($explodedFolder !== '') {
186
                $folders[] = $explodedFolder;
187
            }
188
        }
189
190
        return $folders;
191
    }
192
193
    /**
194
     * Check for modman file and .basedir
195
     *
196
     * @param array $folders
197
     *
198
     * @return array
199
     */
200
    protected function checkModman(array $folders)
201
    {
202
        foreach ($this->searchFolders($folders) as $searchFolder) {
203
            $finder = Finder::create();
204
            $finder
205
                ->files()
206
                ->ignoreUnreadableDirs(true)
207
                ->depth(0)
208
                ->followLinks()
209
                ->ignoreDotFiles(false)
210
                ->name('.basedir')
211
                ->in($searchFolder);
212
213
            $count = $finder->count();
214
            if ($count > 0) {
215
                $baseFolderContent = trim(file_get_contents($searchFolder . '/.basedir'));
216
                $this->writeDebug('Found modman .basedir file with content <info>' . $baseFolderContent . '</info>');
217
218
                if (!empty($baseFolderContent)) {
219
                    array_push($folders, $searchFolder . '/../' . $baseFolderContent);
220
                }
221
            }
222
        }
223
224
        return $folders;
225
    }
226
227
    /**
228
     * Check for magerun stop-file
229
     *
230
     * @param array $folders
231
     *
232
     * @return array
233
     */
234
    protected function checkMagerunFile(array $folders)
235
    {
236
        foreach ($this->searchFolders($folders) as $searchFolder) {
237
            $stopFile = '.' . pathinfo($this->_customConfigFilename, PATHINFO_FILENAME);
238
            $finder = Finder::create();
239
            $finder
240
                ->files()
241
                ->ignoreUnreadableDirs(true)
242
                ->depth(0)
243
                ->followLinks()
244
                ->ignoreDotFiles(false)
245
                ->name($stopFile)
246
                ->in($searchFolder);
247
248
            $count = $finder->count();
249
            if ($count > 0) {
250
                $this->_magerunStopFileFound = true;
251
                $this->_magerunStopFileFolder = $searchFolder;
252
                $magerunFilePath = $searchFolder . '/' . $stopFile;
253
                $magerunFileContent = trim(file_get_contents($magerunFilePath));
254
                $message = sprintf(
255
                    'Found stopfile \'%s\' file with content <info>%s</info>',
256
                    $stopFile,
257
                    $magerunFileContent
258
                );
259
                $this->writeDebug($message);
260
261
                array_push($folders, $searchFolder . '/' . $magerunFileContent);
262
            }
263
        }
264
265
        return $folders;
266
    }
267
268
    /**
269
     * Turn an array of folders into a Traversable of readable paths.
270
     *
271
     * @param array $folders
272
     * @return Traversable
0 ignored issues
show
Documentation introduced by
Should the return type not be CallbackFilterIterator?

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...
273
     */
274
    private function searchFolders(array $folders)
275
    {
276
        $that = $this;
277
278
        $callback = function ($searchFolder, $key, $iterator) use ($that) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $iterator is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
279
            if (!is_readable($searchFolder)) {
280
                $that->writeDebug('Folder <info>' . $searchFolder . '</info> is not readable. Skip.');
281
282
                return false;
283
            }
284
285
            return true;
286
        };
287
288
        return new CallbackFilterIterator(
289
            new ArrayIterator(array_reverse($folders)),
290
            $callback
291
        );
292
    }
293
294
    /**
295
     * @param string $searchFolder
296
     *
297
     * @return bool
298
     */
299
    protected function _search($searchFolder)
300
    {
301
        $this->writeDebug('Search for Magento in folder <info>' . $searchFolder . '</info>');
302
303
        if (!is_dir($searchFolder . '/app')) {
304
            return false;
305
        }
306
307
        $finder = Finder::create();
308
        $finder
309
            ->ignoreUnreadableDirs(true)
310
            ->depth(0)
311
            ->followLinks()
312
            ->name('Mage.php')
313
            ->name('bootstrap.php')
314
            ->name('autoload.php')
315
            ->in($searchFolder . '/app');
316
317
        if ($finder->count() > 0) {
318
            $files = iterator_to_array($finder, false);
319
            /* @var $file \SplFileInfo */
320
321
            $hasMageFile = false;
322
            foreach ($files as $file) {
323
                if ($file->getFilename() == 'Mage.php') {
324
                    $hasMageFile = true;
325
                }
326
            }
327
328
            $this->_magentoRootFolder = $searchFolder;
329
330
            // Magento 2 does not have a god class and thus if this file is not there it is version 2
331
            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...
332
                $this->_magentoMajorVersion = Application::MAGENTO_MAJOR_VERSION_2;
333
            } else {
334
                $this->_magentoMajorVersion = Application::MAGENTO_MAJOR_VERSION_1;
335
            }
336
337
            $this->writeDebug('Found Magento in folder <info>' . $this->_magentoRootFolder . '</info>');
338
339
            return true;
340
        }
341
342
        return false;
343
    }
344
345
    /**
346
     * @return array
347
     * @throws RuntimeException
348
     */
349
    public function getBaseConfig()
350
    {
351
        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...
352
            $this->initBaseConfig();
353
        }
354
355
        return $this->baseConfig;
356
    }
357
358
    private function initBaseConfig()
359
    {
360
        $this->baseConfig = [];
361
362
        $application = $this->getApplication();
363
364
        $configFiles = [
365
            'app/etc/config.php',
366
            'app/etc/env.php'
367
        ];
368
369
        foreach ($configFiles as $configFileName) {
370
            $this->addBaseConfig($application->getMagentoRootFolder(), $configFileName);
371
        }
372
    }
373
374
    /**
375
     * private getter for application that has magento detected
376
     *
377
     * @return Application|\Symfony\Component\Console\Application
378
     */
379
    private function getApplication()
380
    {
381
        $command = $this->getHelperSet()->getCommand();
382
        if ($command == null) {
383
            $application = new Application();
384
        } else {
385
            /* @var $application Application */
386
            $application = $command->getApplication();
387
        }
388
        $application->detectMagento();
389
390
        return $application;
391
    }
392
393
    /**
394
     * @param string $configFileName
395
     */
396
    private function addBaseConfig($root, $configFileName)
397
    {
398
        $configFile = $root . '/' . $configFileName;
399
        if (!(is_file($configFile) && is_readable($configFile))) {
400
            throw new RuntimeException(sprintf('%s is not readable', $configFileName));
401
        }
402
403
        $config = @include $configFile;
404
405
        if (!is_array($config)) {
406
            throw new RuntimeException(sprintf('%s is corrupted. Please check it.', $configFileName));
407
        }
408
409
        $this->baseConfig = array_merge($this->baseConfig, $config);
410
    }
411
412
    /**
413
     * @param string $message
414
     * @return void
415
     */
416
    private function writeDebug($message)
417
    {
418
        if (OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity()) {
419
            $this->output->writeln(
420
                '<debug>' . $message . '</debug>'
421
            );
422
        }
423
    }
424
}
425