Completed
Push — master ( 855d01...b60e57 )
by Tim
10s
created

AbstractImportCommand::configure()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 116
Code Lines 94

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 116
rs 8.2857
c 2
b 0
f 0
ccs 0
cts 114
cp 0
cc 1
eloc 94
nc 1
nop 0
crap 2

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\ConfigurationInterface;
28
use TechDivision\Import\App\Simple;
29
use TechDivision\Import\App\Utils\SynteticServiceKeys;
30
use TechDivision\Import\Cli\ConfigurationFactory;
31
use TechDivision\Import\Configuration\Jms\Configuration;
32
use TechDivision\Import\Configuration\Jms\Configuration\Database;
33
use TechDivision\Import\Configuration\Jms\Configuration\LoggerFactory;
34
use Symfony\Component\Config\FileLocator;
35
use Symfony\Component\Console\Command\Command;
36
use Symfony\Component\Console\Input\InputOption;
37
use Symfony\Component\Console\Input\InputArgument;
38
use Symfony\Component\Console\Input\InputInterface;
39
use Symfony\Component\Console\Output\OutputInterface;
40
use Symfony\Component\DependencyInjection\ContainerBuilder;
41
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
42
43
/**
44
 * The abstract import command implementation.
45
 *
46
 * @author    Tim Wagner <[email protected]>
47
 * @copyright 2016 TechDivision GmbH <[email protected]>
48
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
49
 * @link      https://github.com/techdivision/import-cli-simple
50
 * @link      http://www.techdivision.com
51
 */
