Completed
Pull Request — master (#129)
by Tim
24:26
created

AbstractSubject::getEmitter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
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 League\Event\EmitterInterface;
24
use Doctrine\Common\Collections\Collection;
25
use TechDivision\Import\RowTrait;
26
use TechDivision\Import\HeaderTrait;
27
use TechDivision\Import\SystemLoggerTrait;
28
use TechDivision\Import\Utils\ScopeKeys;
29
use TechDivision\Import\Utils\ColumnKeys;
30
use TechDivision\Import\Utils\MemberNames;
31
use TechDivision\Import\Utils\RegistryKeys;
32
use TechDivision\Import\Utils\Generators\GeneratorInterface;
33
use TechDivision\Import\Callbacks\CallbackInterface;
34
use TechDivision\Import\Observers\ObserverInterface;
35
use TechDivision\Import\Adapter\ImportAdapterInterface;
36
use TechDivision\Import\Exceptions\WrappedColumnException;
37
use TechDivision\Import\Services\RegistryProcessorInterface;
38
use TechDivision\Import\Configuration\SubjectConfigurationInterface;
39
use TechDivision\Import\Utils\EventNames;
40
41
/**
42
 * An abstract subject implementation.
43
 *
44
 * @author    Tim Wagner <[email protected]>
45
 * @copyright 2016 TechDivision GmbH <[email protected]>
46
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
47
 * @link      https://github.com/techdivision/import
48
 * @link      http://www.techdivision.com
49
 */
50
abstract class AbstractSubject implements SubjectInterface
51
{
52
53
    /**
54
     * The trait that provides basic filesystem handling functionality.
55
     *
56
     * @var TechDivision\Import\Subjects\FilesystemTrait
57
     */
58
    use FilesystemTrait;
59
60
    /**
61
     * The trait that provides basic filesystem handling functionality.
62
     *
63
     * @var TechDivision\Import\SystemLoggerTrait
64
     */
65
    use SystemLoggerTrait;
66
67
    /**
68
     * The trait that provides header handling functionality.
69
     *
70
     * @var TechDivision\Import\HeaderTrait
71
     */
72
    use HeaderTrait;
73
74
    /**
75
     * The trait that provides row handling functionality.
76
     *
77
     * @var TechDivision\Import\RowTrait
78
     */
79
    use RowTrait;
80
81
    /**
82
     * The name of the file to be imported.
83
     *
84
     * @var string
85
     */
86
    protected $filename;
87
88
    /**
89
     * The actual line number.
90
     *
91
     * @var integer
92
     */
93
    protected $lineNumber = 0;
94
95
    /**
96
     * The actual operation name.
97
     *
98
     * @var string
99
     */
100
    protected $operationName ;
101
102
    /**
103
     * The flag that stop's overserver execution on the actual row.
104
     *
105
     * @var boolean
106
     */
107
    protected $skipRow = false;
108
109
    /**
110
     * The import adapter instance.
111
     *
112
     * @var \TechDivision\Import\Adapter\ImportAdapterInterface
113
     */
114
    protected $importAdapter;
115
116
    /**
117
     * The system configuration.
118
     *
119
     * @var \TechDivision\Import\Configuration\SubjectConfigurationInterface
120
     */
121
    protected $configuration;
122
123
    /**
124
     * The RegistryProcessor instance to handle running threads.
125
     *
126
     * @var \TechDivision\Import\Services\RegistryProcessorInterface
127
     */
128
    protected $registryProcessor;
129
130
    /**
131
     * The actions unique serial.
132
     *
133
     * @var string
134
     */
135
    protected $serial;
136
137
    /**
138
     * Array with the subject's observers.
139
     *
140
     * @var array
141
     */
142
    protected $observers = array();
143
144
    /**
145
     * Array with the subject's callbacks.
146
     *
147
     * @var array
148
     */
149
    protected $callbacks = array();
150
151
    /**
152
     * The subject's callback mappings.
153
     *
154
     * @var array
155
     */
156
    protected $callbackMappings = array();
157
158
    /**
159
     * The available root categories.
160
     *
161
     * @var array
162
     */
163
    protected $rootCategories = array();
164
165
    /**
166
     * The Magento configuration.
167
     *
168
     * @var array
169
     */
170
    protected $coreConfigData = array();
171
172
    /**
173
     * The available stores.
174
     *
175
     * @var array
176
     */
177
    protected $stores = array();
178
179
    /**
180
     * The available websites.
181
     *
182
     * @var array
183
     */
184
    protected $storeWebsites = array();
185
186
    /**
187
     * The default store.
188
     *
189
     * @var array
190
     */
191
    protected $defaultStore;
192
193
    /**
194
     * The store view code the create the product/attributes for.
195
     *
196
     * @var string
197
     */
198
    protected $storeViewCode;
199
200
    /**
201
     * The UID generator for the core config data.
202
     *
203
     * @var \TechDivision\Import\Utils\Generators\GeneratorInterface
204
     */
205
    protected $coreConfigDataUidGenerator;
206
207
    /**
208
     * UNIX timestamp with the date the last row counter has been logged.
209
     *
210
     * @var integer
211
     */
212
    protected $lastLog = 0;
213
214
    /**
215
     * The number of the last line that has been logged with the row counter
216
     * @var integer
217
     */
218
    protected $lastLineNumber = 0;
219
220
    /**
221
     * The event emitter instance.
222
     *
223
     * @var \League\Event\EmitterInterface
224
     */
225
    protected $emitter;
226
227
    /**
228
     * Initialize the subject instance.
229
     *
230
     * @param \TechDivision\Import\Services\RegistryProcessorInterface $registryProcessor          The registry processor instance
231
     * @param \TechDivision\Import\Utils\Generators\GeneratorInterface $coreConfigDataUidGenerator The UID generator for the core config data
232
     * @param \Doctrine\Common\Collections\Collection                  $systemLoggers              The array with the system loggers instances
233
     * @param \League\Event\EmitterInterface                           $emitter                    The event emitter instance
234
     */
235 77
    public function __construct(
236
        RegistryProcessorInterface $registryProcessor,
237
        GeneratorInterface $coreConfigDataUidGenerator,
238
        Collection $systemLoggers,
239
        EmitterInterface $emitter
240
    ) {
241 77
        $this->emitter = $emitter;
242 77
        $this->systemLoggers = $systemLoggers;
0 ignored issues
show
Documentation Bug introduced by
It seems like $systemLoggers of type object<Doctrine\Common\Collections\Collection> is incompatible with the declared type array of property $systemLoggers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
243 77
        $this->registryProcessor = $registryProcessor;
244 77
        $this->coreConfigDataUidGenerator = $coreConfigDataUidGenerator;
245 77
    }
246
247
    /**
248
     * Return's the event emitter instance.
249
     *
250
     * @return \League\Event\EmitterInterface The event emitter instance
251
     */
252 11
    public function getEmitter()
253
    {
254 11
        return $this->emitter;
255
    }
256
257
    /**
258
     * Set's the name of the file to import
259
     *
260
     * @param string $filename The filename
261
     *
262
     * @return void
263
     */
264 14
    public function setFilename($filename)
265
    {
266 14
        $this->filename = $filename;
267 14
    }
268
269
    /**
270
     * Return's the name of the file to import.
271
     *
272
     * @return string The filename
273
     */
274 13
    public function getFilename()
275
    {
276 13
        return $this->filename;
277
    }
278
279
    /**
280
     * Set's the actual operation name.
281
     *
282
     * @param string $operationName The actual operation name
283
     *
284
     * @return void
285
     */
286 1
    public function setOperationName($operationName)
287
    {
288 1
        $this->operationName = $operationName;
289 1
    }
290
291
    /**
292
     * Return's the actual operation name.
293
     *
294
     * @return string
295
     */
296 1
    public function getOperationName()
297
    {
298 1
        return $this->operationName;
299
    }
300
301
    /**
302
     * Set's the actual line number.
303
     *
304
     * @param integer $lineNumber The line number
305
     *
306
     * @return void
307
     */
308 1
    public function setLineNumber($lineNumber)
309
    {
310 1
        $this->lineNumber = $lineNumber;
311 1
    }
312
313
    /**
314
     * Return's the actual line number.
315
     *
316
     * @return integer The line number
317
     */
318 13
    public function getLineNumber()
319
    {
320 13
        return $this->lineNumber;
321
    }
322
323
    /**
324
     * Stop's observer execution on the actual row.
325
     *
326
     * @return void
327
     */
328 1
    public function skipRow()
329
    {
330 1
        $this->skipRow = true;
331 1
    }
332
333
    /**
334
     * Return's the default callback mappings.
335
     *
336
     * @return array The default callback mappings
337
     */
338 1
    public function getDefaultCallbackMappings()
339
    {
340 1
        return array();
341
    }
342
343
    /**
344
     * Tries to format the passed value to a valid date with format 'Y-m-d H:i:s'.
345
     * If the passed value is NOT a valid date, NULL will be returned.
346
     *
347
     * @param string $value The value to format
348
     *
349
     * @return string|null The formatted date or NULL if the date is not valid
350
     */
351 2
    public function formatDate($value)
352
    {
353
354
        // create a DateTime instance from the passed value
355 2
        if ($dateTime = \DateTime::createFromFormat($this->getSourceDateFormat(), $value)) {
356 1
            return $dateTime->format('Y-m-d H:i:s');
357
        }
358
359
        // return NULL, if the passed value is NOT a valid date
360 1
        return null;
361
    }
362
363
    /**
364
     * Extracts the elements of the passed value by exploding them
365
     * with the also passed delimiter.
366
     *
367
     * @param string      $value     The value to extract
368
     * @param string|null $delimiter The delimiter used to extrace the elements
369
     *
370
     * @return array The exploded values
371
     */
372 2
    public function explode($value, $delimiter = null)
373
    {
374
        // load the global configuration
375 2
        $configuration = $this->getConfiguration();
376
377
        // initializet delimiter, enclosure and escape char
378 2
        $delimiter = $delimiter ? $delimiter : $configuration->getDelimiter();
379 2
        $enclosure = $configuration->getEnclosure();
380 2
        $escape = $configuration->getEscape();
381
382
        // parse and return the found data as array
383 2
        return str_getcsv($value, $delimiter, $enclosure, $escape);
384
    }
385
386
    /**
387
     * Queries whether or not debug mode is enabled or not, default is TRUE.
388
     *
389
     * @return boolean TRUE if debug mode is enabled, else FALSE
390
     */
391 1
    public function isDebugMode()
392
    {
393 1
        return $this->getConfiguration()->isDebugMode();
394
    }
395
396
    /**
397
     * Set's the subject configuration.
398
     *
399
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $configuration The subject configuration
400
     *
401
     * @return void
402
     */
403 77
    public function setConfiguration(SubjectConfigurationInterface $configuration)
404
    {
405 77
        $this->configuration = $configuration;
406 77
    }
407
408
    /**
409
     * Return's the subject configuration.
410
     *
411
     * @return \TechDivision\Import\Configuration\SubjectConfigurationInterface The subject configuration
412
     */
413 77
    public function getConfiguration()
414
    {
415 77
        return $this->configuration;
416
    }
417
418
    /**
419
     * Set's the import adapter instance.
420
     *
421
     * @param \TechDivision\Import\Adapter\ImportAdapterInterface $importAdapter The import adapter instance
422
     *
423
     * @return void
424
     */
425 3
    public function setImportAdapter(ImportAdapterInterface $importAdapter)
426
    {
427 3
        $this->importAdapter = $importAdapter;
428 3
    }
429
430
    /**
431
     * Return's the import adapter instance.
432
     *
433
     * @return \TechDivision\Import\Adapter\ImportAdapterInterface The import adapter instance
434
     */
435 3
    public function getImportAdapter()
436
    {
437 3
        return $this->importAdapter;
438
    }
439
440
    /**
441
     * Return's the RegistryProcessor instance to handle the running threads.
442
     *
443
     * @return \TechDivision\Import\Services\RegistryProcessorInterface The registry processor instance
444
     */
445 77
    public function getRegistryProcessor()
446
    {
447 77
        return $this->registryProcessor;
448
    }
449
450
    /**
451
     * Set's the unique serial for this import process.
452
     *
453
     * @param string $serial The unique serial
454
     *
455
     * @return void
456
     */
457 10
    public function setSerial($serial)
458
    {
459 10
        $this->serial = $serial;
460 10
    }
461
462
    /**
463
     * Return's the unique serial for this import process.
464
     *
465
     * @return string The unique serial
466
     */
467 4
    public function getSerial()
468
    {
469 4
        return $this->serial;
470
    }
471
472
    /**
473
     * Return's the source date format to use.
474
     *
475
     * @return string The source date format
476
     */
477 4
    public function getSourceDateFormat()
478
    {
479 4
        return $this->getConfiguration()->getSourceDateFormat();
480
    }
481
482
    /**
483
     * Return's the multiple field delimiter character to use, default value is comma (,).
484
     *
485
     * @return string The multiple field delimiter character
486
     */
487 1
    public function getMultipleFieldDelimiter()
488
    {
489 1
        return $this->getConfiguration()->getMultipleFieldDelimiter();
490
    }
491
492
    /**
493
     * Return's the multiple value delimiter character to use, default value is comma (|).
494
     *
495
     * @return string The multiple value delimiter character
496
     */
497 1
    public function getMultipleValueDelimiter()
498
    {
499 1
        return $this->getConfiguration()->getMultipleValueDelimiter();
500
    }
501
502
    /**
503
     * Intializes the previously loaded global data for exactly one bunch.
504
     *
505
     * @param string $serial The serial of the actual import
506
     *
507
     * @return void
508
     */
509 77
    public function setUp($serial)
510
    {
511
512
        // load the status of the actual import
513 77
        $status = $this->getRegistryProcessor()->getAttribute($serial);
514
515
        // load the global data we've prepared initially
516 77
        $this->stores = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::STORES];
517 77
        $this->defaultStore = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::DEFAULT_STORE];
518 77
        $this->storeWebsites  = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::STORE_WEBSITES];
