Completed
Push — 2.x ( 22170b...61914f )
by Tim
05:43
created

ConfigurationLoader::initializeAliases()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 7
cp 0
rs 9.9
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
/**
4
 * TechDivision\Import\Cli\ConfigurationLoader
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 Psr\Log\LogLevel;
24
use Ramsey\Uuid\Uuid;
25
use TechDivision\Import\Cli\Command\InputArgumentKeys;
26
use TechDivision\Import\Cli\Command\InputOptionKeys;
27
use TechDivision\Import\Cli\Utils\MagentoConfigurationKeys;
28
use TechDivision\Import\Configuration\Jms\Configuration\Database;
29
use TechDivision\Import\Utils\EntityTypeCodes;
30
use TechDivision\Import\ConfigurationInterface;
31
32
/**
33
 * The configuration loader implementation.
34
 *
35
 * @author    Tim Wagner <[email protected]>
36
 * @copyright 2016 TechDivision GmbH <[email protected]>
37
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
38
 * @link      https://github.com/techdivision/import-cli-simple
39
 * @link      http://www.techdivision.com
40
 */
41
class ConfigurationLoader extends SimpleConfigurationLoader
42
{
43
44
    /**
45
     * The array with the default entity type code => import directory mappings.
46
     *
47
     * @var array
48
     */
49
    protected $defaultDirectories = array(
50
        EntityTypeCodes::CATALOG_PRODUCT               => 'products',
51
        EntityTypeCodes::CATALOG_PRODUCT_PRICE         => 'products',
52
        EntityTypeCodes::CATALOG_PRODUCT_TIER_PRICE    => 'products',
53
        EntityTypeCodes::CATALOG_PRODUCT_INVENTORY     => 'products',
54
        EntityTypeCodes::CATALOG_PRODUCT_INVENTORY_MSI => 'products',
55
        EntityTypeCodes::CATALOG_CATEGORY              => 'categories',
56
        EntityTypeCodes::EAV_ATTRIBUTE                 => 'attributes',
57
        EntityTypeCodes::EAV_ATTRIBUTE_SET             => 'attributes',
58
        EntityTypeCodes::CUSTOMER                      => 'customers',
59
        EntityTypeCodes::CUSTOMER_ADDRESS              => 'customers'
60
    );
61
62
    /**
63
     * The Magento Edition specific default libraries.
64
     *
65
     * @var array
66
     */
67
    protected $defaultLibraries = array(
68
        'ce' => array(
69
            'techdivision/import-app-simple',
70
            'techdivision/import',
71
            'techdivision/import-attribute',
72
            'techdivision/import-attribute-set',
73
            'techdivision/import-category',
74
            'techdivision/import-customer',
75
            'techdivision/import-customer-address',
76
            'techdivision/import-product',
77
            'techdivision/import-product-tier-price',
78
            'techdivision/import-product-url-rewrite',
79
            'techdivision/import-product-bundle',
80
            'techdivision/import-product-link',
81
            'techdivision/import-product-media',
82
            'techdivision/import-product-variant',
83
            'techdivision/import-product-grouped'
84
        ),
85
        'ee' => array(
86
            'techdivision/import-app-simple',
87
            'techdivision/import',
88
            'techdivision/import-ee',
89
            'techdivision/import-attribute',
90
            'techdivision/import-attribute-set',
91
            'techdivision/import-category',
92
            'techdivision/import-category-ee',
93
            'techdivision/import-customer',
94
            'techdivision/import-customer-address',
95
            'techdivision/import-product',
96
            'techdivision/import-product-tier-price',
97
            'techdivision/import-product-url-rewrite',
98
            'techdivision/import-product-ee',
99
            'techdivision/import-product-bundle',
100
            'techdivision/import-product-bundle-ee',
101
            'techdivision/import-product-link',
102
            'techdivision/import-product-link-ee',
103
            'techdivision/import-product-media',
104
            'techdivision/import-product-media-ee',
105
            'techdivision/import-product-variant',
106
            'techdivision/import-product-variant-ee',
107
            'techdivision/import-product-grouped',
108
            'techdivision/import-product-grouped-ee'
109
        )
110
    );
111
112
    /**
113
     * Factory implementation to create a new initialized configuration instance.
114
     *
115
     * If command line options are specified, they will always override the
116
     * values found in the configuration file.
117
     *
118
     * @return \TechDivision\Import\ConfigurationInterface The configuration instance
119
     * @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
120
     */
121
    public function load()
122
    {
123
124
        // load the configuration instance
125
        $instance = parent::load();
126
127
        // set the serial that has been specified as command line option (or the default value)
128
        $instance->setSerial($this->input->getOption(InputOptionKeys::SERIAL));
129
130
        // query whether or not an operation name has been specified as command line
131
        // option, if yes override the value from the configuration file
132
        if ($operationName = $this->input->getArgument(InputArgumentKeys::OPERATION_NAME)) {
133
            $instance->setOperationName($operationName);
134
        }
135
136
        // query whether or not a Magento version has been specified as command line
137
        // option, if yes override the value from the configuration file
138
        if ($magentoVersion = $this->input->getOption(InputOptionKeys::MAGENTO_VERSION)) {
139
            $instance->setMagentoVersion($magentoVersion);
140
        }
141
142
        // query whether or not a directory containing the imported files has been specified as command line
143
        // option, if yes override the value from the configuration file
144
        if ($targetDir = $this->input->getOption(InputOptionKeys::TARGET_DIR)) {
145
            $instance->setTargetDir($targetDir);
146
        }
147
148
        // query whether or not a directory containing the archived imported files has been specified as command line
149
        // option, if yes override the value from the configuration file
150
        if ($archiveDir = $this->input->getOption(InputOptionKeys::ARCHIVE_DIR)) {
151
            $instance->setArchiveDir($archiveDir);
152
        }
153
154
        // query whether or not the debug mode has been specified as command line
155
        // option, if yes override the value from the configuration file
156
        if ($archiveArtefacts = $this->input->getOption(InputOptionKeys::ARCHIVE_ARTEFACTS)) {
157
            $instance->setArchiveArtefacts($instance->mapBoolean($archiveArtefacts));
158
        }
159
160
        // query whether or not a source date format has been specified as command
161
        // line  option, if yes override the value from the configuration file
162
        if ($sourceDateFormat = $this->input->getOption(InputOptionKeys::SOURCE_DATE_FORMAT)) {
163
            $instance->setSourceDateFormat($sourceDateFormat);
164
        }
165
166
        // query whether or not the debug mode has been specified as command line
167
        // option, if yes override the value from the configuration file
168
        if ($debugMode = $this->input->getOption(InputOptionKeys::DEBUG_MODE)) {
169
            $instance->setDebugMode($instance->mapBoolean($debugMode));
170
        }
171
172
        // query whether or not the log level has been specified as command line
173
        // option, if yes override the value from the configuration file
174
        if ($logLevel = $this->input->getOption(InputOptionKeys::LOG_LEVEL)) {
175
            $instance->setLogLevel($logLevel);
176
        }
177
178
        // query whether or not the single transaction flag has been specified as command line
179
        // option, if yes override the value from the configuration file
180
        if ($singleTransaction = $this->input->getOption(InputOptionKeys::SINGLE_TRANSACTION)) {
181
            $instance->setSingleTransaction($instance->mapBoolean($singleTransaction));
182
        }
183
184
        // query whether or not the cache flag has been specified as command line
185
        // option, if yes override the value from the configuration file
186
        if ($cacheEnabled = $this->input->getOption(InputOptionKeys::CACHE_ENABLED)) {
187
            $instance->setCacheEnabled($instance->mapBoolean($cacheEnabled));
188
        }
189
190
        // query whether or not we've an valid Magento root directory specified
191
        if ($this->isMagentoRootDir($installationDir = $instance->getInstallationDir())) {
192
            // if yes, add the database configuration
193
            $instance->addDatabase($this->getMagentoDbConnection($installationDir));
194
195
            // add the source directory if NOT specified in the configuration file
196
            if (($sourceDir = $instance->getSourceDir()) === null) {
197
                $instance->setSourceDir($sourceDir = sprintf('%s/var/importexport', $installationDir));
198
            }
199
200
            // add the target directory if NOT specified in the configuration file
201
            if ($instance->getTargetDir() === null) {
202
                $instance->setTargetDir($sourceDir);
203
            }
204
        }
205
206
        // query whether or not a DB ID has been specified as command line
207
        // option, if yes override the value from the configuration file
208
        if ($useDbId = $this->input->getOption(InputOptionKeys::USE_DB_ID)) {
209
            $instance->setUseDbId($useDbId);
210
        } else {
211
            // query whether or not a PDO DSN has been specified as command line
212
            // option, if yes override the value from the configuration file
213
            if ($dsn = $this->input->getOption(InputOptionKeys::DB_PDO_DSN)) {
214
                // first REMOVE all other database configurations
215
                $instance->clearDatabases();
216
217
                // add the database configuration
218
                $instance->addDatabase(
219
                    $this->newDatabaseConfiguration(
220
                        $dsn,
221
                        $this->input->getOption(InputOptionKeys::DB_USERNAME),
222
                        $this->input->getOption(InputOptionKeys::DB_PASSWORD)
223
                    )
224
                );
225
            }
226
        }
227
228
        // extend the plugins with the main configuration instance
229
        /** @var \TechDivision\Import\Cli\Configuration\Subject $subject */
230
        foreach ($instance->getPlugins() as $plugin) {
231
            // set the configuration instance on the plugin
232
            $plugin->setConfiguration($instance);
233
234
            // query whether or not the plugin has subjects configured
235
            if ($subjects = $plugin->getSubjects()) {
236
                // extend the plugin's subjects with the main configuration instance
237
                /** @var \TechDivision\Import\Cli\Configuration\Subject $subject */
238
                foreach ($subjects as $subject) {
239
                    // set the configuration instance on the subject
240
                    $subject->setConfiguration($instance);
241
                }
242
            }
243
        }
244
245
        // query whether or not the debug mode is enabled and log level
246
        // has NOT been overwritten with a commandline option
247
        if ($instance->isDebugMode() && !$this->input->getOption(InputOptionKeys::LOG_LEVEL)) {
248
            // set debug log level, if log level has NOT been overwritten on command line
249
            $instance->setLogLevel(LogLevel::DEBUG);
250
        }
251
252
        // prepend the array with the Magento Edition specific core libraries
253
        $instance->setExtensionLibraries(
254
            array_merge(
255
                $this->getDefaultLibraries($instance->getMagentoEdition()),
256
                $instance->getExtensionLibraries()
257
            )
258
        );
259
260
        // load the extension libraries, if configured
261
        $this->libraryLoader->load($instance);
262
263
        // register the configured aliases in the DI container, this MUST
264
        // happen after the libraries have been loaded, else it would not
265
        // be possible to override existing aliases
266
        $this->initializeAliases($instance);
267
268
        // return the initialized configuration instance
269
        return $instance;
270
    }
271
272
    /**
273
     * Query whether or not, the passed directory is a Magento root directory.
274
     *
275
     * @param string $dir The directory to query
276
     *
277
     * @return boolean TRUE if the directory is a Magento root directory, else FALSE
278
     */
279
    protected function isMagentoRootDir($dir)
280
    {
281
        return is_file($this->getMagentoEnv($dir));
282
    }
283
284
    /**
285
     * Return's the path to the Magento file with the environment configuration.
286
     *
287
     * @param string $dir The path to the Magento root directory
288
     *
289
     * @return string The path to the Magento file with the environment configuration
290
     */
291
    protected function getMagentoEnv($dir)
292
    {
293
        return sprintf('%s/app/etc/env.php', $dir);
294
    }
295
296
    /**
297
     * Return's the requested Magento DB connction data.
298
     *
299
     * @param string $dir            The path to the Magento root directory
300
     * @param string $connectionName The connection name to return the data for
301
     *
302
     * @return array The connection data
303
     * @throws \Exception Is thrown, if the requested DB connection is not available
304
     */
305
    protected function getMagentoDbConnection($dir, $connectionName = 'default')
306
    {
307
308
        // load the magento environment
309
        $env = require $this->getMagentoEnv($dir);
310
311
        // query whether or not, the requested connection is available
312
        if (isset($env[MagentoConfigurationKeys::DB][MagentoConfigurationKeys::CONNECTION][$connectionName])) {
313
            // load the connection data
314
            $connection = $env[MagentoConfigurationKeys::DB][MagentoConfigurationKeys::CONNECTION][$connectionName];
315
316
            // create and return a new database configuration
317
            return $this->newDatabaseConfiguration(
318
                $this->newDsn($connection[MagentoConfigurationKeys::HOST], $connection[MagentoConfigurationKeys::DBNAME]),
319
                $connection[MagentoConfigurationKeys::USERNAME],
320
                $connection[MagentoConfigurationKeys::PASSWORD],
321
                false
322
            );
323
        }
324
325
        // throw an execption if not
326
        throw new \Exception(sprintf('Requested Magento DB connection "%s" not found in Magento "%s"', $connectionName, $dir));
327
    }
328
329
    /**
330
     * Create's and return's a new database configuration instance, initialized with
331
     * the passed values.
332
     *
333
     * @param string      $dsn      The DSN to use
334
     * @param string      $username The username to  use
335
     * @param string|null $password The passed to use
336
     * @param boolean     $default  TRUE if this should be the default connection
337
     * @param string      $id       The ID to use
338
     *
339
     * @return \TechDivision\Import\Configuration\Jms\Configuration\Database The database configuration instance
340
     */
341
    protected function newDatabaseConfiguration($dsn, $username = 'root', $password = null, $default = true, $id = null)
342
    {
343
344
        // initialize a new database configuration
345
        $database = new Database();
346
        $database->setDsn($dsn);
347
        $database->setDefault($default);
348
        $database->setUsername($username);
349
350
        // query whether or not an ID has been passed
351
        if ($id === null) {
352
            $id = Uuid::uuid4()->__toString();
353
        }
354
355
        // set the ID
356
        $database->setId($id);
357
358
        // query whether or not a password has been passed
359
        if ($password) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $password of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
360
            $database->setPassword($password);
361
        }
362
363
        // return the database configuration
364
        return $database;
365
    }