52
abstract class AbstractImportCommand extends Command implements ImportCommandInterface
53
{
54
55
    /**
56
     * Configures the current command.
57
     *
58
     * @return void
59
     * @see \Symfony\Component\Console\Command\Command::configure()
60
     */
61
    protected function configure()
62
    {
63
64
        // initialize the command with the required/optional options
65
        $this->addArgument(
66
            InputArgumentKeys::OPERATION_NAME,
67
            InputArgument::OPTIONAL,
68
            'The operation that has to be used for the import, one of "add-update", "replace" or "delete"',
69
            $this->getDefaultOperation()
70
        )
71
        ->addOption(
72
            InputOptionKeys::CONFIGURATION,
73
            null,
74
            InputOption::VALUE_REQUIRED,
75
            'Specify the pathname to the configuration file to use',
76
            $this->getDefaultConfiguration()
77
        )
78
        ->addOption(
79
            InputOptionKeys::INSTALLATION_DIR,
80
            null,
81
            InputOption::VALUE_OPTIONAL,
82
            'The Magento installation directory to which the files has to be imported',
83
            $this->getMagentoInstallationDir()
84
        )
85
        ->addOption(
86
            InputOptionKeys::ENTITY_TYPE_CODE,
87
            null,
88
            InputOption::VALUE_REQUIRED,
89
            'Specify the entity type code to use'
90
        )
91
        ->addOption(
92
            InputOptionKeys::SOURCE_DIR,
93
            null,
94
            InputOption::VALUE_REQUIRED,
95
            'The directory that has to be watched for new files'
96
        )
97
        ->addOption(
98
            InputOptionKeys::TARGET_DIR,
99
            null,
100
            InputOption::VALUE_REQUIRED,
101
            'The target directory with the files that has been imported'
102
        )
103
        ->addOption(
104
            InputOptionKeys::UTILITY_CLASS_NAME,
105
            null,
106
            InputOption::VALUE_REQUIRED,
107
            'The utility class name with the SQL statements'
108
        )
109
        ->addOption(
110
            InputOptionKeys::PREFIX,
111
            null,
112
            InputOption::VALUE_REQUIRED,
113
            'The prefix of the CSV source file(s) that has/have to be imported'
114
        )
115
        ->addOption(
116
            InputOptionKeys::MAGENTO_EDITION,
117
            null,
118
            InputOption::VALUE_REQUIRED,
119
            'The Magento edition to be used, either one of CE or EE'
120
        )
121
        ->addOption(
122
            InputOptionKeys::MAGENTO_VERSION,
123
            null,
124
            InputOption::VALUE_REQUIRED,
125
            'The Magento version to be used, e. g. 2.1.2'
126
        )
127
        ->addOption(
128
            InputOptionKeys::SOURCE_DATE_FORMAT,
129
            null,
130
            InputOption::VALUE_REQUIRED,
131
            'The date format used in the CSV file(s)'
132
        )
133
        ->addOption(
134
            InputOptionKeys::USE_DB_ID,
135
            null,
136
            InputOption::VALUE_REQUIRED,
137
            'The explicit database ID used for the actual import process'
138
        )
139
        ->addOption(
140
            InputOptionKeys::DB_PDO_DSN,
141
            null,
142
            InputOption::VALUE_REQUIRED,
143
            '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'
144
        )
145
        ->addOption(
146
            InputOptionKeys::DB_USERNAME,
147
            null,
148
            InputOption::VALUE_REQUIRED,
149
            'The username used to connect to the Magento database'
150
        )
151
        ->addOption(
152
            InputOptionKeys::DB_PASSWORD,
153
            null,
154
            InputOption::VALUE_REQUIRED,
155
            'The password used to connect to the Magento database'
156
        )
157
        ->addOption(
158
            InputOptionKeys::LOG_LEVEL,
159
            null,
160
            InputOption::VALUE_REQUIRED,
161
            'The log level to use'
162
        )
163
        ->addOption(
164
            InputOptionKeys::DEBUG_MODE,
165
            null,
166
            InputOption::VALUE_REQUIRED,
167
            'Whether use the debug mode or not'
168
        )
169
        ->addOption(
170
            InputOptionKeys::PID_FILENAME,
171
            null,
172
            InputOption::VALUE_REQUIRED,
173
            'The explicit PID filename to use',
174
            sprintf('%s/%s', sys_get_temp_dir(), Configuration::PID_FILENAME)
175
        );
176
    }
177
178
    /**
179
     * Executes the current command.
180
     *
181
     * This method is not abstract because you can use this class
182
     * as a concrete class. In this case, instead of defining the
183
     * execute() method, you set the code to execute by passing
184
     * a Closure to the setCode() method.
185
     *
186
     * @param \Symfony\Component\Console\Input\InputInterface   $input  An InputInterface instance
187
     * @param \Symfony\Component\Console\Output\OutputInterface $output An OutputInterface instance
188
     *
189
     * @return null|int null or 0 if everything went fine, or an error code
190
     * @throws \LogicException When this abstract method is not implemented
191
     * @see \Symfony\Component\Console\Command\Command::execute()
192
     */
193
    protected function execute(InputInterface $input, OutputInterface $output)
194
    {
195
196
        // load the actual vendor directory
197
        $vendorDirectory = $this->getVendorDir();
198
199
        // the path of the JMS serializer directory, relative to the vendor directory
200
        $jmsDirectory = DIRECTORY_SEPARATOR . 'jms' . DIRECTORY_SEPARATOR . 'serializer' . DIRECTORY_SEPARATOR . 'src';
201
202
        // try to find the path to the JMS Serializer annotations
203
        if (!file_exists($annotationDirectory = $vendorDirectory . DIRECTORY_SEPARATOR . $jmsDirectory)) {
204
            // stop processing, if the JMS annotations can't be found
205
            throw new \Exception(
206
                sprintf(
207
                    'The jms/serializer libarary can not be found in one of %s',
208
                    implode(', ', $this->getVendorDir())
209
                )
210
            );
211
        }
212
213
        // register the autoloader for the JMS serializer annotations
214
        \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
215
            'JMS\Serializer\Annotation',
216
            $annotationDirectory
217
        );
218
219
        // load the importer configuration and set the entity type code
220
        $configuration = ConfigurationFactory::load($input);
221
222
        // initialize the DI container
223
        $container = new ContainerBuilder();
224
225
        // initialize the default loader and load the DI configuration for the this library
226
        $defaultLoader = new XmlFileLoader($container, new FileLocator($vendorDirectory));
227
228
        // load the DI configuration for all the extension libraries
229
        foreach ($this->getExtensionLibraries($configuration) as $library) {
230
            if (file_exists($diConfiguration = sprintf('%s/%s/symfony/Resources/config/services.xml', $vendorDirectory, $library))) {
231
                $defaultLoader->load($diConfiguration);
232
            }
233
        }
234
235
        // register autoloaders for additional vendor directories
236
        $customLoader = new XmlFileLoader($container, new FileLocator());
237
        foreach ($configuration->getAdditionalVendorDirs() as $additionalVendorDir) {
238
            // load the vendor directory's auto loader
239
            if (file_exists($autoLoader = $additionalVendorDir->getVendorDir() . '/autoload.php')) {
240
                require $autoLoader;
241
            }
242
243
            // load the DI configuration for the extension libraries
244
            foreach ($additionalVendorDir->getLibraries() as $library) {
245
                $customLoader->load(realpath(sprintf('%s/%s/symfony/Resources/config/services.xml', $additionalVendorDir->getVendorDir(), $library)));
246
            }
247
        }
248
249
        // add the configuration as well as input/outut instances to the DI container
250
        $container->set(SynteticServiceKeys::INPUT, $input);
251
        $container->set(SynteticServiceKeys::OUTPUT, $output);
252
        $container->set(SynteticServiceKeys::CONFIGURATION, $configuration);
253
        $container->set(SynteticServiceKeys::APPLICATION, $this->getApplication());
0 ignored issues
show
Bug introduced by
It seems like $this->getApplication() can be null; however, set() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
254
255
        // initialize the PDO connection
256
        $dsn = $configuration->getDatabase()->getDsn();
257
        $username = $configuration->getDatabase()->getUsername();
258
        $password = $configuration->getDatabase()->getPassword();
259
        $connection = new \PDO($dsn, $username, $password);
260
        $connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
261
262
        // add the PDO connection to the DI container
263
        $container->set(SynteticServiceKeys::CONNECTION, $connection);
264
265
        // initialize the system logger
266
        $loggers = array();
267
268
        // initialize the default system logger
269
        $systemLogger = new Logger('techdivision/import');
270
        $systemLogger->pushHandler(
271
            new ErrorLogHandler(
272
                ErrorLogHandler::OPERATING_SYSTEM,
273
                $configuration->getLogLevel()
274
            )
275
        );
276
277
        // add it to the array
278
        $loggers[LoggerKeys::SYSTEM] = $systemLogger;
279
280
        // append the configured loggers or override the default one
281
        foreach ($configuration->getLoggers() as $loggerConfiguration) {
282
            // load the factory class that creates the logger instance
283
            $loggerFactory = $loggerConfiguration->getFactory();
284
            // create the logger instance and add it to the available loggers
285
            $loggers[$loggerConfiguration->getName()] = $loggerFactory::factory($loggerConfiguration);
286
        }
287
288
        // add the system loggers to the DI container
289
        $container->set(SynteticServiceKeys::LOGGERS, $loggers);
0 ignored issues
show
Documentation introduced by
$loggers is of type array<integer|string,object<Monolog\Logger>>, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
290
291
        // start the import process
292
        $container->get(SynteticServiceKeys::SIMPLE)->process();
293
    }