519 77
        $this->rootCategories = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::ROOT_CATEGORIES];
520 77
        $this->coreConfigData = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::CORE_CONFIG_DATA];
521
522
        // initialize the operation name
523 77
        $this->operationName = $this->getConfiguration()->getConfiguration()->getOperationName();
524
525
        // merge the callback mappings with the mappings from the child instance
526 77
        $this->callbackMappings = array_merge($this->callbackMappings, $this->getDefaultCallbackMappings());
527
528
        // merge the header mappings with the values found in the configuration
529 77
        $this->headerMappings = array_merge($this->headerMappings, $this->getConfiguration()->getHeaderMappings());
530
531
        // merge the callback mappings the the one from the configuration file
532 77
        foreach ($this->getConfiguration()->getCallbacks() as $callbackMappings) {
533 77
            foreach ($callbackMappings as $attributeCode => $mappings) {
534
                // write a log message, that default callback configuration will
535
                // be overwritten with the one from the configuration file
536 77
                if (isset($this->callbackMappings[$attributeCode])) {
537 72
                    $this->getSystemLogger()->notice(
538 72
                        sprintf('Now override callback mappings for attribute %s with values found in configuration file', $attributeCode)
539
                    );
540
                }
541
542
                // override the attributes callbacks
543 77
                $this->callbackMappings[$attributeCode] = $mappings;
544
            }
545
        }