366
367
    /**
368
     * Create's and return's a new DSN from the passed values.
369
     *
370
     * @param string $host    The host to use
371
     * @param string $dbName  The database name to use
372
     * @param string $charset The charset to use
373
     *
374
     * @return string The DSN
375
     */
376
    protected function newDsn($host, $dbName, $charset = 'utf8')
377
    {
378
        return sprintf('mysql:host=%s;dbname=%s;charset=%s', $host, $dbName, $charset);
379
    }
380
381
    /**
382
     * Return's the Magento Edition specific default libraries. Supported Magento Editions are CE or EE.
383
     *
384
     * @param string $magentoEdition The Magento Edition to return the libraries for
385
     *
386
     * @return array The Magento Edition specific default libraries
387
     * @throws \Exception Is thrown, if the passed Magento Edition is NOT supported
388
     */
389
    protected function getDefaultLibraries($magentoEdition)
390
    {
391
392
        // query whether or not, default libraries for the passed edition are available
393
        if (isset($this->defaultLibraries[$edition = strtolower($magentoEdition)])) {
394
            return $this->defaultLibraries[$edition];
395
        }
396
397
        // throw an exception, if the passed edition is not supported
398
        throw new \Exception(
399
            sprintf(
400
                'Default libraries for Magento \'%s\' not supported (MUST be one of CE or EE)',
401
                $magentoEdition
402
            )
403
        );
404
    }
