Completed
Push — master ( 2fc146...c7d720 )
by Tim
11s
created

AbstractImportCommand::execute()   C

Complexity

Conditions 8
Paths 17

Size

Total Lines 108
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 0
loc 108
ccs 0
cts 70
cp 0
rs 5.2676
cc 8
eloc 53
nc 17
nop 2
crap 72

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * TechDivision\Import\Cli\Command\ImportCommandTrait
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\Command;
22
23
use Monolog\Logger;
24
use Monolog\Handler\ErrorLogHandler;
25
use TechDivision\Import\Utils\LoggerKeys;
26
use TechDivision\Import\Utils\OperationKeys;
27
use TechDivision\Import\App\Simple;
28
use TechDivision\Import\App\Utils\SynteticServiceKeys;
29
use TechDivision\Import\Cli\Utils\DependencyInjectionKeys;
30
use TechDivision\Import\Configuration\Jms\Configuration;
31
use TechDivision\Import\Configuration\Jms\Configuration\Database;
32
use TechDivision\Import\Configuration\Jms\Configuration\LoggerFactory;
33
use Symfony\Component\Config\FileLocator;
34
use Symfony\Component\Console\Command\Command;
35
use Symfony\Component\Console\Input\InputOption;
36
use Symfony\Component\Console\Input\InputArgument;
37
use Symfony\Component\Console\Input\InputInterface;
38
use Symfony\Component\Console\Output\OutputInterface;
39
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
40
41
/**
42
 * The abstract import command implementation.
43
 *
44
 * @author    Tim Wagner <[email protected]>
45
 * @copyright 2016 TechDivision GmbH <[email protected]>
46
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
47
 * @link      https://github.com/techdivision/import-cli-simple
48
 * @link      http://www.techdivision.com
49
 */