546 77
    }
547
548
    /**
549
     * Clean up the global data after importing the variants.
550
     *
551
     * @param string $serial The serial of the actual import
552
     *
553
     * @return void
554
     */
555 1
    public function tearDown($serial)
556
    {
557
558
        // load the registry processor
559 1
        $registryProcessor = $this->getRegistryProcessor();
560
561
        // update the source directory for the next subject
562 1
        $registryProcessor->mergeAttributesRecursive(
563 1
            $serial,
564
            array(
565 1
                RegistryKeys::SOURCE_DIRECTORY => $newSourceDir = $this->getNewSourceDir($serial),
566 1
                RegistryKeys::FILES => array($this->getFilename() => array(RegistryKeys::STATUS => 1))
567
            )
568
        );
569
570
        // log a debug message with the new source directory
571 1
        $this->getSystemLogger()->debug(
572 1
            sprintf('Subject %s successfully updated source directory to %s', get_class($this), $newSourceDir)
573
        );
574 1
    }
575
576
    /**
577
     * Return's the target directory for the artefact export.
578
     *
579
     * @return string The target directory for the artefact export
580
     */
581 1
    public function getTargetDir()
582
    {
583 1
        return $this->getNewSourceDir($this->getSerial());
584
    }
585
586
    /**
587
     * Return's the next source directory, which will be the target directory
588
     * of this subject, in most cases.
589
     *
590
     * @param string $serial The serial of the actual import
591
     *
592
     * @return string The new source directory
593
     */
