Completed
Push — master ( 6197de...1878e1 )
by Tim
02:31
created

SimpleConfigurationLoader::getEditionMapping()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 25
rs 9.9332
1
<?php
2
3
/**
4
 * TechDivision\Import\Cli\SimpleConfigurationLoader
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2016 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/techdivision/import-cli-simple
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Cli;
22
23
use Symfony\Component\Console\Input\InputInterface;
24
use Symfony\Component\DependencyInjection\ContainerInterface;
25
use TechDivision\Import\ConfigurationFactoryInterface;
26
use TechDivision\Import\Cli\Command\InputOptionKeys;
27
use TechDivision\Import\Cli\Configuration\LibraryLoader;
28
use TechDivision\Import\Cli\Utils\DependencyInjectionKeys;
29
use TechDivision\Import\Cli\Utils\MagentoConfigurationKeys;
30
use TechDivision\Import\Utils\CommandNames;
31
use TechDivision\Import\Utils\Mappings\CommandNameToEntityTypeCode;
32
33
/**
34
 * The configuration loader implementation.
35
 *
36
 * @author    Tim Wagner <[email protected]>
37
 * @copyright 2016 TechDivision GmbH <[email protected]>
38
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
39
 * @link      https://github.com/techdivision/import-cli-simple
40
 * @link      http://www.techdivision.com
41
 */
