Completed
Pull Request — master (#34)
by Tim
02:53
created

Simple::setInput()   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 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
/**
4
 * TechDivision\Import\Cli\Simple
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 Rhumsaa\Uuid\Uuid;
24
use Psr\Log\LogLevel;
25
use Psr\Log\LoggerInterface;
26
use Symfony\Component\Console\Input\InputInterface;
27
use Symfony\Component\Console\Output\OutputInterface;
28
use Symfony\Component\Console\Helper\FormatterHelper;
29
use TechDivision\Import\Utils\MemberNames;
30
use TechDivision\Import\Utils\RegistryKeys;
31
use TechDivision\Import\ConfigurationInterface;
32
use TechDivision\Import\Subjects\SubjectInterface;
33
use TechDivision\Import\Cli\Utils\BunchKeys;
34
use TechDivision\Import\Cli\Callbacks\CallbackVisitor;
35
use TechDivision\Import\Cli\Observers\ObserverVisitor;
36
use TechDivision\Import\Services\ImportProcessorInterface;
37
use TechDivision\Import\Services\RegistryProcessorInterface;
38
use TechDivision\Import\Subjects\ExportableSubjectInterface;
39
40
/**
41
 * The M2IF - Console Tool implementation.
42
 *
43
 * This is a example console tool implementation that should give developers an impression
44
 * on how the M2IF could be used to implement their own Magento 2 importer.
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
class Simple
53
{
54
55
    /**
56
     * The default style to write messages to the symfony console.
57
     *
58
     * @var string
59
     */
60
    const DEFAULT_STYLE = 'info';
61
62
    /**
63
     * The TechDivision company name as ANSI art.
64
     *
65
     * @var string
66
     */
67
    protected $ansiArt = ' _______        _     _____  _       _     _
68
|__   __|      | |   |  __ \(_)     (_)   (_)
69
   | | ___  ___| |__ | |  | |___   ___ ___ _  ___  _ __
70
   | |/ _ \/ __| \'_ \| |  | | \ \ / / / __| |/ _ \| \'_ \
71
   | |  __/ (__| | | | |__| | |\ V /| \__ \ | (_) | | | |
72
   |_|\___|\___|_| |_|_____/|_| \_/ |_|___/_|\___/|_| |_|
73
';
74
75
    /**
76
     * The log level => console style mapping.
77
     *
78
     * @var array
79
     */
80
    protected $logLevelStyleMapping = array(
81
        LogLevel::INFO      => 'info',
82
        LogLevel::DEBUG     => 'comment',
83
        LogLevel::ERROR     => 'error',
84
        LogLevel::ALERT     => 'error',
85
        LogLevel::CRITICAL  => 'error',
86
        LogLevel::EMERGENCY => 'error',
87
        LogLevel::WARNING   => 'error',
88
        LogLevel::NOTICE    => 'info'
89
    );
90
91
    /**
92
     * The actions unique serial.
93
     *
94
     * @var string
95
     */
96
    protected $serial;
97
98
    /**
99
     * The system logger implementation.
100
     *
101
     * @var \Psr\Log\LoggerInterface
102
     */
103
    protected $systemLogger;
104
105
    /**
106
     * The RegistryProcessor instance to handle running threads.
107
     *
108
     * @var \TechDivision\Import\Services\RegistryProcessorInterface
109
     */
110
    protected $registryProcessor;
111
112
    /**
113
     * The processor to read/write the necessary import data.
114
     *
115
     * @var \TechDivision\Import\Services\ImportProcessorInterface
116
     */
117
    protected $importProcessor;
118
119
    /**
120
     * The system configuration.
121
     *
122
     * @var \TechDivision\Import\ConfigurationInterface
123
     */
124
    protected $configuration;
125
126
    /**
127
     * The input stream to read console information from.
128
     *
129
     * @var \Symfony\Component\Console\Input\InputInterface
130
     */
131
    protected $input;
132
133
    /**
134
     * The output stream to write console information to.
135
     *
136
     * @var \Symfony\Component\Console\Output\OutputInterface
137
     */
138
    protected $output;
139
140
    /**
141
     * The matches for the last processed CSV filename.
142
     *
143
     * @var array
144
     */
145
    protected $matches = array();
146
147
    /**
148
     * Set's the unique serial for this import process.
149
     *
150
     * @param string $serial The unique serial
151
     *
152
     * @return void
153
     */
154
    public function setSerial($serial)
155
    {
156
        $this->serial = $serial;
157
    }
158
159
    /**
160
     * Return's the unique serial for this import process.
161
     *
162
     * @return string The unique serial
163
     */
164
    public function getSerial()
165
    {
166
        return $this->serial;
167
    }
168
169
    /**
170
     * Set's the system logger.
171
     *
172
     * @param \Psr\Log\LoggerInterface $systemLogger The system logger
173
     *
174
     * @return void
175
     */
176
    public function setSystemLogger(LoggerInterface $systemLogger)
177
    {
178
        $this->systemLogger = $systemLogger;
179
    }
180
181
    /**
182
     * Return's the system logger.
183
     *
184
     * @return \Psr\Log\LoggerInterface The system logger instance
185
     */
186
    public function getSystemLogger()
187
    {
188
        return $this->systemLogger;
189
    }
190
191
    /**
192
     * Sets's the RegistryProcessor instance to handle the running threads.
193
     *
194
     * @param \TechDivision\Import\Services\RegistryProcessorInterface $registryProcessor The registry processor instance
195
     *
196
     * @return void
197
     */
198
    public function setRegistryProcessor(RegistryProcessorInterface $registryProcessor)
199
    {
200
        $this->registryProcessor = $registryProcessor;
201
    }
202
203
    /**
204
     * Return's the RegistryProcessor instance to handle the running threads.
205
     *
206
     * @return \TechDivision\Import\Services\RegistryProcessor The registry processor instance
207
     */
208
    public function getRegistryProcessor()
209
    {
210
        return $this->registryProcessor;
211
    }
212
213
    /**
214
     * Set's the import processor instance.
215
     *
216
     * @param \TechDivision\Import\Services\ImportProcessorInterface $importProcessor The import processor instance
217
     *
218
     * @return void
219
     */
220 1
    public function setImportProcessor(ImportProcessorInterface $importProcessor)
221
    {
222 1
        $this->importProcessor = $importProcessor;
223 1
    }
224
225
    /**
226
     * Return's the import processor instance.
227
     *
228
     * @return \TechDivision\Import\Services\ImportProcessorInterface The import processor instance
229
     */
230 1
    public function getImportProcessor()
231
    {
232 1
        return $this->importProcessor;
233
    }
234
235
    /**
236
     * Set's the system configuration.
237
     *
238
     * @param \TechDivision\Import\ConfigurationInterface $configuration The system configuration
239
     *
240
     * @return void
241
     */
242
    public function setConfiguration(ConfigurationInterface $configuration)
243
    {
244
        $this->configuration = $configuration;
245
    }
246
247
    /**
248
     * Return's the system configuration.
249
     *
250
     * @return \TechDivision\Import\ConfigurationInterface The system configuration
251
     */
252
    public function getConfiguration()
253
    {
254
        return $this->configuration;
255
    }
256
257
    /**
258
     * Set's the input stream to read console information from.
259
     *
260
     * @param \Symfony\Component\Console\Input\InputInterface $input An IutputInterface instance
261
     *
262
     * @return void
263
     */
264
    public function setInput(InputInterface $input)
265
    {
266
        $this->input = $input;
267
    }
268
269
    /**
270
     * Return's the input stream to read console information from.
271
     *
272
     * @return \Symfony\Component\Console\Input\InputInterface An IutputInterface instance
273
     */
274
    protected function getInput()
275
    {
276
        return $this->input;
277
    }
278
279
    /**
280
     * Set's the output stream to write console information to.
281
     *
282
     * @param \Symfony\Component\Console\Output\OutputInterface $output An OutputInterface instance
283
     *
284
     * @return void
285
     */
286
    public function setOutput(OutputInterface $output)
287
    {
288
        $this->output = $output;
289
    }
290
291
    /**
292
     * Return's the output stream to write console information to.
293
     *
294
     * @return \Symfony\Component\Console\Output\OutputInterface An OutputInterface instance
295
     */
296
    protected function getOutput()
297
    {
298
        return $this->output;
299
    }
300
301
    /**
302
     * Return's the source directory that has to be watched for new files.
303
     *
304
     * @return string The source directory
305
     */
306
    protected function getSourceDir()
307
    {
308
        return $this->getConfiguration()->getSourceDir();
309
    }
310
311
    /**
312
     * Parse the temporary upload directory for new files to be imported.
313
     *
314
     * @return void
315
     * @throws \Exception Is thrown if the import can't be finished successfully
316
     */
317
    public function import()
318
    {
319
320
        // track the start time
321
        $startTime = microtime(true);
322
323
        try {
324
            // generate the serial for the new job
325
            $this->setSerial(Uuid::uuid4()->__toString());
326
327
            // prepare the global data for the import process
328
            $this->start();
329
            $this->setUp();
330
            $this->processSubjects();
331
            $this->archive();
332
            $this->tearDown();
333
            $this->finish();
334
335
            // track the time needed for the import in seconds
336
            $endTime = microtime(true) - $startTime;
337
338
            // log a message that import has been finished
339
            $this->log(sprintf('Successfully finished import with serial %s in %f s', $this->getSerial(), $endTime), LogLevel::INFO);
340
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
341
        } catch (\Exception $e) {
342
            // tear down
343
            $this->tearDown();
344
            $this->finish();
345
346
            // track the time needed for the import in seconds
347
            $endTime = microtime(true) - $startTime;
348
349
            // log a message that the file import failed
350
            $this->getSystemLogger()->error($e->__toString());
351
352
            // log a message that import has been finished
353
            $this->log(sprintf('Can\'t finish import with serial %s in %f s', $this->getSerial(), $endTime), LogLevel::ERROR);
354
355
            // re-throw the exception
356
            throw $e;
357
        }
358
    }
359
360
    /**
361
     * This method start's the import process by initializing
362
     * the status and appends it to the registry.
363
     *
364
     * @return void
365
     */
366
    protected function start()
367
    {
368
369
        // write the TechDivision ANSI art icon to the console
370
        $this->log($this->ansiArt);
371
372
        // log the debug information, if debug mode is enabled
373
        if ($this->getConfiguration()->isDebugMode()) {
374
            // log the system's PHP configuration
375
            $this->log(sprintf('PHP version: %s', phpversion()), LogLevel::DEBUG);
376
            $this->log('-------------------- Loaded Extensions -----------------------', LogLevel::DEBUG);
377
            $this->log(implode(', ', $loadedExtensions = get_loaded_extensions()), LogLevel::DEBUG);
378
            $this->log('--------------------------------------------------------------', LogLevel::DEBUG);
379
380
            // write a warning for low performance, if XDebug extension is activated
381
            if (in_array('xdebug', $loadedExtensions)) {
382
                $this->log('Low performance exptected, as result of enabled XDebug extension!', LogLevel::WARNING);
383
            }
384
        }
385
386
        // log a message that import has been started
387
        $this->log(sprintf('Now start import with serial %s', $this->getSerial()), LogLevel::INFO);
388
389
        // initialize the status
390
        $status = array(
391
            RegistryKeys::STATUS => 1,
392
            RegistryKeys::SOURCE_DIRECTORY => $this->getConfiguration()->getSourceDir()
393
        );
394
395
        // initialize the status information for the subjects */
396
        /** @var \TechDivision\Import\Configuration\SubjectInterface $subject */
397
        foreach ($this->getConfiguration()->getSubjects() as $subject) {
398
            $status[$subject->getPrefix()] = array();
399
        }
400
401
        // append it to the registry
402
        $this->getRegistryProcessor()->setAttribute($this->getSerial(), $status);
403
    }
404
405
    /**
406
     * Prepares the global data for the import process.
407
     *
408
     * @return void
409
     */
410
    protected function setUp()
411
    {
412
413
        // load the registry
414
        $importProcessor = $this->getImportProcessor();
415
        $registryProcessor = $this->getRegistryProcessor();
416
417
        // initialize the array for the global data
418
        $globalData = array();
419
420
        // initialize the global data
421
        $globalData[RegistryKeys::STORES] = $importProcessor->getStores();
422
        $globalData[RegistryKeys::LINK_TYPES] = $importProcessor->getLinkTypes();
423
        $globalData[RegistryKeys::TAX_CLASSES] = $importProcessor->getTaxClasses();
424
        $globalData[RegistryKeys::DEFAULT_STORE] = $importProcessor->getDefaultStore();
425
        $globalData[RegistryKeys::STORE_WEBSITES] = $importProcessor->getStoreWebsites();
426
        $globalData[RegistryKeys::LINK_ATTRIBUTES] = $importProcessor->getLinkAttributes();
427
        $globalData[RegistryKeys::ROOT_CATEGORIES] = $importProcessor->getRootCategories();
428
        $globalData[RegistryKeys::CORE_CONFIG_DATA] = $importProcessor->getCoreConfigData();
429
        $globalData[RegistryKeys::ATTRIBUTE_SETS] = $eavAttributeSets = $importProcessor->getEavAttributeSetsByEntityTypeId(4);
430
431
        // prepare the categories
432
        $categories = array();
433
        foreach ($importProcessor->getCategories() as $category) {
434
            // expload the entity IDs from the category path
435
            $entityIds = explode('/', $category[MemberNames::PATH]);
436
437
            // cut-off the root category
438
            array_shift($entityIds);
439
440
            // continue with the next category if no entity IDs are available
441
            if (sizeof($entityIds) === 0) {
442
                continue;
443
            }
444
445
            // initialize the array for the path elements
446
            $path = array();
447
            foreach ($importProcessor->getCategoryVarcharsByEntityIds($entityIds) as $cat) {
448
                $path[] = $cat[MemberNames::VALUE];
449
            }
450
451
            // append the catogory with the string path as key
452
            $categories[implode('/', $path)] = $category;
453
        }
454
455
        // initialize the array with the categories
456
        $globalData[RegistryKeys::CATEGORIES] = $categories;
457
458
        // prepare an array with the EAV attributes grouped by their attribute set name as keys
459
        $eavAttributes = array();
460
        foreach (array_keys($eavAttributeSets) as $eavAttributeSetName) {
461
            $eavAttributes[$eavAttributeSetName] = $importProcessor->getEavAttributesByEntityTypeIdAndAttributeSetName(4, $eavAttributeSetName);
462
        }
463
464
        // initialize the array with the EAV attributes
465
        $globalData[RegistryKeys::EAV_ATTRIBUTES] = $eavAttributes;
466
467
        // add the status with the global data
468
        $registryProcessor->mergeAttributesRecursive(
469
            $this->getSerial(),
470
            array(RegistryKeys::GLOBAL_DATA => $globalData)
471
        );
472
473
        // log a message that the global data has been prepared
474
        $this->log(sprintf('Successfully prepared global data for import with serial %s', $this->getSerial()), LogLevel::INFO);
475
    }
476
477
    /**
478
     * Process all the subjects defined in the system configuration.
479
     *
480
     * @return void
481
     * @throws \Exception Is thrown, if one of the subjects can't be processed
482
     */
483
    protected function processSubjects()
484
    {
485
486
        try {
487
            // load system logger and registry
488
            $importProcessor = $this->getImportProcessor();
489
490
            // load the subjects
491
            $subjects = $this->getConfiguration()->getSubjects();
492
493
            // start the transaction
494
            $importProcessor->getConnection()->beginTransaction();
495
496
            // process all the subjects found in the system configuration
497
            foreach ($subjects as $subject) {
498
                $this->processSubject($subject);
499
            }
500
501
            // commit the transaction
502
            $importProcessor->getConnection()->commit();
503
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
504
        } catch (\Exception $e) {
505
            // rollback the transaction
506
            $importProcessor->getConnection()->rollBack();
507
508
            // re-throw the exception
509
            throw $e;
510
        }
511
    }
512
513
    /**
514
     * Process the subject with the passed name/identifier.
515
     *
516
     * We create a new, fresh and separate subject for EVERY file here, because this would be
517
     * the starting point to parallelize the import process in a multithreaded/multiprocessed
518
     * environment.
519
     *
520
     * @param \TechDivision\Import\Configuration\SubjectInterface $subject The subject configuration
521
     *
522
     * @return void
523
     * @throws \Exception Is thrown, if the subject can't be processed
524
     */
525
    protected function processSubject(\TechDivision\Import\Configuration\SubjectInterface $subject)
526
    {
527
528
        // clear the filecache
529
        clearstatcache();
530
531
        // load the actual status
532
        $status = $this->getRegistryProcessor()->getAttribute($this->getSerial());
533
534
        // query whether or not the configured source directory is available
535 View Code Duplication
        if (!is_dir($sourceDir = $status[RegistryKeys::SOURCE_DIRECTORY])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
536
            throw new \Exception(sprintf('Configured source directory %s is not available!', $sourceDir));
537
        }
538
539
        // initialize the file iterator on source directory
540
        $fileIterator = new \FilesystemIterator($sourceDir);
541
542
        // log a debug message
543
        $this->log(sprintf('Now checking directory %s for files to be imported', $sourceDir), LogLevel::DEBUG);
544
545
        // initialize the counter for the bunches that has been imported
546
        $bunches = 0;
547
548
        // iterate through all CSV files and process the subjects
549
        foreach ($fileIterator as $filename) {
550
            // initialize prefix + pathname
551
            $prefix = $subject->getPrefix();
552
            $pathname = $filename->getPathname();
553
554
            // query whether or not we've a file that is part of a bunch here
555
            if ($this->isPartOfBunch($prefix, $pathname)) {
556
                // initialize the subject and import the bunch
557
                $subjectInstance = $this->subjectFactory($subject);
0 ignored issues
show
Documentation introduced by
$subject is of type object<TechDivision\Impo...ation\SubjectInterface>, but the function expects a object<TechDivision\Import\Configuration\Subject>.

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...
558
                $subjectInstance->import($this->getSerial(), $pathname);
559
560
                // query whether or not, we've to export artefacts
561
                if ($subjectInstance instanceof ExportableSubjectInterface) {
0 ignored issues
show
Bug introduced by
The class TechDivision\Import\Subj...ortableSubjectInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
562
                    $subjectInstance->export($this->matches[BunchKeys::FILENAME], $this->matches[BunchKeys::COUNTER]);
563
                }
564
565
                // raise the number of the imported bunches
566
                $bunches++;
567
            }
568
        }
569
570
        // reset the matches, because the exported artefacts
571
        $this->matches = array();
572
573
        // and and log a message that the subject has been processed
574
        $this->log(sprintf('Successfully processed subject %s with %d bunch(es)!', $subject->getClassName(), $bunches), LogLevel::DEBUG);
575
    }
576
577
    /**
578
     * Queries whether or not, the passed filename is part of a bunch or not.
579
     *
580
     * @param string $prefix   The prefix to query for
581
     * @param string $filename The filename to query for
582
     *
583
     * @return boolean TRUE if the filename is part, else FALSE
584
     */
585 2
    public function isPartOfBunch($prefix, $filename)
586
    {
587
588
        // initialize the pattern
589 2
        $pattern = '';
0 ignored issues
show
Unused Code introduced by
$pattern is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
590
591
        // query whether or not, this is the first file to be processed
592 2
        if (sizeof($this->matches) === 0) {
593
            // initialize the pattern to query whether the FIRST file has to be processed or not
594 2
            $pattern = sprintf(
595 2
                '/^.*\/(?<%s>%s)_(?<%s>.*)_(?<%s>\d+)\\.csv$/',
596 2
                BunchKeys::PREFIX,
597
                $prefix,
598 2
                BunchKeys::FILENAME,
599 2
                BunchKeys::COUNTER
600
            );
601
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
602
        } else {
603
            // initialize the pattern to query whether the NEXT file is part of a bunch or not
604 2
            $pattern = sprintf(
605 2
                '/^.*\/(?<%s>%s)_(?<%s>%s)_(?<%s>\d+)\\.csv$/',
606 2
                BunchKeys::PREFIX,
607 2
                $this->matches[BunchKeys::PREFIX],
608 2
                BunchKeys::FILENAME,
609 2
                $this->matches[BunchKeys::FILENAME],
610 2
                BunchKeys::COUNTER
611
            );
612
        }
613
614
        // initialize the array for the matches
615 2
        $matches = array();
616
617
        // update the matches, if the pattern matches
618 2
        if ($result = preg_match($pattern, $filename, $matches)) {
619 2
            $this->matches = $matches;
620
        }
621
622
        // stop processing, if the filename doesn't match
623 2
        return (boolean) $result;
624
    }
625
626
    /**
627
     * Factory method to create new handler instances.
628
     *
629
     * @param \TechDivision\Import\Configuration\Subject $subject The subject configuration
630
     *
631
     * @return object The handler instance
632
     */
633
    public function subjectFactory($subject)
634
    {
635
636
        // load the subject class name
637
        $className = $subject->getClassName();
638
639
        // the database connection to use
640
        $connection = $this->getImportProcessor()->getConnection();
641
642
        // initialize a new handler with the passed class name
643
        $instance = new $className();
644
645
        // $instance the handler instance
646
        $instance->setConfiguration($subject);
647
        $instance->setSystemLogger($this->getSystemLogger());
648
        $instance->setRegistryProcessor($this->getRegistryProcessor());
649
650
        // instanciate and set the product processor, if specified
651
        if ($processorFactory = $subject->getProcessorFactory()) {
652
            $productProcessor = $processorFactory::factory($connection, $subject);
653
            $instance->setProductProcessor($productProcessor);
654
        }
655
656
        // initialize the callbacks/visitors
657
        CallbackVisitor::get()->visit($instance);
658
        ObserverVisitor::get()->visit($instance);
659
660
        // return the subject instance
661
        return $instance;
662
    }
663
664
    /**
665
     * Lifecycle callback that will be inovked after the
666
     * import process has been finished.
667
     *
668
     * @return void
669
     * @throws \Exception Is thrown, if the
670
     */
671
    protected function archive()
672
    {
673
674
        // query whether or not, the import artefacts have to be archived
675
        if (!$this->getConfiguration()->haveArchiveArtefacts()) {
676
            return;
677
        }
678
679
        // clear the filecache
680
        clearstatcache();
681
682
        // load the actual status
683
        $status = $this->getRegistryProcessor()->getAttribute($this->getSerial());
684
685
        // query whether or not the configured source directory is available
686 View Code Duplication
        if (!is_dir($sourceDir = $status[RegistryKeys::SOURCE_DIRECTORY])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
687
            throw new \Exception(sprintf('Configured source directory %s is not available!', $sourceDir));
688
        }
689
690
        // init file iterator on source directory
691
        $fileIterator = new \FilesystemIterator($sourceDir);
692
693
        // log the number of files that has to be archived
694
        $fileCounter = iterator_count($fileIterator);
695
696
        // if no files are available, return
697
        if ($fileCounter == 0) {
698
            // log the number of files that has to be archived
699
            $this->log(sprintf('Found no files to archive'), LogLevel::INFO);
700
            return;
701
        }
702
703
        // log the number of files that has to be archived
704
        $this->log(sprintf('Found %d files to archive in directory %s', $fileCounter, $sourceDir), LogLevel::INFO);
705
706
        // initialize the directory to create the archive in
707
        $archiveDir = sprintf('%s/%s', $this->getConfiguration()->getTargetDir(), $this->getConfiguration()->getArchiveDir());
708
709
        // query whether or not the directory already exists
710
        if (!is_dir($archiveDir)) {
711
            mkdir($archiveDir);
712
        }
713
714
        // create the ZIP archive
715
        $archive = new \ZipArchive();
716
        $archive->open($archiveFile = sprintf('%s/%s.zip', $archiveDir, $this->getSerial()), \ZipArchive::CREATE);
717
718
        // iterate through all files and add them to the ZIP archive
719
        foreach ($fileIterator as $filename) {
720
            $archive->addFile($filename);
721
        }
722
723
        // save the ZIP archive
724
        $archive->close();
725
726
        // finally remove the directory with the imported files
727
        $this->removeDir($sourceDir);
728
729
        // and and log a message that the import artefacts have been archived
730
        $this->log(sprintf('Successfully archived imported files to %s!', $archiveFile), LogLevel::INFO);
731
    }
732
733
    /**
734
     * Removes the passed directory recursively.
735
     *
736
     * @param string $src Name of the directory to remove
737
     *
738
     * @return void
739
     * @throws \Exception Is thrown, if the directory can not be removed
740
     */
741
    protected function removeDir($src)
742
    {
743
744
        // open the directory
745
        $dir = opendir($src);
746
747
        // remove files/folders recursively
748
        while (false !== ($file = readdir($dir))) {
749
            if (($file != '.') && ($file != '..')) {
750
                $full = $src . '/' . $file;
751
                if (is_dir($full)) {
752
                    $this->removeDir($full);
753
                } else {
754
                    if (!unlink($full)) {
755
                        throw new \Exception(sprintf('Can\'t remove file %s', $full));
756
                    }
757
                }
758
            }
759
        }
760
761
        // close handle and remove directory itself
762
        closedir($dir);
763
        if (!rmdir($src)) {
764
            throw new \Exception(sprintf('Can\'t remove directory %s', $src));
765
        }
766
    }
767
768
    /**
769
     * Simple method that writes the passed method the the console and the
770
     * system logger, if configured and a log level has been passed.
771
     *
772
     * @param string $msg      The message to log
773
     * @param string $logLevel The log level to use
774
     *
775
     * @return void
776
     */
777
    protected function log($msg, $logLevel = null)
778
    {
779
780
        // initialize the formatter helper
781
        $helper = new FormatterHelper();
782
783
        // map the log level to the console style
784
        $style = $this->mapLogLevelToStyle($logLevel);
785
786
        // format the message, according to the passed log level and write it to the console
787
        $this->getOutput()->writeln($logLevel ? $helper->formatBlock($msg, $style) : $msg);
788
789
        // log the message if a log level has been passed
790
        if ($logLevel && $systemLogger = $this->getSystemLogger()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $logLevel 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...
791
            $systemLogger->log($logLevel, $msg);
792
        }
793
    }
794
795
    /**
796
     * Map's the passed log level to a valid symfony console style.
797
     *
798
     * @param string $logLevel The log level to map
799
     *
800
     * @return string The apropriate symfony console style
801
     */
802
    protected function mapLogLevelToStyle($logLevel)
803
    {
804
805
        // query whether or not the log level is mapped
806
        if (isset($this->logLevelStyleMapping[$logLevel])) {
807
            return $this->logLevelStyleMapping[$logLevel];
808
        }
809
810
        // return the default style => info
811
        return Simple::DEFAULT_STYLE;
812
    }
813
814
    /**
815
     * Lifecycle callback that will be inovked after the
816
     * import process has been finished.
817
     *
818
     * @return void
819
     * @throws \Exception Is thrown, if the
820
     */
821
    protected function tearDown()
822
    {
823
    }
824
825
    /**
826
     * This method finishes the import process and cleans the registry.
827
     *
828
     * @return void
829
     */
830
    protected function finish()
831
    {
832
        $this->getRegistryProcessor()->removeAttribute($this->getSerial());
833
    }
834
}
835