594 4
    public function getNewSourceDir($serial)
595
    {
596 4
        return sprintf('%s/%s', $this->getConfiguration()->getTargetDir(), $serial);
597
    }
598
599
    /**
600
     * Register the passed observer with the specific type.
601
     *
602
     * @param \TechDivision\Import\Observers\ObserverInterface $observer The observer to register
603
     * @param string                                           $type     The type to register the observer with
604
     *
605
     * @return void
606
     */
607 6
    public function registerObserver(ObserverInterface $observer, $type)
608
    {
609
610
        // query whether or not the array with the callbacks for the
611
        // passed type has already been initialized, or not
612 6
        if (!isset($this->observers[$type])) {
613 6
            $this->observers[$type] = array();
614
        }
615
616
        // append the callback with the instance of the passed type
617 6
        $this->observers[$type][] = $observer;
618 6
    }
619
620
    /**
621
     * Register the passed callback with the specific type.
622
     *
623
     * @param \TechDivision\Import\Callbacks\CallbackInterface $callback The subject to register the callbacks for
624
     * @param string                                           $type     The type to register the callback with
625
     *
626
     * @return void
627
     */
628 2
    public function registerCallback(CallbackInterface $callback, $type)
629
    {
630
631
        // query whether or not the array with the callbacks for the
632
        // passed type has already been initialized, or not
633 2
        if (!isset($this->callbacks[$type])) {
634 2
            $this->callbacks[$type] = array();
635
        }
636
637
        // append the callback with the instance of the passed type
638 2
        $this->callbacks[$type][] = $callback;
639 2
    }
640
641
    /**
642
     * Return's the array with callbacks for the passed type.
643
     *
644
     * @param string $type The type of the callbacks to return
645
     *
646
     * @return array The callbacks
647
     */
648 1
    public function getCallbacksByType($type)
649
    {
650
651
        // initialize the array for the callbacks
652 1
        $callbacks = array();
653
654
        // query whether or not callbacks for the type are available
655 1
        if (isset($this->callbacks[$type])) {
656 1
            $callbacks = $this->callbacks[$type];
657
        }
658
659
        // return the array with the type's callbacks
660 1
        return $callbacks;
661
    }
662
663
    /**
664
     * Return's the array with the available observers.
665
     *
666
     * @return array The observers
667
     */
668 6
    public function getObservers()
669
    {
670 6
        return $this->observers;
671
    }
672
673
    /**
674
     * Return's the array with the available callbacks.
675
     *
676
     * @return array The callbacks
677
     */
678 1
    public function getCallbacks()
679
    {
680 1
        return $this->callbacks;
681
    }
682
683
    /**
684
     * Return's the callback mappings for this subject.
685
     *
686
     * @return array The array with the subject's callback mappings
687
     */
688 2
    public function getCallbackMappings()
689
    {
690 2
        return $this->callbackMappings;
691
    }
692
693
    /**
694
     * Imports the content of the file with the passed filename.
695
     *
696
     *
697
     * @param string $serial   The serial of the actual import
698
     * @param string $filename The filename to process
699
     *
700
     * @return void
701
     * @throws \Exception Is thrown, if the import can't be processed
702
     */
703 5
    public function import($serial, $filename)
