Completed
Pull Request — master (#29)
by Tim
03:41
created

AbstractSubject::setConfiguration()   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
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
/**
4
 * TechDivision\Import\Subjects\AbstractSubject
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
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Subjects;
22
23
use Psr\Log\LoggerInterface;
24
use League\Flysystem\Filesystem;
25
use League\Flysystem\Adapter\Local;
26
use League\Flysystem\FilesystemInterface;
27
use Goodby\CSV\Import\Standard\Lexer;
28
use Goodby\CSV\Import\Standard\LexerConfig;
29
use Goodby\CSV\Import\Standard\Interpreter;
30
use TechDivision\Import\Utils\RegistryKeys;
31
use TechDivision\Import\Utils\ConfigurationKeys;
32
use TechDivision\Import\Services\RegistryProcessor;
33
use TechDivision\Import\Callbacks\CallbackInterface;
34
use TechDivision\Import\Observers\ObserverInterface;
35
use TechDivision\Import\Services\RegistryProcessorInterface;
36
use TechDivision\Import\Configuration\SubjectInterface as SubjectConfigurationInterface;
37
38
/**
39
 * An abstract subject implementation.
40
 *
41
 * @author    Tim Wagner <[email protected]>
42
 * @copyright 2016 TechDivision GmbH <[email protected]>
43
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
44
 * @link      https://github.com/techdivision/import
45
 * @link      http://www.techdivision.com
46
 */
47
abstract class AbstractSubject implements SubjectInterface
48
{
49
50
    /**
51
     * The root directory for the virtual filesystem.
52
     *
53
     * @var string
54
     */
55
    protected $rootDir;
56
57
    /**
58
     * The system configuration.
59
     *
60
     * @var \TechDivision\Import\Configuration\SubjectInterface
61
     */
62
    protected $configuration;
63
64
    /**
65
     * The system logger implementation.
66
     *
67
     * @var \Psr\Log\LoggerInterface
68
     */
69
    protected $systemLogger;
70
71
    /**
72
     * The RegistryProcessor instance to handle running threads.
73
     *
74
     * @var \TechDivision\Import\Services\RegistryProcessorInterface
75
     */
76
    protected $registryProcessor;
77
78
    /**
79
     * The actions unique serial.
80
     *
81
     * @var string
82
     */
83
    protected $serial;
84
85
    /**
86
     * The name of the file to be imported.
87
     *
88
     * @var string
89
     */
90
    protected $filename;
91
92
    /**
93
     * Array with the subject's observers.
94
     *
95
     * @var array
96
     */
97
    protected $observers = array();
98
99
    /**
100
     * Array with the subject's callbacks.
101
     *
102
     * @var array
103
     */
104
    protected $callbacks = array();
105
106
    /**
107
     * Contain's the column names from the header line.
108
     *
109
     * @var array
110
     */
111
    protected $headers = array();
112
113
    /**
114
     * The virtual filesystem instance.
115
     *
116
     * @var \League\Flysystem\FilesystemInterface
117
     */
118
    protected $filesystem;
119
120
    /**
121
     * The actual line number.
122
     *
123
     * @var integer
124
     */
125
    protected $lineNumber = 0;
126
127
    /**
128
     * The actual operation name.
129
     *
130
     * @var string
131
     */
132
    protected $operationName ;
133
134
    /**
135
     * The flag that stop's overserver execution on the actual row.
136
     *
137
     * @var boolean
138
     */
139
    protected $skipRow = false;
140
141
    /**
142
     * Mappings for attribute code => CSV column header.
143
     *
144
     * @var array
145
     */
146
    protected $headerMappings = array(
147
        'product_online' => 'status',
148
        'tax_class_name' => 'tax_class_id',
149
        'bundle_price_type' => 'price_type',
150
        'bundle_sku_type' => 'sku_type',
151
        'bundle_price_view' => 'price_view',
152
        'bundle_weight_type' => 'weight_type',
153
        'base_image' => 'image',
154
        'base_image_label' => 'image_label',
155
        'thumbnail_image' => 'thumbnail',
156
        'thumbnail_image_label'=> 'thumbnail_label',
157
        'bundle_shipment_type' => 'shipment_type'
158
    );
159
160
    /**
161
     * Stop's observer execution on the actual row.
162
     *
163
     * @return void
164
     */
165
    public function skipRow()
166
    {
167
        $this->skipRow = true;
168
    }
169
170
    /**
171
     * Return's the actual line number.
172
     *
173
     * @return integer The line number
174
     */
175
    public function getLineNumber()
176
    {
177
        return $this->lineNumber;
178
    }
179
180
    /**
181
     * Return's the actual operation name.
182
     *
183
     * @return string
184
     */
185
    public function getOperationName()
186
    {
187
        return $this->operationName;
188
    }
189
190
    /**
191
     * Set's the array containing header row.
192
     *
193
     * @param array $headers The array with the header row
194
     *
195
     * @return void
196
     */
197
    public function setHeaders(array $headers)
198
    {
199
        $this->headers = $headers;
200
    }
201
202
    /**
203
     * Return's the array containing header row.
204
     *
205
     * @return array The array with the header row
206
     */
207
    public function getHeaders()
208
    {
209
        return $this->headers;
210
    }
211
212
    /**
213
     * Queries whether or not the header with the passed name is available.
214
     *
215
     * @param string $name The header name to query
216
     *
217
     * @return boolean TRUE if the header is available, else FALSE
218
     */
219
    public function hasHeader($name)
220
    {
221
        return isset($this->headers[$name]);
222
    }
223
224
    /**
225
     * Return's the header value for the passed name.
226
     *
227
     * @param string $name The name of the header to return the value for
228
     *
229
     * @return mixed The header value
230
     * \InvalidArgumentException Is thrown, if the header with the passed name is NOT available
231
     */
232
    public function getHeader($name)
233
    {
234
235
        // query whether or not, the header is available
236
        if (isset($this->headers[$name])) {
237
            return $this->headers[$name];
238
        }
239
240
        // throw an exception, if not
241
        throw new \InvalidArgumentException(sprintf('Header %s is not available', $name));
242
    }
243
244
    /**
245
     * Add's the header with the passed name and position, if not NULL.
246
     *
247
     * @param string $name The header name to add
248
     *
249
     * @return integer The new headers position
250
     */
251
    public function addHeader($name)
252
    {
253
254
        // add the header
255
        $this->headers[$name] = $position = sizeof($this->headers);
256
257
        // return the new header's position
258
        return $position;
259
    }
260
261
    /**
262
     * Queries whether or not debug mode is enabled or not, default is TRUE.
263
     *
264
     * @return boolean TRUE if debug mode is enabled, else FALSE
265
     */
266
    public function isDebugMode()
267
    {
268
        return $this->getConfiguration()->isDebugMode();
269
    }
270
271
    /**
272
     * Set's the system configuration.
273
     *
274
     * @param \TechDivision\Import\Configuration\Subject $configuration The system configuration
275
     *
276
     * @return void
277
     */
278
    public function setConfiguration(SubjectConfigurationInterface $configuration)
279
    {
280
        $this->configuration = $configuration;
281
    }
282
283
    /**
284
     * Return's the system configuration.
285
     *
286
     * @return \TechDivision\Import\Configuration\SubjectInterface The system configuration
287
     */
288
    public function getConfiguration()
289
    {
290
        return $this->configuration;
291
    }
292
293
    /**
294
     * Set's the system logger.
295
     *
296
     * @param \Psr\Log\LoggerInterface $systemLogger The system logger
297
     *
298
     * @return void
299
     */
300
    public function setSystemLogger(LoggerInterface $systemLogger)
301
    {
302
        $this->systemLogger = $systemLogger;
303
    }
304
305
    /**
306
     * Return's the system logger.
307
     *
308
     * @return \Psr\Log\LoggerInterface The system logger instance
309
     */
310
    public function getSystemLogger()
311
    {
312
        return $this->systemLogger;
313
    }
314
315
    /**
316
     * Set's root directory for the virtual filesystem.
317
     *
318
     * @param string $rootDir The root directory for the virtual filesystem
319
     *
320
     * @return void
321
     */
322
    public function setRootDir($rootDir)
323
    {
324
        $this->rootDir = $rootDir;
325
    }
326
327
    /**
328
     * Return's the root directory for the virtual filesystem.
329
     *
330
     * @return string The root directory for the virtual filesystem
331
     */
332
    public function getRootDir()
333
    {
334
        return $this->rootDir;
335
    }
336
337
    /**
338
     * Set's the virtual filesystem instance.
339
     *
340
     * @param \League\Flysystem\FilesystemInterface $filesystem The filesystem instance
341
     *
342
     * @return void
343
     */
344
    public function setFilesystem(FilesystemInterface $filesystem)
345
    {
346
        $this->filesystem = $filesystem;
347
    }
348
349
    /**
350
     * Return's the virtual filesystem instance.
351
     *
352
     * @return \League\Flysystem\FilesystemInterface The filesystem instance
353
     */
354
    public function getFilesystem()
355
    {
356
        return $this->filesystem;
357
    }
358
359
    /**
360
     * Sets's the RegistryProcessor instance to handle the running threads.
361
     *
362
     * @param \TechDivision\Import\Services\RegistryProcessorInterface $registryProcessor The registry processor instance
363
     *
364
     * @return void
365
     */
366
    public function setRegistryProcessor(RegistryProcessorInterface $registryProcessor)
367
    {
368
        $this->registryProcessor = $registryProcessor;
369
    }
370
371
    /**
372
     * Return's the RegistryProcessor instance to handle the running threads.
373
     *
374
     * @return \TechDivision\Import\Services\RegistryProcessorInterface The registry processor instance
375
     */
376
    public function getRegistryProcessor()
377
    {
378
        return $this->registryProcessor;
379
    }
380
381
    /**
382
     * Set's the unique serial for this import process.
383
     *
384
     * @param string $serial The unique serial
385
     *
386
     * @return void
387
     */
388
    public function setSerial($serial)
389
    {
390
        $this->serial = $serial;
391
    }
392
393
    /**
394
     * Return's the unique serial for this import process.
395
     *
396
     * @return string The unique serial
397
     */
398
    public function getSerial()
399
    {
400
        return $this->serial;
401
    }
402
403
    /**
404
     * Set's the name of the file to import
405
     *
406
     * @param string $filename The filename
407
     *
408
     * @return void
409
     */
410
    public function setFilename($filename)
411
    {
412
        $this->filename = $filename;
413
    }
414
415
    /**
416
     * Return's the name of the file to import.
417
     *
418
     * @return string The filename
419
     */
420
    public function getFilename()
421
    {
422
        return $this->filename;
423
    }
424
425
    /**
426
     * Return's the source date format to use.
427
     *
428
     * @return string The source date format
429
     */
430
    public function getSourceDateFormat()
431
    {
432
        return $this->getConfiguration()->getSourceDateFormat();
433
    }
434
435
    /**
436
     * Return's the multiple field delimiter character to use, default value is comma (,).
437
     *
438
     * @return string The multiple field delimiter character
439
     */
440
    public function getMultipleFieldDelimiter()
441
    {
442
        return $this->getConfiguration()->getMultipleFieldDelimiter();
0 ignored issues
show
Bug introduced by
The method getMultipleFieldDelimiter() does not seem to exist on object<TechDivision\Impo...ation\SubjectInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
443
    }
444
445
    /**
446
     * Return's the initialized PDO connection.
447
     *
448
     * @return \PDO The initialized PDO connection
449
     */
450
    public function getConnection()
451
    {
452
        return $this->getProductProcessor()->getConnection();
0 ignored issues
show
Bug introduced by
The method getProductProcessor() does not seem to exist on object<TechDivision\Impo...bjects\AbstractSubject>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
453
    }
454
455
    /**
456
     * Intializes the previously loaded global data for exactly one bunch.
457
     *
458
     * @return void
459
     * @see \Importer\Csv\Actions\ProductImportAction::prepare()
460
     */
461
    public function setUp()
462
    {
463
464
        // initialize the filesystems root directory
465
        $this->setRootDir(
466
            $this->getConfiguration()->getParam(ConfigurationKeys::ROOT_DIRECTORY, getcwd())
467
        );
468
469
        // initialize the filesystem
470
        $this->filesystem = new Filesystem(new Local($this->getRootDir()));
471
472
        // initialize the operation name
473
        $this->operationName = $this->getConfiguration()->getConfiguration()->getOperationName();
474
    }
475
476
    /**
477
     * This method tries to resolve the passed path and returns it. If the path
478
     * is relative, the actual working directory will be prepended.
479
     *
480
     * @param string $path The path to be resolved
481
     *
482
     * @return string The resolved path
483
     * @throws \InvalidArgumentException Is thrown, if the path can not be resolved
484
     */
485
    public function resolvePath($path)
486
    {
487
        // if we've an absolute path, return it immediately
488
        if ($this->getFilesystem()->has($path)) {
489
            return $path;
490
        }
491
492
        // try to prepend the actual working directory, assuming we've a relative path
493
        if ($this->getFilesystem()->has($path = getcwd() . DIRECTORY_SEPARATOR . $path)) {
494
            return $path;
495
        }
496
497
        // throw an exception if the passed directory doesn't exists
498
        throw new \InvalidArgumentException(
499
            sprintf('Directory %s doesn\'t exist', $path)
500
        );
501
    }
502
503
    /**
504
     * Clean up the global data after importing the variants.
505
     *
506
     * @return void
507
     */
508
    public function tearDown()
509
    {
510
511
        // load the registry processor
512
        $registryProcessor = $this->getRegistryProcessor();
513
514
        // update the source directory for the next subject
515
        $registryProcessor->mergeAttributesRecursive(
516
            $this->getSerial(),
517
            array(RegistryKeys::SOURCE_DIRECTORY => $this->getNewSourceDir())
518
        );
519
520
        // log a debug message with the new source directory
521
        $this->getSystemLogger()->debug(
522
            sprintf('Subject %s successfully updated source directory to %s', __CLASS__, $this->getNewSourceDir())
523
        );
524
    }
525
526
    /**
527
     * Return's the next source directory, which will be the target directory
528
     * of this subject, in most cases.
529
     *
530
     * @return string The new source directory
531
     */
532
    protected function getNewSourceDir()
533
    {
534
        return sprintf('%s/%s', $this->getConfiguration()->getTargetDir(), $this->getSerial());
535
    }
536
537
    /**
538
     * Register the passed observer with the specific type.
539
     *
540
     * @param \TechDivision\Import\Observers\ObserverInterface $observer The observer to register
541
     * @param string                                           $type     The type to register the observer with
542
     *
543
     * @return void
544
     */
545
    public function registerObserver(ObserverInterface $observer, $type)
546
    {
547
548
        // query whether or not the array with the callbacks for the
549
        // passed type has already been initialized, or not
550
        if (!isset($this->observers[$type])) {
551
            $this->observers[$type] = array();
552
        }
553
554
        // append the callback with the instance of the passed type
555
        $this->observers[$type][] = $observer;
556
    }
557
558
    /**
559
     * Register the passed callback with the specific type.
560
     *
561
     * @param \TechDivision\Import\Callbacks\CallbackInterface $callback The subject to register the callbacks for
562
     * @param string                                           $type     The type to register the callback with
563
     *
564
     * @return void
565
     */
566
    public function registerCallback(CallbackInterface $callback, $type)
567
    {
568
569
        // query whether or not the array with the callbacks for the
570
        // passed type has already been initialized, or not
571
        if (!isset($this->callbacks[$type])) {
572
            $this->callbacks[$type] = array();
573
        }
574
575
        // append the callback with the instance of the passed type
576
        $this->callbacks[$type][] = $callback;
577
    }
578
579
    /**
580
     * Return's the array with callbacks for the passed type.
581
     *
582
     * @param string $type The type of the callbacks to return
583
     *
584
     * @return array The callbacks
585
     */
586
    public function getCallbacksByType($type)
587
    {
588
589
        // initialize the array for the callbacks
590
        $callbacks = array();
591
592
        // query whether or not callbacks for the type are available
593
        if (isset($this->callbacks[$type])) {
594
            $callbacks = $this->callbacks[$type];
595
        }
596
597
        // return the array with the type's callbacks
598
        return $callbacks;
599
    }
600
601
    /**
602
     * Return's the array with the available observers.
603
     *
604
     * @return array The observers
605
     */
606
    public function getObservers()
607
    {
608
        return $this->observers;
609
    }
610
611
    /**
612
     * Return's the array with the available callbacks.
613
     *
614
     * @return array The callbacks
615
     */
616
    public function getCallbacks()
617
    {
618
        return $this->callbacks;
619
    }
620
621
    /**
622
     * Imports the content of the file with the passed filename.
623
     *
624
     * @param string $serial   The unique process serial
625
     * @param string $filename The filename to process
626
     *
627
     * @return void
628
     * @throws \Exception Is thrown, if the import can't be processed
629
     */
630
    public function import($serial, $filename)
631
    {
632
633
        try {
634
            // stop processing, if the filename doesn't match
635
            if (!$this->match($filename)) {
636
                return;
637
            }
638
639
            // load the system logger instance
640
            $systemLogger = $this->getSystemLogger();
641
642
            // prepare the flag filenames
643
            $inProgressFilename = sprintf('%s.inProgress', $filename);
644
            $importedFilename = sprintf('%s.imported', $filename);
645
            $failedFilename = sprintf('%s.failed', $filename);
646
647
            // query whether or not the file has already been imported
648
            if (is_file($failedFilename) ||
649
                is_file($importedFilename) ||
650
                is_file($inProgressFilename)
651
            ) {
652
                // log a debug message and exit
653
                $systemLogger->debug(sprintf('Import running, found inProgress file %s', $inProgressFilename));
654
                return;
655
            }
656
657
            // flag file as in progress
658
            touch($inProgressFilename);
659
660
            // track the start time
661
            $startTime = microtime(true);
662
663
            // initialize serial and filename
664
            $this->setSerial($serial);
665
            $this->setFilename($filename);
666
667
            // load the system logger
668
            $systemLogger = $this->getSystemLogger();
669
670
            // initialize the global global data to import a bunch
671
            $this->setUp();
672
673
            // initialize the lexer instance itself
674
            $lexer = new Lexer($this->getLexerConfig());
675
676
            // initialize the interpreter
677
            $interpreter = new Interpreter();
678
            $interpreter->addObserver(array($this, 'importRow'));
679
680
            // query whether or not we want to use the strict mode
681
            if (!$this->getConfiguration()->isStrictMode()) {
682
                $interpreter->unstrict();
683
            }
684
685
            // log a message that the file has to be imported
686
            $systemLogger->debug(sprintf('Now start importing file %s', $filename));
687
688
            // parse the CSV file to be imported
689
            $lexer->parse($filename, $interpreter);
690
691
            // track the time needed for the import in seconds
692
            $endTime = microtime(true) - $startTime;
693
694
            // clean up the data after importing the bunch
695
            $this->tearDown();
696
697
            // log a message that the file has successfully been imported
698
            $systemLogger->debug(sprintf('Succesfully imported file %s in %f s', $filename, $endTime));
699
700
            // rename flag file, because import has been successfull
701
            rename($inProgressFilename, $importedFilename);
702
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
703
        } catch (\Exception $e) {
704
            // rename the flag file, because import failed and write the stack trace
705
            rename($inProgressFilename, $failedFilename);
0 ignored issues
show
Bug introduced by
The variable $inProgressFilename does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $failedFilename does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
706
            file_put_contents($failedFilename, $e->__toString());
707
708
            // clean up the data after importing the bunch
709
            $this->tearDown();
710
711
            // re-throw the exception
712
            throw $e;
713
        }
714
    }
715
716
    /**
717
     * This method queries whether or not the passed filename matches
718
     * the pattern, based on the subjects configured prefix.
719
     *
720
     * @param string $filename The filename to match
721
     *
722
     * @return boolean TRUE if the filename matches, else FALSE
723
     */
724
    protected function match($filename)
725
    {
726
727
        // prepare the pattern to query whether the file has to be processed or not
728
        $pattern = sprintf('/^.*\/%s.*\\.csv$/', $this->getConfiguration()->getPrefix());
729
730
        // stop processing, if the filename doesn't match
731
        return (boolean) preg_match($pattern, $filename);
732
    }
733
734
    /**
735
     * Initialize and return the lexer configuration.
736
     *
737
     * @return \Goodby\CSV\Import\Standard\LexerConfig The lexer configuration
738
     */
739
    protected function getLexerConfig()
740
    {
741
742
        // initialize the lexer configuration
743
        $config = new LexerConfig();
744
745
        // query whether or not a delimiter character has been configured
746
        if ($delimiter = $this->getConfiguration()->getDelimiter()) {
747
            $config->setDelimiter($delimiter);
748
        }
749
750
        // query whether or not a custom escape character has been configured
751
        if ($escape = $this->getConfiguration()->getEscape()) {
752
            $config->setEscape($escape);
753
        }
754
755
        // query whether or not a custom enclosure character has been configured
756
        if ($enclosure = $this->getConfiguration()->getEnclosure()) {
757
            $config->setEnclosure($enclosure);
758
        }
759
760
        // query whether or not a custom source charset has been configured
761
        if ($fromCharset = $this->getConfiguration()->getFromCharset()) {
762
            $config->setFromCharset($fromCharset);
763
        }
764
765
        // query whether or not a custom target charset has been configured
766
        if ($toCharset = $this->getConfiguration()->getToCharset()) {
767
            $config->setToCharset($toCharset);
768
        }
769
770
        // return the lexer configuratio
771
        return $config;
772
    }
773
774
    /**
775
     * Imports the passed row into the database.
776
     *
777
     * If the import failed, the exception will be catched and logged,
778
     * but the import process will be continued.
779
     *
780
     * @param array $row The row with the data to be imported
781
     *
782
     * @return void
783
     */
784
    public function importRow(array $row)
785
    {
786
787
        // raise the line number and reset the skip row flag
788
        $this->lineNumber++;
789
        $this->skipRow = false;
790
791
        // initialize the headers with the columns from the first line
792
        if (sizeof($this->headers) === 0) {
793
            foreach ($row as $value => $key) {
794
                $this->headers[$this->mapAttributeCodeByHeaderMapping($key)] = $value;
795
            }
796
            return;
797
        }
798
799
        // process the observers
800
        foreach ($this->getObservers() as $observers) {
801
            // invoke the pre-import/import and post-import observers
802
            foreach ($observers as $observer) {
803
                // query whether or not we have to skip the row
804
                if ($this->skipRow) {
805
                    break;
806
                }
807
                // if not, process the next observer
808
                if ($observer instanceof ObserverInterface) {
809
                    $row = $observer->handle($row);
0 ignored issues
show
Bug introduced by
The method handle() does not seem to exist on object<TechDivision\Impo...vers\ObserverInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
810
                }
811
            }
812
        }
813
814
        // log a debug message with the actual line nr/file information
815
        $this->getSystemLogger()->debug(
816
            sprintf(
817
                'Successfully processed row (operation: %s) in file %s on line %d',
818
                $this->operationName,
819
                $this->filename,
820
                $this->lineNumber
821
            )
822
        );
823
    }
824
825
    /**
826
     * Map the passed attribute code, if a header mapping exists and return the
827
     * mapped mapping.
828
     *
829
     * @param string $attributeCode The attribute code to map
830
     *
831
     * @return string The mapped attribute code, or the original one
832
     */
833
    public function mapAttributeCodeByHeaderMapping($attributeCode)
834
    {
835
836
        // query weather or not we've a mapping, if yes, map the attribute code
837
        if (isset($this->headerMappings[$attributeCode])) {
838
            $attributeCode = $this->headerMappings[$attributeCode];
839
        }
840
841
        // return the (mapped) attribute code
842
        return $attributeCode;
843
    }
844
}
845