Completed
Push — develop ( bbf1d7...d89cb9 )
by Tom
05:51
created

MagentoHelper::getApplication()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 17
rs 9.4285
cc 3
eloc 8
nc 4
nop 0
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 UnexpectedValueException;
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
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
     * @return string
129
     */
130
    public function getRootFolder()
131
    {
132
        return $this->_magentoRootFolder;
133
    }
134
135
    public function getEdition()
136
    {
137
        return $this->_magentoMajorVersion;
138
    }
139
140
    /**
141
     * @return bool
142
     */
143
    public function isEnterpriseEdition()
144
    {
145
        return $this->_magentoEnterprise;
146
    }
147
148
    /**
149
     * @return int
150
     */
151
    public function getMajorVersion()
152
    {
153
        return $this->_magentoMajorVersion;
154
    }
155
156
    /**
157
     * @return boolean
158
     */
159
    public function isMagerunStopFileFound()
160
    {
161
        return $this->_magerunStopFileFound;
162
    }
163
164
    /**
165
     * @return string
166
     */
167
    public function getMagerunStopFileFolder()
168
    {
169
        return $this->_magerunStopFileFolder;
170
    }
171
172
    /**
173
     * @param string $folder
174
     *
175
     * @return array
176
     */
177
    protected function splitPathFolders($folder)
178
    {
179
        $folders = array();
180
181
        $folderParts = explode('/', $folder);
182
        foreach ($folderParts as $key => $part) {
183
            $explodedFolder = implode('/', array_slice($folderParts, 0, $key + 1));
184
            if ($explodedFolder !== '') {
185
                $folders[] = $explodedFolder;
186
            }
187
        }
188
189
        return $folders;
190
    }
191
192
    /**
193
     * Check for modman file and .basedir
194
     *
195
     * @param array $folders
196
     *
197
     * @return array
198
     */
199
    protected function checkModman(array $folders)
200
    {
201
        foreach ($this->searchFolders($folders) as $searchFolder) {
202
            $finder = Finder::create();
203
            $finder
204
                ->files()
205
                ->ignoreUnreadableDirs(true)
206
                ->depth(0)
207
                ->followLinks()
208
                ->ignoreDotFiles(false)
209
                ->name('.basedir')
210
                ->in($searchFolder);
211
212
            $count = $finder->count();
213
            if ($count > 0) {
214
                $baseFolderContent = trim(file_get_contents($searchFolder . '/.basedir'));
215
                $this->writeDebug('Found modman .basedir file with content <info>' . $baseFolderContent . '</info>');
216
217
                if (!empty($baseFolderContent)) {
218
                    array_push($folders, $searchFolder . '/../' . $baseFolderContent);
219
                }
220
            }
221
        }
222
223
        return $folders;
224
    }
225
226
    /**
227
     * Check for magerun stop-file
228
     *
229
     * @param array $folders
230
     *
231
     * @return array
232
     */
233
    protected function checkMagerunFile(array $folders)
234
    {
235
        foreach ($this->searchFolders($folders) as $searchFolder) {
236
            $stopFile = '.' . pathinfo($this->_customConfigFilename, PATHINFO_FILENAME);
237
            $finder = Finder::create();
238
            $finder
239
                ->files()
240
                ->ignoreUnreadableDirs(true)
241
                ->depth(0)
242
                ->followLinks()
243
                ->ignoreDotFiles(false)
244
                ->name($stopFile)
245
                ->in($searchFolder);
246
247
            $count = $finder->count();
248
            if ($count > 0) {
249
                $this->_magerunStopFileFound = true;
250
                $this->_magerunStopFileFolder = $searchFolder;
251
                $magerunFilePath = $searchFolder . '/' . $stopFile;
252
                $magerunFileContent = trim(file_get_contents($magerunFilePath));
253
                $message = sprintf(
254
                    'Found stopfile \'%s\' file with content <info>%s</info>',
255
                    $stopFile,
256
                    $magerunFileContent
257
                );
258
                $this->writeDebug($message);
259
260
                array_push($folders, $searchFolder . '/' . $magerunFileContent);
261
            }
262
        }
263
264
        return $folders;
265
    }
266
267
    /**
268
     * Turn an array of folders into a Traversable of readable paths.
269
     *
270
     * @param array $folders
271
     * @return CallbackFilterIterator Traversable of strings that are readable paths
272
     */