704
    {
705
706
        try {
707
            // stop processing, if the filename doesn't match
708 5
            if (!$this->match($filename)) {
709 1
                return;
710
            }
711
712
            // initialize the serial/filename
713 4
            $this->setSerial($serial);
714 4
            $this->setFilename($filename);
715
716
            // invoke the events that has to be fired before the artfact will be processed
717 4
            $this->getEmitter()->emit(EventNames::SUBJECT_ARTEFACT_PROCESS_START, $this);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
718
719
            // load the system logger instance
720 4
            $systemLogger = $this->getSystemLogger();
721
722
            // prepare the flag filenames
723 4
            $inProgressFilename = sprintf('%s.inProgress', $filename);
724 4
            $importedFilename = sprintf('%s.imported', $filename);
725 4
            $failedFilename = sprintf('%s.failed', $filename);
726
727
            // query whether or not the file has already been imported
728 4
            if ($this->isFile($failedFilename) ||
729 3
                $this->isFile($importedFilename) ||
730 4
                $this->isFile($inProgressFilename)
731
            ) {
732
                // log a debug message and exit
733 1
                $systemLogger->debug(sprintf('Import running, found inProgress file %s', $inProgressFilename));
734 1
                return;
735
            }
736
737
            // flag file as in progress
738 3
            $this->touch($inProgressFilename);
739
740
            // track the start time
741 3
            $startTime = microtime(true);
742
743
            // initialize the last time we've logged the counter with the processed rows per minute
744 3
            $this->lastLog = time();
745
746
            // log a message that the file has to be imported
747 3
            $systemLogger->info(sprintf('Now start processing file %s', $filename));
748
749
            // let the adapter process the file
750 3
            $this->getImportAdapter()->import(array($this, 'importRow'), $filename);
751
752
            // track the time needed for the import in seconds
753 1
            $endTime = microtime(true) - $startTime;
754
755
            // log a message that the file has successfully been imported
756 1
            $systemLogger->info(sprintf('Successfully processed file %s with %d lines in %f s', $filename, $this->lineNumber, $endTime));
757
758
            // rename flag file, because import has been successfull
759 1
            $this->rename($inProgressFilename, $importedFilename);
760
761
            // invoke the events that has to be fired when the artfact has been successfully processed
762 1
            $this->getEmitter()->emit(EventNames::SUBJECT_ARTEFACT_PROCESS_SUCCESS, $this);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
763
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
764 2
        } catch (\Exception $e) {
765
            // rename the flag file, because import failed and write the stack trace
766 2
            $this->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...
767 2
            $this->write($failedFilename, $e->__toString());
768
769
            // invoke the events that has to be fired when the artfact can't be processed
770 2
            $this->getEmitter()->emit(EventNames::SUBJECT_ARTEFACT_PROCESS_FAILURE, $this, $e);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
771
772
            // do not wrap the exception if not already done
773 2
            if ($e instanceof WrappedColumnException) {
774 1
                throw $e;
775
            }
776
777
            // else wrap and throw the exception
778 1
            throw $this->wrapException(array(), $e);
779
        }
780 1
    }
781
782
    /**
783
     * This method queries whether or not the passed filename matches
784
     * the pattern, based on the subjects configured prefix.
785
     *
786
     * @param string $filename The filename to match
787
     *
788
     * @return boolean TRUE if the filename matches, else FALSE
789
     */
790 5
    protected function match($filename)
791
    {
792
793
        // prepare the pattern to query whether the file has to be processed or not
794 5
        $pattern = sprintf(
795 5
            '/^.*\/%s.*\\.%s$/',
796 5
            $this->getConfiguration()->getPrefix(),
797 5
            $this->getConfiguration()->getSuffix()
798
        );
799
800
        // stop processing, if the filename doesn't match
801 5
        return (boolean) preg_match($pattern, $filename);
802
    }
803
804
    /**
805
     * Imports the passed row into the database. If the import failed, the exception
806
     * will be catched and logged, but the import process will be continued.
807
     *
808
     * @param array $row The row with the data to be imported
809
     *
810
     * @return void
811
     */
812 7
    public function importRow(array $row)
813
    {
814
815
        // initialize the row
816 7
        $this->row = $row;
817
818
        // raise the line number and reset the skip row flag
819 7
        $this->lineNumber++;
820 7
        $this->skipRow = false;
821
822
        // invoke the events that has to be fired before the artfact's row will be processed
823 7
        $this->getEmitter()->emit(EventNames::SUBJECT_ARTEFACT_ROW_PROCESS_START, $this);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
824
825
        // initialize the headers with the columns from the first line
826 7
        if (sizeof($this->headers) === 0) {
827 1
            foreach ($this->row as $value => $key) {
828 1
                $this->headers[$this->mapAttributeCodeByHeaderMapping($key)] = $value;
829
            }
830 1
            return;
831
        }
832
833
        // process the observers
834 6
        foreach ($this->getObservers() as $observers) {
835
            // invoke the pre-import/import and post-import observers
836 6
            foreach ($observers as $observer) {
837
                // query whether or not we have to skip the row
838 6
                if ($this->skipRow) {
839
                    // log a debug message with the actual line nr/file information
840 1
                    $this->getSystemLogger()->warning(
841 1
                        $this->appendExceptionSuffix(
842 1
                            sprintf(
843 1
                                'Skip processing operation "%s" after observer "%s"',
844 1
                                $this->operationName,
845 1
                                $this->getConfiguration()->getId()
846
                            )
847
                        )
848
                    );
849
850
                    // skip the row
851 1
                    break 2;
852
                }
853
854
                // if not, set the subject and process the observer
855 6
                if ($observer instanceof ObserverInterface) {
856 6
                    $this->row = $observer->handle($this);
857
                }
858
            }
859
        }
860
861
        // query whether or not a minute has been passed
862 6
        if ($this->lastLog < time() - 60) {
863
            // log the number processed rows per minute
864 6
            $this->getSystemLogger()->info(
865 6
                sprintf(
866 6
                    'Successfully processed "%d (%d)" rows per minute of file "%s"',
867 6
                    $this->lineNumber - $this->lastLineNumber,
868 6
                    $this->lineNumber,
869 6
                    $this->getFilename()
870
                )
871
            );
872
873
            // reset the last log time and the line number
874 6
            $this->lastLog = time();
875 6
            $this->lastLineNumber = $this->lineNumber;
876
        }
877
878
        // log a debug message with the actual line nr/file information
879 6
        $this->getSystemLogger()->debug(
880 6
            $this->appendExceptionSuffix(
881 6
                sprintf(
882 6
                    'Successfully processed operation "%s"',
883 6
                    $this->operationName
884
                )
885
            )
886
        );
887
888
        // invoke the events that has to be fired when the artfact's row has been successfully processed
889 6
        $this->getEmitter()->emit(EventNames::SUBJECT_ARTEFACT_ROW_PROCESS_SUCCESS, $this);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
890 6
    }