294
295
    /**
296
     * Return's the array with the magento specific extension libraries.
297
     *
298
     * @param \TechDivision\Import\ConfigurationInterface $configuration The configuration instance
299
     *
300
     * @return array The magento edition specific extension libraries
301
     */
302
    public function getExtensionLibraries(ConfigurationInterface $configuration)
303
    {
304
305
        // return the array with the Magento Edition specific libraries
306
        return array_merge(
307
            Simple::getDefaultLibraries($configuration->getMagentoEdition()),
308
            $configuration->getExtensionLibraries()
309
        );
310
    }
311
312
    /**
313
     * Return's the absolute path to the actual vendor directory.
314
     *
315
     * @return string The absolute path to the actual vendor directory
316
     * @throws \Exception Is thrown, if none of the possible vendor directories can be found
317
     */
318
    public function getVendorDir()
319
    {
320
321
        // the possible paths to the vendor directory
322
        $possibleVendorDirectories = array(
323
            dirname(dirname(dirname(dirname(dirname(__DIR__))))) . DIRECTORY_SEPARATOR . 'vendor',
324
            dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'vendor'
325
        );
326
327
        // try to find the path to the JMS Serializer annotations
328
        foreach ($possibleVendorDirectories as $possibleVendorDirectory) {
329
            // return the directory as vendor directory if available
330
            if (is_dir($possibleVendorDirectory)) {
331
                return $possibleVendorDirectory;
332
            }
333
        }
334
335
        // stop processing, if NO vendor directory is available
336
        throw new \Exception(
337
            sprintf(
338
                'None of the possible vendor directories %s is available',
339
                implode(', ', $possibleVendorDirectories)
340
            )
341
        );
342
    }
343
344
    /**
345
     * Return's the Magento installation directory, assuming that this is the
346
     * actual directory.
347
     *
348
     * @return string The Magento installation directory
349
     */
350
    public function getMagentoInstallationDir()
351
    {
352
        return getcwd();
353
    }
354
355
    /**
356
     * Return's the given entity type's specific default configuration file.
357
     *
358
     * @return string The name of the library to query for the default configuration file
359
     * @throws \Exception Is thrown, if no default configuration for the passed entity type is available
360
     */
361
    public function getDefaultImportDir()
362
    {
363
        return sprintf('%s/var/importexport', $this->getMagentoInstallationDir());
364
    }
365
366
    /**
367
     * Return's the given entity type's specific default configuration file.
368
     *
369
     * @return string The name of the library to query for the default configuration file
370
     * @throws \Exception Is thrown, if no default configuration for the passed entity type is available
371
     */
372
    public function getDefaultConfiguration()
373
    {
374
        return sprintf(
375
            '%s/%s/etc/techdivision-import.json',
376
            $this->getVendorDir(),
377
            Simple::getDefaultConfiguration($this->getEntityTypeCode())
378
        );
379
    }
380
381
    /**
382
     * Return's the default operation.
383
     *
384
     * @return string The default operation
385
     */
386
    public function getDefaultOperation()
387
    {
388
        return OperationKeys::ADD_UPDATE;
389
    }
390
}
391