273
    private function searchFolders(array $folders)
274
    {
275
        $that = $this;
276
277
        $callback = function ($searchFolder) use ($that) {
278
            if (!is_readable($searchFolder)) {
279
                $that->writeDebug('Folder <info>' . $searchFolder . '</info> is not readable. Skip.');
280
281
                return false;
282
            }
283
284
            return true;
285
        };
286
287
        return new CallbackFilterIterator(
288
            new ArrayIterator(array_reverse($folders)),
289
            $callback
290
        );
291
    }
292
293
    /**
294
     * @param string $searchFolder
295
     *
296
     * @return bool
297
     */
298
    protected function _search($searchFolder)
299
    {
300
        $this->writeDebug('Search for Magento in folder <info>' . $searchFolder . '</info>');
301
302
        if (!is_dir($searchFolder . '/app')) {
303
            return false;
304
        }
305
306
        $finder = Finder::create();
307
        $finder
308
            ->ignoreUnreadableDirs(true)
309
            ->depth(0)
310
            ->followLinks()
311
            ->name('Mage.php')
312
            ->name('bootstrap.php')
313
            ->name('autoload.php')
314
            ->in($searchFolder . '/app');
315
316
        if ($finder->count() > 0) {
317
            $files = iterator_to_array($finder, false);
318
            /* @var $file \SplFileInfo */
319
320
            $hasMageFile = false;
321
            foreach ($files as $file) {
322
                if ($file->getFilename() == 'Mage.php') {
323
                    $hasMageFile = true;
324
                }
325
            }
326
327
            $this->_magentoRootFolder = $searchFolder;
328
329
            // Magento 2 does not have a god class and thus if this file is not there it is version 2
330
            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...
331
                $this->_magentoMajorVersion = Application::MAGENTO_MAJOR_VERSION_2;
332
            } else {
333
                $this->_magentoMajorVersion = Application::MAGENTO_MAJOR_VERSION_1;
334
            }
335
336
            $this->writeDebug('Found Magento in folder <info>' . $this->_magentoRootFolder . '</info>');
337
338
            return true;
339
        }
340
341
        return false;
342
    }
343
344
    /**
345
     * @return array
346
     * @throws RuntimeException
347
     */
348
    public function getBaseConfig()
349
    {
350
        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...
351
            $this->initBaseConfig();
352
        }
353
354
        return $this->baseConfig;
355
    }
356
357
    private function initBaseConfig()
358
    {
359
        $this->baseConfig = [];
360
361
        $application = $this->getApplication();
362
363
        $configFiles = [
364
            'app/etc/config.php',
365
            'app/etc/env.php',
366
        ];
367
368
        foreach ($configFiles as $configFileName) {
369
            $this->addBaseConfig($application->getMagentoRootFolder(), $configFileName);
370
        }
371
    }
372
373
    /**
374
     * private getter for application that has magento detected
375
     *
376
     * @return Application
377
     */
378
    private function getApplication()
379
    {
380
        $command = $this->getHelperSet()->getCommand();
381
382
        $application = $command ? $command->getApplication() : new Application();
383
384
        // verify type because of detectMagento() call below
385
        if (!$application instanceof Application) {
386
            throw new UnexpectedValueException(
387
                sprintf('Expected magerun application got %s', get_class($application))
388
            );
389
        }
390
391
        $application->detectMagento();
392
393
        return $application;
394
    }
395
396
    /**
397
     * @param string $root
398
     * @param string $configFileName
399
     */
400
    private function addBaseConfig($root, $configFileName)
401
    {
402
        $configFile = $root . '/' . $configFileName;
403
        if (!(is_file($configFile) && is_readable($configFile))) {
404
            throw new RuntimeException(sprintf('%s is not readable', $configFileName));
405
        }
406
407
        $config = @include $configFile;
408
409
        if (!is_array($config)) {
410
            throw new RuntimeException(sprintf('%s is corrupted. Please check it.', $configFileName));
411
        }
412
413
        $this->baseConfig = array_merge($this->baseConfig, $config);
414
    }
415
416
    /**
417
     * @param string $message
418
     * @return void
419
     */
420
    private function writeDebug($message)
421
    {
422
        if (OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity()) {
423
            $this->output->writeln(
424
                '<debug>' . $message . '</debug>'
425
            );
426
        }
427
    }
428
}
429