891
892
    /**
893
     * Queries whether or not that the subject needs an OK file to be processed.
894
     *
895
     * @return boolean TRUE if the subject needs an OK file, else FALSE
896
     */
897 1
    public function isOkFileNeeded()
898
    {
899 1
        return $this->getConfiguration()->isOkFileNeeded();
900
    }
901
902
    /**
903
     * Return's the default store.
904
     *
905
     * @return array The default store
906
     */
907
    public function getDefaultStore()
908
    {
909
        return $this->defaultStore;
910
    }
911
912
    /**
913
     * Return's the default store view code.
914
     *
915
     * @return array The default store view code
916
     */
917 5
    public function getDefaultStoreViewCode()
918
    {
919 5
        return $this->defaultStore[MemberNames::CODE];
920
    }
921
922
    /**
923
     * Set's the store view code the create the product/attributes for.
924
     *
925
     * @param string $storeViewCode The store view code
926
     *
927
     * @return void
928
     */
929 4
    public function setStoreViewCode($storeViewCode)
930
    {
931 4
        $this->storeViewCode = $storeViewCode;
932 4
    }
933
934
    /**
935
     * Return's the store view code the create the product/attributes for.
936
     *
937
     * @param string|null $default The default value to return, if the store view code has not been set
938
     *
939
     * @return string The store view code
940
     */
941 8
    public function getStoreViewCode($default = null)
942
    {
943
944
        // return the store view code, if available
945 8
        if ($this->storeViewCode !== null) {
946 4
            return $this->storeViewCode;
947
        }
948
949
        // if NOT and a default code is available
950 4
        if ($default !== null) {
951
            // return the default value
952 3
            return $default;
953
        }
954
955
        // return the default store view code
956 1
        return $this->getDefaultStoreViewCode();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getDefaultStoreViewCode(); (array) is incompatible with the return type declared by the interface TechDivision\Import\Subj...rface::getStoreViewCode of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
957
    }
958
959
    /**
960
     * Prepare's the store view code in the subject. If the store_view_code row doesn't contain
961
     * any value, the default code of the default store view will be set.
962
     *
963
     * @return void
964
     */
965 2
    public function prepareStoreViewCode()
966
    {
967
968
        // re-set the store view code
969 2
        $this->setStoreViewCode(null);
970
971
        // initialize the store view code
972 2
        if ($storeViewCode = $this->getValue(ColumnKeys::STORE_VIEW_CODE)) {
973 2
            $this->setStoreViewCode($storeViewCode);
974
        }
975 2
    }
976
977
    /**
978
     * Return's the store ID of the store with the passed store view code
979
     *
980
     * @param string $storeViewCode The store view code to return the store ID for
981
     *
982
     * @return integer The ID of the store with the passed ID
983
     * @throws \Exception Is thrown, if the store with the actual code is not available
984
     */
985 4
    public function getStoreId($storeViewCode)
986
    {
987
988
        // query whether or not, the requested store is available
989 4
        if (isset($this->stores[$storeViewCode])) {
990 3
            return (integer) $this->stores[$storeViewCode][MemberNames::STORE_ID];
991
        }
992
993
        // throw an exception, if not
994 1
        throw new \Exception(
995 1
            sprintf(
996 1
                'Found invalid store view code %s in file %s on line %d',
997 1
                $storeViewCode,
998 1
                $this->getFilename(),
999 1
                $this->getLineNumber()
1000
            )
1001
        );
1002
    }
1003
1004
    /**
1005
     * Return's the store ID of the actual row, or of the default store
1006
     * if no store view code is set in the CSV file.
1007
     *
1008
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
1009
     *
1010
     * @return integer The ID of the actual store
1011
     * @throws \Exception Is thrown, if the store with the actual code is not available
1012
     */
1013 2
    public function getRowStoreId($default = null)
1014
    {
1015
1016
        // initialize the default store view code, if not passed
1017 2
        if ($default === null) {
1018 2
            $default = $this->getDefaultStoreViewCode();
1019
        }
1020
1021
        // load the store view code the create the product/attributes for
1022 2
        return $this->getStoreId($this->getStoreViewCode($default));
0 ignored issues
show
Bug introduced by
It seems like $default defined by $this->getDefaultStoreViewCode() on line 1018 can also be of type array; however, TechDivision\Import\Subj...ect::getStoreViewCode() does only seem to accept string|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $this->getStoreViewCode($default) targeting TechDivision\Import\Subj...ect::getStoreViewCode() can also be of type array; however, TechDivision\Import\Subj...ctSubject::getStoreId() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1023
    }
1024
1025
    /**
1026
     * Return's the root category for the actual view store.
1027
     *
1028
     * @return array The store's root category
1029
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
1030
     */
1031 2
    public function getRootCategory()
1032
    {
1033
1034
        // load the actual store view code
1035 2
        $storeViewCode = $this->getStoreViewCode($this->getDefaultStoreViewCode());
0 ignored issues
show
Documentation introduced by
$this->getDefaultStoreViewCode() is of type array, but the function expects a string|null.

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...
1036
1037
        // query weather or not we've a root category or not
1038 2
        if (isset($this->rootCategories[$storeViewCode])) {
1039 1
            return $this->rootCategories[$storeViewCode];
1040
        }
1041
1042
        // throw an exception if the root category is NOT available
1043 1
        throw new \Exception(sprintf('Root category for %s is not available', $storeViewCode));
1044
    }
