Completed
Push — master ( f5a8cf...dc6729 )
by Christian
07:18
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
cc 3
eloc 13
nc 3
nop 3
dl 0
loc 19
rs 9.4285
c 0
b 0
f 0
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
    /**
238
     * @param string $magentoRootFolder
239
     * @param string|array $in
240
     * @param integer $depth
241
     */
242
    private function traversePluginFoldersForConfigFile($magentoRootFolder, $in, $depth)
243
    {
244
        $basename = $this->_customConfigFilename;
245
        if (1 > count($in = array_filter(array_filter((array) $in, 'strlen'), 'is_dir'))) {
246
            return;
247
        }
248
249
        $finder = Finder::create()
250
            ->files()
251
            ->depth($depth)
252
            ->followLinks()
253
            ->ignoreUnreadableDirs(true)
254
            ->name($basename)
255
            ->in($in);
256
257
        foreach ($finder as $file) {
258
            $this->registerPluginConfigFile($magentoRootFolder, $file);
259
        }
260
    }
261
262
    /**
263
     * Check if there is a user config file. ~/.n98-magerun.yaml
264
     *
265
     * @param array $config
266
     * @param string $magentoRootFolder [optional]
267
     *
268
     * @return array
269
     */
270
    public function loadUserConfig(array $config, $magentoRootFolder = null)
271
    {
272
        if (null === $this->_userConfig) {
273
            $this->_userConfig = array();
274
            $locator = new ConfigLocator($this->_customConfigFilename, $magentoRootFolder);
275
            if ($userConfigFile = $locator->getUserConfigFile()) {
276
                $this->_userConfig = $userConfigFile->toArray();
277
            }
278
        }
279
280
        $config = ArrayFunctions::mergeArrays($config, $this->_userConfig);
281
282
        return $config;
283
    }
284
285
    /**
286
     * MAGENTO_ROOT/app/etc/n98-magerun.yaml
287
     *
288
     * @param string $magentoRootFolder
289
     * @param string $magerunStopFileFolder
290
     * @param array $config
291
     *
292
     * @return array
293
     */
294
    public function loadProjectConfig($magentoRootFolder, $magerunStopFileFolder, array $config)
295
    {
296
        if (null !== $this->_projectConfig) {
297
            return ArrayFunctions::mergeArrays($config, $this->_projectConfig);
298
        }
299
300
        $this->_projectConfig = array();
301
302
        $locator = new ConfigLocator($this->_customConfigFilename, $magentoRootFolder);
303
304
        if ($projectConfigFile = $locator->getProjectConfigFile()) {
305
            $this->_projectConfig = $projectConfigFile->toArray();
306
        }
307
308
        if ($stopFileConfigFile = $locator->getStopFileConfigFile($magerunStopFileFolder)) {
309
            $this->_projectConfig = $stopFileConfigFile->mergeArray($this->_projectConfig);
310
        }
311
312
        return ArrayFunctions::mergeArrays($config, $this->_projectConfig);
313
    }
314
315
    /**
316
     * Loads a plugin config file and merges it to plugin config
317
     *
318
     * @param string $magentoRootFolder
319
     * @param SplFileInfo $file
320
     */
321
    protected function registerPluginConfigFile($magentoRootFolder, $file)
322
    {
323
        $path = $file->getPathname();
324
325
        $this->logDebug('Load plugin config <comment>' . $path . '</comment>');
326
        $localPluginConfigFile = ConfigFile::createFromFile($path);
327
        $localPluginConfigFile->applyVariables($magentoRootFolder, $file);
328
        $this->_pluginConfig = $localPluginConfigFile->mergeArray($this->_pluginConfig);
329
    }
330
331
    /**
332
     * @return string
333
     */
334
    public function getVendorDir()
335
    {
336
        $configurationLoaderDir = $this->getConfigurationLoaderDir();
337
338
        /* source version vendor folder (also in phar archive) */
339
        $vendorFolder = $configurationLoaderDir . '/../../../../vendor';
340
        if (is_dir($vendorFolder)) {
341
            return $vendorFolder;
342
        }
343
344
        /* composer installed vendor folder */
345
        $vendorFolder = $configurationLoaderDir . '/../../../../../../../vendor';
346
        if (is_dir($vendorFolder)) {
347
            return $vendorFolder;
348
        }
349
350
        return '';
351
    }
352
353
    /**
354
     * @return string
355
     */
356
    public function getConfigurationLoaderDir()
357
    {
358
        return __DIR__;
359
    }
360
361
    /**
362
     * @param string $message
363
     */
364
    private function logDebug($message)
365
    {
366
        if (OutputInterface::VERBOSITY_DEBUG <= $this->_output->getVerbosity()) {
367
            $this->log('<debug>' . $message . '</debug>');
368
        }
369
    }
370
371
    /**
372
     * @param string $message
373
     */
374
    private function log($message)
375
    {
376
        $this->_output->writeln($message);
377
    }
378
}
379