50
abstract class AbstractImportCommand extends Command implements ImportCommandInterface
51
{
52
53
    /**
54
     * Configures the current command.
55
     *
56
     * @return void
57
     * @see \Symfony\Component\Console\Command\Command::configure()
58
     */
59
    protected function configure()
60
    {
61
62
        // initialize the command with the required/optional options
63
        $this->addArgument(
64
            InputArgumentKeys::OPERATION_NAME,
65
            InputArgument::OPTIONAL,
66
            'The operation that has to be used for the import, one of "add-update", "replace" or "delete"',
67
            OperationKeys::ADD_UPDATE
68
        )
69
        ->addOption(
70
            InputOptionKeys::INSTALLATION_DIR,
71
            null,
72
            InputOption::VALUE_REQUIRED,
73
            'The Magento installation directory to which the files has to be imported',
74
            getcwd()
75
        )
76
        ->addOption(
77
            InputOptionKeys::SYSTEM_NAME,
78
            null,
79
            InputOption::VALUE_REQUIRED,
80
            'Specify the system name to use',
81
            gethostname()
82
        )
83
        ->addOption(
84
            InputOptionKeys::PID_FILENAME,
85
            null,
86
            InputOption::VALUE_REQUIRED,
87
            'The explicit PID filename to use',
88
            sprintf('%s/%s', sys_get_temp_dir(), Configuration::PID_FILENAME)
89
        )
90
        ->addOption(
91
            InputOptionKeys::MAGENTO_EDITION,
92
            null,
93
            InputOption::VALUE_REQUIRED,
94
            'The Magento edition to be used, either one of "CE" or "EE"'
95
        )
96
        ->addOption(
97
            InputOptionKeys::MAGENTO_VERSION,
98
            null,
99
            InputOption::VALUE_REQUIRED,
100
            'The Magento version to be used, e. g. "2.1.2"'
101
        )
102
        ->addOption(
103
            InputOptionKeys::CONFIGURATION,
104
            null,
105
            InputOption::VALUE_REQUIRED,
106
            'Specify the pathname to the configuration file to use'
107
        )
108
        ->addOption(
109
            InputOptionKeys::ENTITY_TYPE_CODE,
110
            null,
111
            InputOption::VALUE_REQUIRED,
112
            'Specify the entity type code to use, either one of "catalog_product", "catalog_category" or "eav_attribute"'
113
        )
114
        ->addOption(
115
            InputOptionKeys::SOURCE_DIR,
116
            null,
117
            InputOption::VALUE_REQUIRED,
118
            'The directory that has to be watched for new files'
119
        )
120
        ->addOption(
121
            InputOptionKeys::TARGET_DIR,
122
            null,
123
            InputOption::VALUE_REQUIRED,
124
            'The target directory with the files that has been imported'
125
        )
126
        ->addOption(
127
            InputOptionKeys::ARCHIVE_DIR,
128
            null,
129
            InputOption::VALUE_REQUIRED,
130
            'The directory the imported files will be archived in'
131
        )
132
        ->addOption(
133
            InputOptionKeys::SOURCE_DATE_FORMAT,
134
            null,
135
            InputOption::VALUE_REQUIRED,
136
            'The date format used in the CSV file(s)'
137
        )
138
        ->addOption(
139
            InputOptionKeys::USE_DB_ID,
140
            null,
141
            InputOption::VALUE_REQUIRED,
142
            'The explicit database ID used for the actual import process'
143
        )
144
        ->addOption(
145
            InputOptionKeys::DB_PDO_DSN,
146
            null,
147
            InputOption::VALUE_REQUIRED,
148
            'The DSN used to connect to the Magento database where the data has to be imported, e. g. mysql:host=127.0.0.1;dbname=magento;charset=utf8'
149
        )
150
        ->addOption(
151
            InputOptionKeys::DB_USERNAME,
152
            null,
153
            InputOption::VALUE_REQUIRED,
154
            'The username used to connect to the Magento database'
155
        )
156
        ->addOption(
157
            InputOptionKeys::DB_PASSWORD,
158
            null,
159
            InputOption::VALUE_REQUIRED,
160
            'The password used to connect to the Magento database'
161
        )
162
        ->addOption(
163
            InputOptionKeys::LOG_LEVEL,
164
            null,
165
            InputOption::VALUE_REQUIRED,
166
            'The log level to use'
167
        )
168
        ->addOption(
169
            InputOptionKeys::DEBUG_MODE,
170
            null,
171
            InputOption::VALUE_REQUIRED,
172
            'Whether use the debug mode or not'
173
        );
174
    }
175
176
    /**
177
     * Executes the current command.
178
     *
179
     * This method is not abstract because you can use this class
180
     * as a concrete class. In this case, instead of defining the
181
     * execute() method, you set the code to execute by passing
182
     * a Closure to the setCode() method.
183
     *
184
     * @param \Symfony\Component\Console\Input\InputInterface   $input  An InputInterface instance
185
     * @param \Symfony\Component\Console\Output\OutputInterface $output An OutputInterface instance
186
     *
187
     * @return null|int null or 0 if everything went fine, or an error code
188
     * @throws \LogicException When this abstract method is not implemented
189
     * @see \Symfony\Component\Console\Command\Command::execute()
190
     */
191
    protected function execute(InputInterface $input, OutputInterface $output)
192
    {
193
194
        // load the actual vendor directory
195
        $vendorDir = $this->getVendorDir();
196
197
        // load the container instance
198
        $container = $this->getApplication()->getContainer();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Console\Application as the method getContainer() does only exist in the following sub-classes of Symfony\Component\Console\Application: TechDivision\Import\Cli\Application. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
199
200
        // initialize the default loader and load the DI configuration for the this library
201
        $defaultLoader = new XmlFileLoader($container, new FileLocator($vendorDir));
202
203
        // initialize and load the importer configuration
204
        /** @var \TechDivision\Import\ConfigurationInterface $configuration */
205
        $configuration = $container->get(DependencyInjectionKeys::CONFIGURATION_LOADER)->load($input, $this->getEntityTypeCode());
206
207
        // load the DI configuration for all the extension libraries
208
        foreach ($configuration->getExtensionLibraries() as $library) {
209
            if (file_exists($diConfiguration = sprintf('%s/%s/symfony/Resources/config/services.xml', $vendorDir, $library))) {
210
                $defaultLoader->load($diConfiguration);
211
            } else {
212
                throw new \Exception(
213
                    sprintf(
214
                        'Can\'t load DI configuration for library "%s"',
215
                        $diConfiguration
216
                    )
217
                );
218
            }
219
        }
220
221
        // register autoloaders for additional vendor directories
222
        $customLoader = new XmlFileLoader($container, new FileLocator());
223
        foreach ($configuration->getAdditionalVendorDirs() as $additionalVendorDir) {
224
            // load the vendor directory's auto loader
225
            if (file_exists($autoLoader = $additionalVendorDir->getVendorDir() . '/autoload.php')) {
226
                require $autoLoader;
227
            } else {
228
                throw new \Exception(
229
                    sprintf(
230
                        'Can\'t find autoloader in configured additional vendor directory "%s"',
231
                        $additionalVendorDir->getVendorDir()
232
                    )
233
                );
234
            }
235
236
            // try to load the DI configuration for the configured extension libraries
237
            foreach ($additionalVendorDir->getLibraries() as $library) {
238
                // prepare the DI configuration filename
239
                $diConfiguration = realpath(sprintf('%s/%s/symfony/Resources/config/services.xml', $additionalVendorDir->getVendorDir(), $library));
240
                // try to load the filename
241
                if (file_exists($diConfiguration)) {
242
                    $customLoader->load($diConfiguration);
243
                } else {
244
                    throw new \Exception(
245
                        sprintf(
246
                            'Can\'t load DI configuration for library "%s"',
247
                            $diConfiguration
248
                        )
249
                    );
250
                }
251
            }
252
        }
253
254
        // add the configuration as well as input/outut instances to the DI container
255
        $container->set(SynteticServiceKeys::INPUT, $input);
256
        $container->set(SynteticServiceKeys::OUTPUT, $output);
257
        $container->set(SynteticServiceKeys::CONFIGURATION, $configuration);
258
        $container->set(SynteticServiceKeys::APPLICATION, $this->getApplication());
259
260
        // initialize the PDO connection
261
        $dsn = $configuration->getDatabase()->getDsn();
262
        $username = $configuration->getDatabase()->getUsername();
263
        $password = $configuration->getDatabase()->getPassword();
264
        $connection = new \PDO($dsn, $username, $password);
265
        $connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
266
267
        // add the PDO connection to the DI container
268
        $container->set(SynteticServiceKeys::CONNECTION, $connection);
269
270
        // initialize the system logger
271
        $loggers = array();
272
273
        // initialize the default system logger
274
        $systemLogger = new Logger('techdivision/import');
275
        $systemLogger->pushHandler(
276
            new ErrorLogHandler(
277
                ErrorLogHandler::OPERATING_SYSTEM,
278
                $configuration->getLogLevel()
279
            )
280
        );
281
282
        // add it to the array
283
        $loggers[LoggerKeys::SYSTEM] = $systemLogger;
284
285
        // append the configured loggers or override the default one
286
        foreach ($configuration->getLoggers() as $loggerConfiguration) {
287
            // load the factory class that creates the logger instance
288
            $loggerFactory = $loggerConfiguration->getFactory();
289
            // create the logger instance and add it to the available loggers
290
            $loggers[$loggerConfiguration->getName()] = $loggerFactory::factory($configuration, $loggerConfiguration);
291
        }
292
293
        // add the system loggers to the DI container
294
        $container->set(SynteticServiceKeys::LOGGERS, $loggers);
295
296
        // start the import process
297
        $container->get(SynteticServiceKeys::SIMPLE)->process();
298
    }
299
300
    /**
301
     * Return's the absolute path to the actual vendor directory.
302
     *
303
     * @return string The absolute path to the actual vendor directory
304
     * @throws \Exception Is thrown, if none of the possible vendor directories can be found
305
     */
306
    public function getVendorDir()
307
    {
308
        return $this->getApplication()
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Console\Application as the method getContainer() does only exist in the following sub-classes of Symfony\Component\Console\Application: TechDivision\Import\Cli\Application. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
309
                    ->getContainer()
310
                    ->getParameter(DependencyInjectionKeys::CONFIGURATION_VENDOR_DIR);
311
    }
312
}
313