1045
1046
    /**
1047
     * Return's the Magento configuration value.
1048
     *
1049
     * @param string  $path    The Magento path of the requested configuration value
1050
     * @param mixed   $default The default value that has to be returned, if the requested configuration value is not set
1051
     * @param string  $scope   The scope the configuration value has been set
1052
     * @param integer $scopeId The scope ID the configuration value has been set
1053
     *
1054
     * @return mixed The configuration value
1055
     * @throws \Exception Is thrown, if nor a value can be found or a default value has been passed
1056
     */
1057 5
    public function getCoreConfigData($path, $default = null, $scope = ScopeKeys::SCOPE_DEFAULT, $scopeId = 0)
1058
    {
1059
1060
        // initialize the core config data
1061
        $coreConfigData = array(
1062 5
            MemberNames::PATH => $path,
1063 5
            MemberNames::SCOPE => $scope,
1064 5
            MemberNames::SCOPE_ID => $scopeId
1065
        );
1066
1067
        // generate the UID from the passed data
1068 5
        $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1069
1070
        // iterate over the core config data and try to find the requested configuration value
1071 5
        if (isset($this->coreConfigData[$uniqueIdentifier])) {
1072 1
            return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1073
        }
1074
1075
        // query whether or not we've to query for the configuration value on fallback level 'websites' also
1076 4
        if ($scope === ScopeKeys::SCOPE_STORES) {
1077
            // query whether or not the website with the passed ID is available
1078 2
            foreach ($this->storeWebsites as $storeWebsite) {
1079 2
                if ($storeWebsite[MemberNames::WEBSITE_ID] === $scopeId) {
1080
                    // replace scope with 'websites' and website ID
1081 2
                    $coreConfigData = array_merge(
1082 2
                        $coreConfigData,
1083
                        array(
1084 2
                            MemberNames::SCOPE    => ScopeKeys::SCOPE_WEBSITES,
1085
                            MemberNames::SCOPE_ID => $storeWebsite[MemberNames::WEBSITE_ID]
1086
                        )
1087
                    );
1088
1089
                    // generate the UID from the passed data, merged with the 'websites' scope and ID
1090 2
                    $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1091
1092
                    // query whether or not, the configuration value on 'websites' level
1093 2 View Code Duplication
                    if (isset($this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE])) {
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...
1094 2
                        return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1095
                    }
1096
                }
1097
            }
1098
        }
1099
1100
        // replace scope with 'default' and scope ID '0'
1101 3
        $coreConfigData = array_merge(
1102 3
            $coreConfigData,
1103
            array(
1104 3
                MemberNames::SCOPE    => ScopeKeys::SCOPE_DEFAULT,
1105
                MemberNames::SCOPE_ID => 0
1106
            )
1107
        );
1108
1109
        // generate the UID from the passed data, merged with the 'default' scope and ID 0
1110 3
        $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1111
1112
        // query whether or not, the configuration value on 'default' level
1113 3 View Code Duplication
        if (isset($this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE])) {
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...
1114 1
            return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1115
        }
1116
1117
        // if not, return the passed default value
1118 2
        if ($default !== null) {
1119 1
            return $default;
1120
        }
1121
1122
        // throw an exception if no value can be found
1123
        // in the Magento configuration
1124 1
        throw new \Exception(
1125 1
            sprintf(
1126 1
                'Can\'t find a value for configuration "%s-%s-%d" in "core_config_data"',
1127 1
                $path,
1128 1
                $scope,
1129 1
                $scopeId
1130
            )
1131
        );
1132
    }
1133
1134
    /**
1135
     * Resolve the original column name for the passed one.
1136
     *
1137
     * @param string $columnName The column name that has to be resolved
1138
     *
1139
     * @return string|null The original column name
1140
     */
1141 2
    public function resolveOriginalColumnName($columnName)
1142
    {
1143
1144
        // try to load the original data
1145 2
        $originalData = $this->getOriginalData();
1146
1147
        // query whether or not original data is available
1148 2
        if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES])) {
1149
            // query whether or not the original column name is available
1150 1
            if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES][$columnName])) {
1151 1
                return $originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES][$columnName];
1152
            }
1153
1154
            // query whether or a wildcard column name is available
1155 1
            if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES]['*'])) {
1156 1
                return $originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES]['*'];
1157
            }
1158
        }
1159
1160
        // return the original column name
1161 1
        return $columnName;
1162
    }
1163
1164
    /**
1165
     * Return's the original data if available, or an empty array.
1166
     *
1167
     * @return array The original data
1168
     */
1169 2
    public function getOriginalData()
1170
    {
1171
1172
        // initialize the array for the original data
1173 2
        $originalData = array();
1174
1175
        // query whether or not the column contains original data
1176 2
        if ($this->hasOriginalData()) {
1177
            // unerialize the original data from the column
1178 1
            $originalData = unserialize($this->row[$this->headers[ColumnKeys::ORIGINAL_DATA]]);
1179
        }
1180
1181
        // return an empty array, if not
1182 2
        return $originalData;
1183
    }
1184
1185
    /**
1186
     * Query's whether or not the actual column contains original data like
1187
     * filename, line number and column names.
1188
     *
1189
     * @return boolean TRUE if the actual column contains origin data, else FALSE
1190
     */
