Completed
Pull Request — master (#85)
by Tim
07:49
created

AbstractImportCommand::getDefaultOperation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 4
cp 0
cc 1
eloc 2
nc 1
nop 0
crap 2
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\ConfigurationFactory;
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\ContainerBuilder;
40
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
41
42
/**
43
 * The abstract import command implementation.
44
 *
45
 * @author    Tim Wagner <[email protected]>
46
 * @copyright 2016 TechDivision GmbH <[email protected]>
47
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
48
 * @link      https://github.com/techdivision/import-cli-simple
49
 * @link      http://www.techdivision.com
50
 */
51
abstract class AbstractImportCommand extends Command implements ImportCommandInterface
52
{
53
54
    /**
55
     * Configures the current command.
56
     *
57
     * @return void
58
     * @see \Symfony\Component\Console\Command\Command::configure()
59
     */
60
    protected function configure()
61
    {
62
63
        // initialize the command with the required/optional options
64
        $this->addArgument(
65
            InputArgumentKeys::OPERATION_NAME,
66
            InputArgument::OPTIONAL,
67
            'The operation that has to be used for the import, one of "add-update", "replace" or "delete"',
68
            OperationKeys::ADD_UPDATE
69
        )
70
        ->addOption(
71
            InputOptionKeys::INSTALLATION_DIR,
72
            null,
73
            InputOption::VALUE_REQUIRED,
74
            'The Magento installation directory to which the files has to be imported',
75
            getcwd()
76
        )
77
        ->addOption(
78
            InputOptionKeys::SYSTEM_NAME,
79
            null,
80
            InputOption::VALUE_REQUIRED,
81
            'Specify the system name to use',
82
            gethostname()
83
        )
84
        ->addOption(
85
            InputOptionKeys::PID_FILENAME,
86
            null,
87
            InputOption::VALUE_REQUIRED,
88
            'The explicit PID filename to use',
89
            sprintf('%s/%s', sys_get_temp_dir(), Configuration::PID_FILENAME)
90
        )
91
        ->addOption(
92
            InputOptionKeys::MAGENTO_EDITION,
93
            null,
94
            InputOption::VALUE_REQUIRED,
95
            'The Magento edition to be used, either one of "CE" or "EE"'
96
        )
97
        ->addOption(
98
            InputOptionKeys::MAGENTO_VERSION,
99
            null,
100
            InputOption::VALUE_REQUIRED,
101
            'The Magento version to be used, e. g. "2.1.2"'
102
        )
103
        ->addOption(
104
            InputOptionKeys::CONFIGURATION,
105
            null,
106
            InputOption::VALUE_REQUIRED,
107
            'Specify the pathname to the configuration file to use'
108
        )
109
        ->addOption(
110
            InputOptionKeys::ENTITY_TYPE_CODE,
111
            null,
112
            InputOption::VALUE_REQUIRED,
113
            'Specify the entity type code to use, either one of "catalog_product", "catalog_category" or "eav_attribute"'
114
        )
115
        ->addOption(
116
            InputOptionKeys::SOURCE_DIR,
117
            null,
118
            InputOption::VALUE_REQUIRED,
119
            'The directory that has to be watched for new files'
120
        )
121
        ->addOption(
122
            InputOptionKeys::TARGET_DIR,
123
            null,
124
            InputOption::VALUE_REQUIRED,
125
            'The target directory with the files that has been imported'
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
    }
170
171
    /**
172
     * Executes the current command.
173
     *
174
     * This method is not abstract because you can use this class
175
     * as a concrete class. In this case, instead of defining the
176
     * execute() method, you set the code to execute by passing
177
     * a Closure to the setCode() method.
178
     *
179
     * @param \Symfony\Component\Console\Input\InputInterface   $input  An InputInterface instance
180
     * @param \Symfony\Component\Console\Output\OutputInterface $output An OutputInterface instance
181
     *
182
     * @return null|int null or 0 if everything went fine, or an error code
183
     * @throws \LogicException When this abstract method is not implemented
184
     * @see \Symfony\Component\Console\Command\Command::execute()
185
     */
186
    protected function execute(InputInterface $input, OutputInterface $output)
187
    {
188
189
        // load the actual vendor directory
190
        $vendorDirectory = $this->getVendorDir();
191
192
        // the path of the JMS serializer directory, relative to the vendor directory
193
        $jmsDirectory = DIRECTORY_SEPARATOR . 'jms' . DIRECTORY_SEPARATOR . 'serializer' . DIRECTORY_SEPARATOR . 'src';
194
195
        // try to find the path to the JMS Serializer annotations
196
        if (!file_exists($annotationDirectory = $vendorDirectory . DIRECTORY_SEPARATOR . $jmsDirectory)) {
197
            // stop processing, if the JMS annotations can't be found
198
            throw new \Exception(
199
                sprintf(
200
                    'The jms/serializer libarary can not be found in one of %s',
201
                    implode(', ', $this->getVendorDir())
202
                )
203
            );
204
        }
205
206
        // register the autoloader for the JMS serializer annotations
207
        \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
208
            'JMS\Serializer\Annotation',
209
            $annotationDirectory
210
        );
211
212
        // load the importer configuration and set the entity type code
213
        $configuration = ConfigurationFactory::load($this, $input);
214
215
        // initialize the DI container
216
        $container = new ContainerBuilder();
217
218
        // initialize the default loader and load the DI configuration for the this library
219
        $defaultLoader = new XmlFileLoader($container, new FileLocator($vendorDirectory));
220
221
        // load the DI configuration for all the extension libraries
222
        foreach ($configuration->getExtensionLibraries() as $library) {
223
            if (file_exists($diConfiguration = sprintf('%s/%s/symfony/Resources/config/services.xml', $vendorDirectory, $library))) {
224
                $defaultLoader->load($diConfiguration);
225
            } else {
226
                throw new \Exception(
227
                    sprintf(
228
                        'Can\'t load DI configuration for library %s',
229
                        $diConfiguration
230
                    )
231
                );
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
            } else {
242
                throw new \Exception(
243
                    sprintf(
244
                        'Can\'t find autoloader in configured additional vendor directory %s',
245
                        $additionalVendorDir->getVendorDir()
246
                    )
247
                );
248
            }
249
250
            // try to load the DI configuration for the configured extension libraries
251
            foreach ($additionalVendorDir->getLibraries() as $library) {
252
                // prepare the DI configuration filename
253
                $diConfiguration = realpath(sprintf('%s/%s/symfony/Resources/config/services.xml', $additionalVendorDir->getVendorDir(), $library));
254
                // try to load the filename
255
                if (file_exists($diConfiguration)) {
256
                    $customLoader->load($diConfiguration);
257
                } else {
258
                    throw new \Exception(
259
                        sprintf(
260
                            'Can\'t load DI configuration for library %s',
261
                            $diConfiguration
262
                        )
263
                    );
264
                }
265
            }
266
        }
267
268
        // add the configuration as well as input/outut instances to the DI container
269
        $container->set(SynteticServiceKeys::INPUT, $input);
270
        $container->set(SynteticServiceKeys::OUTPUT, $output);
271
        $container->set(SynteticServiceKeys::CONFIGURATION, $configuration);
272
        $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...
273
274
        // initialize the PDO connection
275
        $dsn = $configuration->getDatabase()->getDsn();
276
        $username = $configuration->getDatabase()->getUsername();
277
        $password = $configuration->getDatabase()->getPassword();
278
        $connection = new \PDO($dsn, $username, $password);
279
        $connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
280
281
        // add the PDO connection to the DI container
282
        $container->set(SynteticServiceKeys::CONNECTION, $connection);
283
284
        // initialize the system logger
285
        $loggers = array();
286
287
        // initialize the default system logger
288
        $systemLogger = new Logger('techdivision/import');
289
        $systemLogger->pushHandler(
290
            new ErrorLogHandler(
291
                ErrorLogHandler::OPERATING_SYSTEM,
292
                $configuration->getLogLevel()
293
            )
294
        );
295
296
        // add it to the array
297
        $loggers[LoggerKeys::SYSTEM] = $systemLogger;
298
299
        // append the configured loggers or override the default one
300
        foreach ($configuration->getLoggers() as $loggerConfiguration) {
301
            // load the factory class that creates the logger instance
302
            $loggerFactory = $loggerConfiguration->getFactory();
303
            // create the logger instance and add it to the available loggers
304
            $loggers[$loggerConfiguration->getName()] = $loggerFactory::factory($configuration, $loggerConfiguration);
305
        }
306
307
        // add the system loggers to the DI container
308
        $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...
309
310
        // start the import process
311
        $container->get(SynteticServiceKeys::SIMPLE)->process();
312
    }
313
314
    /**
315
     * Return's the absolute path to the actual vendor directory.
316
     *
317
     * @return string The absolute path to the actual vendor directory
318
     * @throws \Exception Is thrown, if none of the possible vendor directories can be found
319
     */
320
    public function getVendorDir()
321
    {
322
323
        // the possible paths to the vendor directory
324
        $possibleVendorDirectories = array(
325
            dirname(dirname(dirname(dirname(dirname(__DIR__))))) . DIRECTORY_SEPARATOR . 'vendor',
326
            dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'vendor'
327
        );
328
329
        // try to find the path to the JMS Serializer annotations
330
        foreach ($possibleVendorDirectories as $possibleVendorDirectory) {
331
            // return the directory as vendor directory if available
332
            if (is_dir($possibleVendorDirectory)) {
333
                return $possibleVendorDirectory;
334
            }
335
        }
336
337
        // stop processing, if NO vendor directory is available
338
        throw new \Exception(
339
            sprintf(
340
                'None of the possible vendor directories %s is available',
341
                implode(', ', $possibleVendorDirectories)
342
            )
343
        );
344
    }
345
}
346