Completed
Push — develop ( 57dfb2...713d02 )
by Tom
03:43
created

traversePluginFoldersForConfigFile()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 13
nc 3
nop 3
1
<?php
2
3
namespace N98\Magento\Application;
4
5
use N98\Util\ArrayFunctions;
6
use N98\Util\OperatingSystem;
7
use Symfony\Component\Console\Output\OutputInterface;
8
use Symfony\Component\Finder\Finder;
9
use Symfony\Component\Finder\SplFileInfo;
10
use Symfony\Component\Yaml\Yaml;
11
12
/**
13
 * Config consists of several parts which are merged.
14
 * The configuration which is global (not Magento project specific) is loaded
15
 * during construction.
16
 *
17
 * As soon as the Magento folder is known, loadStageTwo should be called.
18
 *
19
 * The toArray method only works if the Magento folder specific configuration is already loaded.
20
 *
21
 * Class ConfigurationLoader
22
 *
23
 * @package N98\Magento\Command
24
 */
25
class ConfigurationLoader
26
{
27
    /**
28
     * Config passed in the constructor
29
     *
30
     * @var array
31
     */
32
    protected $_initialConfig;
33
34
    /**
35
     * @var array
36
     */
37
    protected $_configArray = null;
38
39
    /**
40
     * Cache
41
     *
42
     * @var array
43
     */
44
    protected $_distConfig;
45
46
    /**
47
     * Cache
48
     *
49
     * @var array
50
     */
51
    protected $_pluginConfig;
52
53
    /**
54
     * Cache
55
     *
56
     * @var array
57
     */
58
    protected $_systemConfig;
59
60
    /**
61
     * Cache
62
     *
63
     * @var array
64
     */
65
    protected $_userConfig;
66
67
    /**
68
     * Cache
69
     *
70
     * @var array
71
     */
72
    protected $_projectConfig;
73
74
    /**
75
     * @var string
76
     */
77
    protected $_customConfigFilename = 'n98-magerun.yaml';
78
79
    /**
80
     * @var bool
81
     */
82
    protected $_isPharMode = true;
83
84
    /**
85
     * @var OutputInterface
86
     */
87
    protected $_output;
88
89
    /**
90
     * Load config
91
     * If $magentoRootFolder is null, only non-project config is loaded
92
     *
93
     * @param array $config
94
     * @param bool $isPharMode
95
     * @param OutputInterface $output
96
     */
97
    public function __construct(array $config, $isPharMode, OutputInterface $output)
98
    {
99
        $this->_initialConfig = $config;
100
        $this->_isPharMode = $isPharMode;
101
        $this->_output = $output;
102
    }
103
104
    /**
105
     * @param bool $loadExternalConfig
106
     * @return array
107
     */
108
    public function getPartialConfig($loadExternalConfig = true)
109
    {
110
        $config = $this->_initialConfig;
111
        $config = $this->loadDistConfig($config);
112
        if ($loadExternalConfig) {
113
            $config = $this->loadSystemConfig($config);
114
            $config = $this->loadUserConfig($config);
115
        }
116
117
        return $config;
118
    }
119
120
    /**
121
     * @param string $magentoRootFolder
122
     * @param bool $loadExternalConfig
123
     * @param string $magerunStopFileFolder
124
     */
125
    public function loadStageTwo($magentoRootFolder, $loadExternalConfig = true, $magerunStopFileFolder = '')
126
    {
127
        $config = $this->_initialConfig;
128
        $config = $this->loadDistConfig($config);
129
        if ($loadExternalConfig) {
130
            $config = $this->loadPluginConfig($config, $magentoRootFolder);
131
            $config = $this->loadSystemConfig($config);
132
            $config = $this->loadUserConfig($config, $magentoRootFolder);
133
            $config = $this->loadProjectConfig($magentoRootFolder, $magerunStopFileFolder, $config);
134
        }
135
        $this->_configArray = $config;
136
    }
137
138
    /**
139
     * @throws \ErrorException
140
     *
141
     * @return array
142
     */
143
    public function toArray()
144
    {
145
        if ($this->_configArray == null) {
146
            throw new \ErrorException('Configuration not yet fully loaded');
147
        }
148
149
        return $this->_configArray;
150
    }
151
152
    /**
153
     * @param array $initConfig
154
     *
155
     * @return array
156
     */
157
    protected function loadDistConfig(array $initConfig)
158
    {
159
        if ($this->_distConfig == null) {
160
            $distConfigFilePath = __DIR__ . '/../../../../config.yaml';
161
            $this->logDebug('Load dist config <comment>' . $distConfigFilePath . '</comment>');
162
            $this->_distConfig = ConfigFile::createFromFile($distConfigFilePath)->toArray();
163
        } else {
164
            $this->logDebug('Load dist config <comment>cached</comment>');
165
        }
166
167
        $config = ArrayFunctions::mergeArrays($this->_distConfig, $initConfig);
168
169
        return $config;
170
    }
171
172
    /**
173
     * Check if there is a global config file in /etc folder
174
     *
175
     * @param array $config
176
     *
177
     * @return array
178
     */
179
    public function loadSystemConfig(array $config)
180
    {
181
        if ($this->_systemConfig == null) {
182
            if (OperatingSystem::isWindows()) {
183
                $systemWideConfigFile = getenv('WINDIR') . '/' . $this->_customConfigFilename;
184
            } else {
185
                $systemWideConfigFile = '/etc/' . $this->_customConfigFilename;
186
            }
187
188
            if ($systemWideConfigFile && file_exists($systemWideConfigFile)) {
189
                $this->logDebug('Load system config <comment>' . $systemWideConfigFile . '</comment>');
190
                $this->_systemConfig = Yaml::parse($systemWideConfigFile);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Symfony\Component\Yaml\...($systemWideConfigFile) can also be of type string or object<stdClass>. However, the property $_systemConfig is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
191
            } else {
192
                $this->_systemConfig = array();
193
            }
194
        }
195
196
        $config = ArrayFunctions::mergeArrays($config, $this->_systemConfig);
197
198
        return $config;
199
    }
200
201
    /**
202
     * Load config from all installed bundles
203
     *
204
     * @param array $config
205
     * @param string $magentoRootFolder
206
     *
207
     * @return array
208
     */
209
    public function loadPluginConfig(array $config, $magentoRootFolder)
210
    {
211
        if (null === $this->_pluginConfig) {
212
            $this->_pluginConfig = array();
213
            $customName = pathinfo($this->_customConfigFilename, PATHINFO_FILENAME);
214
            if (OperatingSystem::isWindows()) {
215
                $config['plugin']['folders'][] = getenv('WINDIR') . '/' . $customName . '/modules';
216
                $config['plugin']['folders'][] = OperatingSystem::getHomeDir() . '/' . $customName . '/modules';
217
            }
218
            $config['plugin']['folders'][] = OperatingSystem::getHomeDir() . '/.' . $customName . '/modules';
219
            $config['plugin']['folders'][] = $magentoRootFolder . '/lib/' . $customName . '/modules';
220
221
            # Modules placed in vendor folder
222
            $vendorDir = $this->getVendorDir();
223
            if (strlen($vendorDir)) {
224
                $this->logDebug('Vendor directory <comment>' . $vendorDir . '</comment>');
225
                $this->traversePluginFoldersForConfigFile($magentoRootFolder, $vendorDir, 2);
226
            }
227
228
            # Glob plugin folders
229
            $this->traversePluginFoldersForConfigFile($magentoRootFolder, $config['plugin']['folders'], 1);
230
        }
231
232
        $config = ArrayFunctions::mergeArrays($config, $this->_pluginConfig);
233
234
        return $config;
235
    }
236
237
    private function traversePluginFoldersForConfigFile($magentoRootFolder, $in, $depth)
238
    {
239
        $basename = $this->_customConfigFilename;
240
        if (1 > count($in = array_filter(array_filter((array) $in, 'strlen'), 'is_dir'))) {
241
            return;
242
        }
243
244
        $finder = Finder::create()
245
            ->files()
246
            ->depth($depth)
247
            ->followLinks()
248
            ->ignoreUnreadableDirs(true)
249
            ->name($basename)
250
            ->in($in);
251
252
        foreach ($finder as $file) {
253
            $this->registerPluginConfigFile($magentoRootFolder, $file);
254
        }
255
    }
256
257
    /**
258
     * Check if there is a user config file. ~/.n98-magerun.yaml
259
     *
260
     * @param array $config
261
     * @param string $magentoRootFolder [optional]
0 ignored issues
show
Documentation introduced by
Should the type for parameter $magentoRootFolder not be string|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...
262
     *
263
     * @return array
264
     */
265
    public function loadUserConfig(array $config, $magentoRootFolder = null)
266
    {
267
        if (null === $this->_userConfig) {
268
            $this->_userConfig = array();
269
            $locator = new ConfigLocator($this->_customConfigFilename, $magentoRootFolder);
270
            if ($userConfigFile = $locator->getUserConfigFile()) {
271
                $this->_userConfig = $userConfigFile->toArray();
272
            }
273
        }
274
275
        $config = ArrayFunctions::mergeArrays($config, $this->_userConfig);
276
277
        return $config;
278
    }
279
280
    /**
281
     * MAGENTO_ROOT/app/etc/n98-magerun.yaml
282
     *
283
     * @param string $magentoRootFolder
284
     * @param string $magerunStopFileFolder
285
     * @param array $config
286
     *
287
     * @return array
288
     */
289
    public function loadProjectConfig($magentoRootFolder, $magerunStopFileFolder, array $config)
290
    {
291
        if (null !== $this->_projectConfig) {
292
            return ArrayFunctions::mergeArrays($config, $this->_projectConfig);
293
        }
294
295
        $this->_projectConfig = array();
296
297
        $locator = new ConfigLocator($this->_customConfigFilename, $magentoRootFolder);
298
299
        if ($projectConfigFile = $locator->getProjectConfigFile()) {
300
            $this->_projectConfig = $projectConfigFile->toArray();
301
        }
302
303
        if ($stopFileConfigFile = $locator->getStopFileConfigFile($magerunStopFileFolder)) {
304
            $this->_projectConfig = $stopFileConfigFile->mergeArray($this->_projectConfig);
305
        }
306
307
        return ArrayFunctions::mergeArrays($config, $this->_projectConfig);
308
    }
309
310
    /**
311
     * Loads a plugin config file and merges it to plugin config
312
     *
313
     * @param string $magentoRootFolder
314
     * @param SplFileInfo $file
315
     */
316
    protected function registerPluginConfigFile($magentoRootFolder, $file)
317
    {
318
        $path = $file->getPathname();
319
320
        $this->logDebug('Load plugin config <comment>' . $path . '</comment>');
321
        $localPluginConfigFile = ConfigFile::createFromFile($path);
322
        $localPluginConfigFile->applyVariables($magentoRootFolder, $file);
323
        $this->_pluginConfig = $localPluginConfigFile->mergeArray($this->_pluginConfig);
324
    }
325
326
    /**
327
     * @return string
328
     */
329
    public function getVendorDir()
330
    {
331
        $configurationLoaderDir = $this->getConfigurationLoaderDir();
332
333
        /* source version vendor folder (also in phar archive) */
334
        $vendorFolder = $configurationLoaderDir . '/../../../../vendor';
335
        if (is_dir($vendorFolder)) {
336
            return $vendorFolder;
337
        }
338
339
        /* composer installed vendor folder */
340
        $vendorFolder = $configurationLoaderDir . '/../../../../../../../vendor';
341
        if (is_dir($vendorFolder)) {
342
            return $vendorFolder;
343
        }
344
345
        return '';
346
    }
347
348
    /**
349
     * @return string
350
     */
351
    public function getConfigurationLoaderDir()
352
    {
353
        return __DIR__;
354
    }
355
356
    /**
357
     * @param string $message
358
     */
359
    private function logDebug($message)
360
    {
361
        if (OutputInterface::VERBOSITY_DEBUG <= $this->_output->getVerbosity()) {
362
            $this->log('<debug>' . $message . '</debug>');
363
        }
364
    }
365
366
    /**
367
     * @param string $message
368
     */
369
    private function log($message)
370
    {
371
        $this->_output->writeln($message);
372
    }
373
}
374