42
class SimpleConfigurationLoader implements ConfigurationLoaderInterface
43
{
44
45
    /**
46
     * The container instance.
47
     *
48
     * @var \Symfony\Component\DependencyInjection\ContainerInterface
49
     */
50
    protected $container;
51
52
    /**
53
     * The actual input instance.
54
     *
55
     * @var \Symfony\Component\Console\Input\InputInterface
56
     */
57
    protected $input;
58
59
    /**
60
     * The library loader instance.
61
     *
62
     * @param \TechDivision\Import\Cli\LibraryLoader
63
     */
64
    protected $libraryLoader;
65
66
    /**
67
     * The configuration factory instance.
68
     *
69
     * @var \TechDivision\Import\ConfigurationFactoryInterface
70
     */
71
    protected $configurationFactory;
72
73
    /**
74
     * The available command names.
75
     *
76
     * @var \TechDivision\Import\Utils\CommandNames
77
     */
78
    protected $commandNames;
79
80
    /**
81
     * The mapping of the command names to the entity type codes
82
     *
83
     * @var \TechDivision\Import\Utils\Mappings\CommandNameToEntityTypeCode
84
     */
85
    protected $commandNameToEntityTypeCode;
86
87
    /**
88
     * Initializes the configuration loader.
89
     *
90
     * @param \Symfony\Component\Console\Input\InputInterface                 $input                        The input instance
91
     * @param \Symfony\Component\DependencyInjection\ContainerInterface       $container                    The container instance
92
     * @param \TechDivision\Import\Cli\Configuration\LibraryLoader            $libraryLoader                The configuration loader instance
93
     * @param \TechDivision\Import\ConfigurationFactoryInterface              $configurationFactory         The configuration factory instance
94
     * @param \TechDivision\Import\Utils\CommandNames                         $commandNames                 The available command names
95
     * @param \TechDivision\Import\Utils\Mappings\CommandNameToEntityTypeCode $commandNameToEntityTypeCodes The mapping of the command names to the entity type codes
96
     */
97
    public function __construct(
98
        InputInterface $input,
99
        ContainerInterface $container,
100
        LibraryLoader $libraryLoader,
101
        ConfigurationFactoryInterface $configurationFactory,
102
        CommandNames $commandNames,
103
        CommandNameToEntityTypeCode $commandNameToEntityTypeCodes
104
    ) {
105
106
        // set the passed instances
107
        $this->input = $input;
108
        $this->container = $container;
109
        $this->libraryLoader = $libraryLoader;
110
        $this->configurationFactory = $configurationFactory;
111
        $this->commandNames = $commandNames;
112
        $this->commandNameToEntityTypeCode = $commandNameToEntityTypeCodes;
113
    }
114
115
    /**
116
     * Factory implementation to create a new initialized configuration instance.
117
     *
118
     * If command line options are specified, they will always override the
119
     * values found in the configuration file.
120
     *
121
     * @return \TechDivision\Import\ConfigurationInterface The configuration instance
122
     * @throws \Exception Is thrown, if the specified configuration file doesn't exist or the mandatory arguments/options to run the requested operation are not available
123
     */
124
    public function load()
125
    {
126
127
        // load the actual vendor directory and entity type code
128
        $vendorDir = $this->getVendorDir();
129
130
        // the path of the JMS serializer directory, relative to the vendor directory
131
        $jmsDir = DIRECTORY_SEPARATOR . 'jms' . DIRECTORY_SEPARATOR . 'serializer' . DIRECTORY_SEPARATOR . 'src';
132
133
        // try to find the path to the JMS Serializer annotations
134
        if (!file_exists($annotationDir = $vendorDir . DIRECTORY_SEPARATOR . $jmsDir)) {
135
            // stop processing, if the JMS annotations can't be found
136
            throw new \Exception(
137
                sprintf(
138
                    'The jms/serializer libarary can not be found in one of "%s"',
139
                    implode(', ', $vendorDir)
0 ignored issues
show
Bug introduced by
$vendorDir of type string is incompatible with the type array expected by parameter $pieces of implode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

139
                    implode(', ', /** @scrutinizer ignore-type */ $vendorDir)
Loading history...
140
                )
141
            );
142
        }
143
144
        // register the autoloader for the JMS serializer annotations
145
        \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\Common\Annotati...sterAutoloadNamespace() has been deprecated: this method is deprecated and will be removed in doctrine/annotations 2.0 autoloading should be deferred to the globally registered autoloader by then. For now, use @example AnnotationRegistry::registerLoader('class_exists') ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

145
        /** @scrutinizer ignore-deprecated */ \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
146
            'JMS\Serializer\Annotation',
147
            $annotationDir
148
        );
149
150
        // query whether or not, a configuration file has been specified
151
        if ($configuration = $this->input->getOption(InputOptionKeys::CONFIGURATION)) {
152
            // load the configuration from the file with the given filename
153
            $instance = $this->createConfiguration($configuration);
154
        } elseif ($magentoEdition = $this->input->getOption(InputOptionKeys::MAGENTO_EDITION)) {
155
            // use the Magento Edition that has been specified as option
156
            $instance = $this->createConfiguration($this->getDefaultConfiguration($magentoEdition, $this->getEntityTypeCode()));
157
158
            // override the Magento Edition
159
            $instance->setMagentoEdition($magentoEdition);
160
        } else {
161
            // finally, query whether or not the installation directory is a valid Magento root directory
162
            if (!$this->isMagentoRootDir($installationDir = $this->input->getOption(InputOptionKeys::INSTALLATION_DIR))) {
0 ignored issues
show
introduced by
The method isMagentoRootDir() does not exist on TechDivision\Import\Cli\SimpleConfigurationLoader. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

162
            if (!$this->/** @scrutinizer ignore-call */ isMagentoRootDir($installationDir = $this->input->getOption(InputOptionKeys::INSTALLATION_DIR))) {
Loading history...
163
                throw new \Exception(
164
                    sprintf(
165
                        'Directory "%s" specified with option "--installation-dir" is not a valid Magento root directory',
166
                        $installationDir
0 ignored issues
show
Bug introduced by
It seems like $installationDir can also be of type string[]; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

166
                        /** @scrutinizer ignore-type */ $installationDir
Loading history...
167
                    )
168
                );
169
            }
170
171
            // load the Magento Edition from the Composer configuration file
172
            $magentoEdition = $this->getEditionMapping($installationDir);
0 ignored issues
show
Bug introduced by
It seems like $installationDir can also be of type string[]; however, parameter $installationDir of TechDivision\Import\Cli\...er::getEditionMapping() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

172
            $magentoEdition = $this->getEditionMapping(/** @scrutinizer ignore-type */ $installationDir);
Loading history...
173
174
            // use the Magento Edition that has been detected by the installation directory
175
            $instance = $this->createConfiguration($this->getDefaultConfiguration($magentoEdition, $this->getEntityTypeCode()));
176
177
            // override the Magento Edition, if NOT explicitly specified
178
            $instance->setMagentoEdition($magentoEdition);
179
        }
180
181
        // query whether or not a system name has been specified as command line option, if yes override the value from the configuration file
182
        if (($this->input->hasOptionSpecified(InputOptionKeys::SYSTEM_NAME) && $this->input->getOption(InputOptionKeys::SYSTEM_NAME)) || $instance->getSystemName() === null) {
0 ignored issues
show
Bug introduced by
The method hasOptionSpecified() does not exist on Symfony\Component\Console\Input\InputInterface. Did you maybe mean hasOption()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

182
        if (($this->input->/** @scrutinizer ignore-call */ hasOptionSpecified(InputOptionKeys::SYSTEM_NAME) && $this->input->getOption(InputOptionKeys::SYSTEM_NAME)) || $instance->getSystemName() === null) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
183
            $instance->setSystemName($this->input->getOption(InputOptionKeys::SYSTEM_NAME));
184
        }
185
186
        // query whether or not a PID filename has been specified as command line option, if yes override the value from the configuration file
187
        if (($this->input->hasOptionSpecified(InputOptionKeys::PID_FILENAME) && $this->input->getOption(InputOptionKeys::PID_FILENAME)) || $instance->getPidFilename() === null) {
188
            $instance->setPidFilename($this->input->getOption(InputOptionKeys::PID_FILENAME));
189
        }
190
191
        // query whether or not a Magento installation directory has been specified as command line option, if yes override the value from the configuration file
192
        if (($this->input->hasOptionSpecified(InputOptionKeys::INSTALLATION_DIR) && $this->input->getOption(InputOptionKeys::INSTALLATION_DIR)) || $instance->getInstallationDir() === null) {
193
            $instance->setInstallationDir($this->input->getOption(InputOptionKeys::INSTALLATION_DIR));
194
        }
195
196
        // query whether or not a Magento edition has been specified as command line option, if yes override the value from the configuration file
197
        if (($this->input->hasOptionSpecified(InputOptionKeys::MAGENTO_EDITION) && $this->input->getOption(InputOptionKeys::MAGENTO_EDITION)) || $instance->getMagentoEdition() === null) {
198
            $instance->setMagentoEdition($this->input->getOption(InputOptionKeys::MAGENTO_EDITION));
199
        }
200
201
        // query whether or not a directory for the source files has been specified as command line option, if yes override the value from the configuration file
202
        if (($this->input->hasOptionSpecified(InputOptionKeys::SOURCE_DIR) && $this->input->getOption(InputOptionKeys::SOURCE_DIR)) || $instance->getSourceDir() === null) {
203
            $instance->setSourceDir($this->input->getOption(InputOptionKeys::SOURCE_DIR));
204
        }
205
206
        // return the initialized configuration instance
207
        return $instance;
208
    }
209
210
    /**
211
     * Create and return a new configuration instance from the passed configuration filename
212
     * after merging additional specified params from the commandline.
213
     *
214
     * @param string $filename The configuration filename to use
215
     *
216
     * @return \TechDivision\Import\ConfigurationInterface The configuration instance
217
     */
218
    protected function createConfiguration($filename)
219
    {
220
221
        // initialize the params specified with the --params parameter
222
        $params = null;
223
224
        // try to load the params from the commandline
225
        if ($this->input->hasOptionSpecified(InputOptionKeys::PARAMS) && $this->input->getOption(InputOptionKeys::PARAMS)) {
226
            $params = $this->input->getOption(InputOptionKeys::PARAMS);
227
        }
228
229
        // initialize the params file specified with the --params-file parameter
230
        $paramsFile = null;
231
232
        // try to load the path of the params file from the commandline
233
        if ($this->input->hasOptionSpecified(InputOptionKeys::PARAMS_FILE) && $this->input->getOption(InputOptionKeys::PARAMS_FILE)) {
234
            $paramsFile = $this->input->getOption(InputOptionKeys::PARAMS_FILE);
235
        }
236
237
        // create the configuration and return it
238
        return $this->configurationFactory->factory($filename, pathinfo($filename, PATHINFO_EXTENSION), $params, $paramsFile);
0 ignored issues
show
Unused Code introduced by
The call to TechDivision\Import\Conf...oryInterface::factory() has too many arguments starting with $params. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

238
        return $this->configurationFactory->/** @scrutinizer ignore-call */ factory($filename, pathinfo($filename, PATHINFO_EXTENSION), $params, $paramsFile);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
239
    }
240
241
    /**
242
     * Return's the DI container instance.
243
     *
244
     * @return \Symfony\Component\DependencyInjection\ContainerInterface The DI container instance
245
     */
246
    protected function getContainer()
247
    {
248
        return $this->container;
249
    }
250
251
    /**
252
     * Return's the absolute path to the actual vendor directory.
253
     *
254
     * @return string The absolute path to the actual vendor directory
255
     * @throws \Exception Is thrown, if none of the possible vendor directories can be found
256
     */
257
    protected function getVendorDir()
258
    {
259
        return $this->getContainer()->getParameter(DependencyInjectionKeys::CONFIGURATION_VENDOR_DIR);
260
    }
261
262
    /**
263
     * Return's the actual command name.
264
     *
265
     * @return string The actual command name
266
     */
267
    protected function getCommandName()
268
    {
269
        return $this->input->getArgument('command');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->input->getArgument('command') also could return the type string[] which is incompatible with the documented return type string.
Loading history...
270
    }
271
272
    /**
273
     * Return's the command's entity type code.
274
     *
275
     * @return string The command's entity type code
276
     * @throws \Exception Is thrown, if the command name can not be mapped
277
     */
278
    protected function getEntityTypeCode()
279
    {
280
281
        // try to map the command name to a entity type code
282
        if (array_key_exists($commandName = $this->getCommandName(), (array) $this->commandNameToEntityTypeCode)) {
283
            return $this->commandNameToEntityTypeCode[$commandName];
284
        }
285
286
        // throw an exception if not possible
287
        throw new \Exception(sprintf('Can\' map command name %s to a entity type', $commandName));
288
    }
289
290
    /**
291
     * Returns the mapped Magento Edition from the passed Magento installation.
292
     *
293
     * @param string $installationDir The Magento installation directory
294
     *
295
     * @return string The mapped Magento Edition, either CE or EE
296
     * @throws \Exception Is thrown, if the passed installation directory doesn't contain a valid Magento installation
297
     */
298
    protected function getEditionMapping($installationDir)
299
    {
300
301
        // load the default edition mappings from the configuration
302
        $editionMappings = $this->getContainer()->getParameter(DependencyInjectionKeys::APPLICATION_EDITION_MAPPINGS);
303
304
        // load the composer file from the Magento root directory
305
        $composer = json_decode(file_get_contents($composerFile = sprintf('%s/composer.json', $installationDir)), true);
306
307
        // try to load and explode the Magento Edition identifier from the Composer name
308
        $explodedVersion = explode('/', $composer[MagentoConfigurationKeys::COMPOSER_EDITION_NAME_ATTRIBUTE]);
309
310
        // try to locate Magento Edition
311
        if (!isset($editionMappings[$possibleEdition = end($explodedVersion)])) {
312
            throw new \Exception(
313
                sprintf(
314
                    '"%s" detected in "%s" is not a valid Magento Edition, please set Magento Edition with the "--magento-edition" option',
315
                    $possibleEdition,
316
                    $composerFile
317
                )
318
            );
319
        }
320
321
        // if Magento Edition/Version are available, load them
322
        return $editionMappings[$possibleEdition];
323
    }
324
325
    /**
326
     * Return's the default configuration for the passed Magento Edition and the actual entity type.
327
     *
328
     * @param string $magentoEdition The Magento Edition to return the configuration for
329
     * @param string $entityTypeCode The entity type code to use
330
     *
331
     * @return string The path to the default configuration
332
     */
333
    protected function getDefaultConfiguration($magentoEdition, $entityTypeCode)
334
    {
335
        return sprintf(
336
            '%s/%s/etc/%s.json',
337
            $this->getVendorDir(),
338
            $this->getDefaultConfigurationLibrary(
339
                $magentoEdition,
340
                $entityTypeCode
341
            ),
342
            $this->getDefaultConfigurationFile($entityTypeCode)
343
        );
344
    }
345
346
    /**
347
     * Return's the name of the default configuration file.
348
     *
349
     * @param string $entityTypeCode The entity type code to return the default configuration file for
350
     *
351
     * @return string The name of the entity type's default configuration file
352
     * @throws \Exception
353
     */
354
    protected function getDefaultConfigurationFile($entityTypeCode)
355
    {
356
357
        // load the default configuration file mappings from the configuration
358
        $defaultConfigurationFileMappings = $this->getContainer()->getParameter(DependencyInjectionKeys::APPLICATION_DEFAULT_CONFIGURATION_FILE_MAPPINGS);
359
360
        // query whether or not a default configuration file for the passed entity type code exists
361
        if (isset($defaultConfigurationFileMappings[$entityTypeCode])) {
362
            return $defaultConfigurationFileMappings[$entityTypeCode];
363
        }
364
365
        // throw an exception, if no default configuration file for the passed entity type is available
366
        throw new \Exception(
367
            sprintf(
368
                'Can\'t find a default configuration file for entity Type Code \'%s\' (MUST be one of catalog_product, catalog_product_price, catalog_product_inventory, catalog_category or eav_attribute)',
369
                $entityTypeCode
370
            )
371
        );
372
    }
373
374
    /**
375
     * Return's the Magento Edition and entity type's specific default library that contains
376
     * the configuration file.
377
     *
378
     * @param string $magentoEdition The Magento Edition to return the default library for
379
     * @param string $entityTypeCode The entity type code to return the default library file for
380
     *
381
     * @return string The name of the library that contains the default configuration file for the passed Magento Edition and entity type code
382
     * @throws \Exception Is thrown, if no default configuration for the passed entity type code is available
383
     */
384
    protected function getDefaultConfigurationLibrary($magentoEdition, $entityTypeCode)
385
    {
386
387
        // load the default configuration file mappings from the configuration
388
        $defaultConfigurations = $this->getContainer()->getParameter(DependencyInjectionKeys::APPLICATION_DEFAULT_CONFIGURATIONS);
389
390
        // query whether or not, a default configuration file for the passed entity type is available
391
        if (isset($defaultConfigurations[$edition = strtolower($magentoEdition)])) {
392
            if (isset($defaultConfigurations[$edition][$entityTypeCode])) {
393
                return $defaultConfigurations[$edition][$entityTypeCode];
394
            }
395
396
            // throw an exception, if the passed entity type is not supported
397
            throw new \Exception(
398
                sprintf(
399
                    'Entity Type Code \'%s\' not supported by entity type code \'%s\' (MUST be one of catalog_product, catalog_category or eav_attribute)',
400
                    $edition,
401
                    $entityTypeCode
402
                )
403
            );
404
        }
405
406
        // throw an exception, if the passed edition is not supported
407
        throw new \Exception(
408
            sprintf(
409
                'Default configuration for Magento \'%s\' not supported (MUST be one of CE or EE)',
410
                $magentoEdition
411
            )
412
        );
413
    }
414
}
415