1191 3
    public function hasOriginalData()
1192
    {
1193 3
        return isset($this->headers[ColumnKeys::ORIGINAL_DATA]) && isset($this->row[$this->headers[ColumnKeys::ORIGINAL_DATA]]);
1194
    }
1195
1196
    /**
1197
     * Wraps the passed exeception into a new one by trying to resolve the original filname,
1198
     * line number and column names and use it for a detailed exception message.
1199
     *
1200
     * @param array      $columnNames The column names that should be resolved and wrapped
1201
     * @param \Exception $parent      The exception we want to wrap
1202
     * @param string     $className   The class name of the exception type we want to wrap the parent one
1203
     *
1204
     * @return \Exception the wrapped exception
1205
     */
1206 2
    public function wrapException(
1207
        array $columnNames = array(),
1208
        \Exception $parent = null,
1209
        $className = '\TechDivision\Import\Exceptions\WrappedColumnException'
1210
    ) {
1211
1212
        // initialize the message
1213 2
        $message = $parent->getMessage();
0 ignored issues
show
Bug introduced by
It seems like $parent is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
1214
1215
        // query whether or not has been a result of invalid data of a previous column of a CSV file
1216 2
        if ($this->hasOriginalData()) {
1217
            // load the original data
1218 1
            $originalData = $this->getOriginalData();
1219
1220
            // replace old filename and line number of the original message
1221 1
            $message = $this->appendExceptionSuffix(
1222 1
                $this->stripExceptionSuffix($message),
1223 1
                $originalData[ColumnKeys::ORIGINAL_FILENAME],
1224 1
                $originalData[ColumnKeys::ORIGINAL_LINE_NUMBER]
1225
            );
1226
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
1227
        } else {
1228
            // append filename and line number to the original message
1229 1
            $message = $this->appendExceptionSuffix(
1230 1
                $this->stripExceptionSuffix($message),
1231 1
                $this->filename,
1232 1
                $this->lineNumber
1233
            );
1234
        }
1235
1236
        // query whether or not, column names has been passed
1237 2
        if (sizeof($columnNames) > 0) {
1238
            // prepare the original column names
1239 1
            $originalColumnNames = array();
1240 1
            foreach ($columnNames as $columnName) {
1241 1
                $originalColumnNames[] = $this->resolveOriginalColumnName($columnName);
1242
            }
1243
1244
            // append the column information
1245 1
            $message = sprintf('%s in column(s) %s', $message, implode(', ', $originalColumnNames));
1246
        }
1247
1248
        // create a new exception and wrap the parent one
1249 2
        return new $className($message, null, $parent);
1250
    }
1251
1252
    /**
1253
     * Strip's the exception suffix containing filename and line number from the
1254
     * passed message.
1255
     *
1256
     * @param string $message The message to strip the exception suffix from
1257
     *
1258
     * @return mixed The message without the exception suffix
1259
     */
1260 2
    public function stripExceptionSuffix($message)
1261
    {
1262 2
        return str_replace($this->appendExceptionSuffix(), '', $message);
1263
    }
1264
1265
    /**
1266
     * Append's the exception suffix containing filename and line number to the
1267
     * passed message. If no message has been passed, only the suffix will be
1268
     * returned
1269
     *
1270
     * @param string|null $message    The message to append the exception suffix to
1271
     * @param string|null $filename   The filename used to create the suffix
1272
     * @param string|null $lineNumber The line number used to create the suffx
1273
     *
1274
     * @return string The message with the appended exception suffix
1275
     */
1276 12
    public function appendExceptionSuffix($message = null, $filename = null, $lineNumber = null)
1277
    {
1278
1279
        // query whether or not a filename has been passed
1280 12
        if ($filename === null) {
1281 12
            $filename = $this->getFilename();
1282
        }
1283
1284
        // query whether or not a line number has been passed
1285 12
        if ($lineNumber === null) {
1286 12
            $lineNumber = $this->getLineNumber();
1287
        }
1288
1289
        // if no message has been passed, only return the suffix
1290 12
        if ($message === null) {
1291 2
            return sprintf(' in file %s on line %d', $filename, $lineNumber);
1292
        }
1293
1294
        // concatenate the message with the suffix and return it
1295 12
        return sprintf('%s in file %s on line %d', $message, $filename, $lineNumber);
1296
    }
1297
1298
    /**
1299
     * Raises the value for the counter with the passed key by one.
1300
     *
1301
     * @param mixed $counterName The name of the counter to raise
1302
     *
1303
     * @return integer The counter's new value
1304
     */
1305 1
    public function raiseCounter($counterName)
1306
    {
1307
1308
        // raise the counter with the passed name
1309 1
        return $this->getRegistryProcessor()->raiseCounter(
1310 1
            $this->getSerial(),
1311 1
            $counterName
1312
        );
1313
    }
1314
1315
    /**
1316
     * Merge the passed array into the status of the actual import.
1317
     *
1318
     * @param array $status The status information to be merged
1319
     *
1320
     * @return void
1321
     */
1322 1
    public function mergeAttributesRecursive(array $status)
1323
    {
1324
1325
        // merge the passed status
1326 1
        $this->getRegistryProcessor()->mergeAttributesRecursive(
1327 1
            $this->getSerial(),
1328 1
            $status
1329
        );
1330 1
    }
1331
}
1332