405
406
    /**
407
     * Return's the entity types specific default import directory.
408
     *
409
     * @param string $entityTypeCode The entity type code to return the default import directory for
410
     *
411
     * @return string The default default import directory
412
     * @throws \Exception Is thrown, if no default import directory for the passed entity type code is available
413
     */
414
    protected function getDefaultDirectory($entityTypeCode)
415
    {
416
417
        // query whether or not, a default configuration file for the passed entity type is available
418
        if (isset($this->defaultDirectories[$entityTypeCode])) {
419
            return $this->defaultDirectories[$entityTypeCode];
420
        }
421
422
        // throw an exception, if the passed entity type is not supported
423
        throw new \Exception(
424
            sprintf(
425
                'Entity Type Code \'%s\' not supported (MUST be one of catalog_product or catalog_category)',
426
                $entityTypeCode
427
            )
428
        );
429
    }
430
431
    /**
432
     * Registers the configured aliases in the DI container.
433
     *
434
     * @param \TechDivision\Import\ConfigurationInterface $configuration The configuration with the aliases to register
435
     *
436
     * @return void
437
     */
438
    protected function initializeAliases(ConfigurationInterface $configuration)
439
    {
440
441
        // load the DI aliases
442
        $aliases = $configuration->getAliases();
443
444
        // register the DI aliases
445
        foreach ($aliases as $alias) {
446
            $this->getContainer()->setAlias($alias->getId(), $alias->getTarget());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Depend...tion\ContainerInterface as the method setAlias() does only exist in the following implementations of said interface: Container14\ProjectServiceContainer, Symfony\Component\Depend...urationContainerBuilder, Symfony\Component\Depend...ection\ContainerBuilder.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
447
        }
448
    }
449
}
450