Completed
Push — 15.x ( d82ad5...f4a7f0 )
by Tim
02:46
created

AbstractSubject::hasOriginalData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
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 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\EventNames;
31
use TechDivision\Import\Utils\MemberNames;
32
use TechDivision\Import\Utils\RegistryKeys;
33
use TechDivision\Import\Utils\EntityTypeCodes;
34
use TechDivision\Import\Utils\Generators\GeneratorInterface;
35
use TechDivision\Import\Callbacks\CallbackInterface;
36
use TechDivision\Import\Observers\ObserverInterface;
37
use TechDivision\Import\Adapter\ImportAdapterInterface;
38
use TechDivision\Import\Exceptions\WrappedColumnException;
39
use TechDivision\Import\Services\RegistryProcessorInterface;
40
use TechDivision\Import\Configuration\SubjectConfigurationInterface;
41
42
/**
43
 * An abstract subject implementation.
44
 *
45
 * @author    Tim Wagner <[email protected]>
46
 * @copyright 2016 TechDivision GmbH <[email protected]>
47
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
48
 * @link      https://github.com/techdivision/import
49
 * @link      http://www.techdivision.com
50
 */
51
abstract class AbstractSubject implements SubjectInterface, FilesystemSubjectInterface, DateConverterSubjectInterface
52
{
53
54
    /**
55
     * The trait that provides basic filesystem handling functionality.
56
     *
57
     * @var \TechDivision\Import\Subjects\FilesystemTrait
58
     */
59
    use FilesystemTrait;
60
61
    /**
62
     * The trait that provides basic filesystem handling functionality.
63
     *
64
     * @var \TechDivision\Import\SystemLoggerTrait
65
     */
66
    use SystemLoggerTrait;
67
68
    /**
69
     * The trait that provides date converting functionality.
70
     *
71
     * @var \TechDivision\Import\DateConverterTrait
72
     */
73
    use DateConverterTrait;
74
75
    /**
76
     * The trait that provides header handling functionality.
77
     *
78
     * @var \TechDivision\Import\HeaderTrait
79
     */
80
    use HeaderTrait;
81
82
    /**
83
     * The trait that provides row handling functionality.
84
     *
85
     * @var \TechDivision\Import\RowTrait
86
     */
87
    use RowTrait;
88
89
    /**
90
     * The name of the file to be imported.
91
     *
92
     * @var string
93
     */
94
    protected $filename;
95
96
    /**
97
     * The actual line number.
98
     *
99
     * @var integer
100
     */
101
    protected $lineNumber = 0;
102
103
    /**
104
     * The import adapter instance.
105
     *
106
     * @var \TechDivision\Import\Adapter\ImportAdapterInterface
107
     */
108
    protected $importAdapter;
109
110
    /**
111
     * The subject configuration.
112
     *
113
     * @var \TechDivision\Import\Configuration\SubjectConfigurationInterface
114
     */
115
    protected $configuration;
116
117
    /**
118
     * The plugin configuration.
119
     *
120
     * @var \TechDivision\Import\Configuration\PluginConfigurationInterface
121
     */
122
    protected $pluginConfiguration;
123
124
    /**
125
     * The RegistryProcessor instance to handle running threads.
126
     *
127
     * @var \TechDivision\Import\Services\RegistryProcessorInterface
128
     */
129
    protected $registryProcessor;
130
131
    /**
132
     * The actions unique serial.
133
     *
134
     * @var string
135
     */
136
    protected $serial;
137
138
    /**
139
     * Array with the subject's observers.
140
     *
141
     * @var array
142
     */
143
    protected $observers = array();
144
145
    /**
146
     * Array with the subject's callbacks.
147
     *
148
     * @var array
149
     */
150
    protected $callbacks = array();
151
152
    /**
153
     * The subject's callback mappings.
154
     *
155
     * @var array
156
     */
157
    protected $callbackMappings = array();
158
159
    /**
160
     * The available root categories.
161
     *
162
     * @var array
163
     */
164
    protected $rootCategories = array();
165
166
    /**
167
     * The Magento configuration.
168
     *
169
     * @var array
170
     */
171
    protected $coreConfigData = array();
172
173
    /**
174
     * The available stores.
175
     *
176
     * @var array
177
     */
178
    protected $stores = array();
179
180
    /**
181
     * The available websites.
182
     *
183
     * @var array
184
     */
185
    protected $storeWebsites = array();
186
187
    /**
188
     * The default store.
189
     *
190
     * @var array
191
     */
192
    protected $defaultStore;
193
194
    /**
195
     * The store view code the create the product/attributes for.
196
     *
197
     * @var string
198
     */
199
    protected $storeViewCode;
200
201
    /**
202
     * The UID generator for the core config data.
203
     *
204
     * @var \TechDivision\Import\Utils\Generators\GeneratorInterface
205
     */
206
    protected $coreConfigDataUidGenerator;
207
208
    /**
209
     * UNIX timestamp with the date the last row counter has been logged.
210
     *
211
     * @var integer
212
     */
213
    protected $lastLog = 0;
214
215
    /**
216
     * The number of the last line that has been logged with the row counter
217
     * @var integer
218
     */
219
    protected $lastLineNumber = 0;
220
221
    /**
222
     * The event emitter instance.
223
     *
224
     * @var \League\Event\EmitterInterface
225
     */
226
    protected $emitter;
227
228
    /**
229
     * Mapping for the virtual entity type code to the real Magento 2 EAV entity type code.
230
     *
231
     * @var array
232
     */
233
    protected $entityTypeCodeMappings = array(
234
        EntityTypeCodes::EAV_ATTRIBUTE                 => EntityTypeCodes::CATALOG_PRODUCT,
235
        EntityTypeCodes::EAV_ATTRIBUTE_SET             => EntityTypeCodes::CATALOG_PRODUCT,
236
        EntityTypeCodes::CATALOG_PRODUCT_PRICE         => EntityTypeCodes::CATALOG_PRODUCT,
237
        EntityTypeCodes::CATALOG_PRODUCT_INVENTORY     => EntityTypeCodes::CATALOG_PRODUCT,
238
        EntityTypeCodes::CATALOG_PRODUCT_INVENTORY_MSI => EntityTypeCodes::CATALOG_PRODUCT,
239
        EntityTypeCodes::CATALOG_PRODUCT_TIER_PRICE    => EntityTypeCodes::CATALOG_PRODUCT
240
    );
241
242
    /**
243
     * Initialize the subject instance.
244
     *
245
     * @param \TechDivision\Import\Services\RegistryProcessorInterface $registryProcessor          The registry processor instance
246
     * @param \TechDivision\Import\Utils\Generators\GeneratorInterface $coreConfigDataUidGenerator The UID generator for the core config data
247
     * @param \Doctrine\Common\Collections\Collection                  $systemLoggers              The array with the system loggers instances
248
     * @param \League\Event\EmitterInterface                           $emitter                    The event emitter instance
249
     */
250 82
    public function __construct(
251
        RegistryProcessorInterface $registryProcessor,
252
        GeneratorInterface $coreConfigDataUidGenerator,
253
        Collection $systemLoggers,
254
        EmitterInterface $emitter
255
    ) {
256 82
        $this->emitter = $emitter;
257 82
        $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...
258 82
        $this->registryProcessor = $registryProcessor;
259 82
        $this->coreConfigDataUidGenerator = $coreConfigDataUidGenerator;
260 82
    }
261
262
    /**
263
     * Return's the event emitter instance.
264
     *
265
     * @return \League\Event\EmitterInterface The event emitter instance
266
     */
267 9
    public function getEmitter()
268
    {
269 9
        return $this->emitter;
270
    }
271
272
    /**
273
     * Set's the name of the file to import
274
     *
275
     * @param string $filename The filename
276
     *
277
     * @return void
278
     */
279 13
    public function setFilename($filename)
280
    {
281 13
        $this->filename = $filename;
282 13
    }
283
284
    /**
285
     * Return's the name of the file to import.
286
     *
287
     * @return string The filename
288
     */
289 14
    public function getFilename()
290
    {
291 14
        return $this->filename;
292
    }
293
294
    /**
295
     * Set's the actual line number.
296
     *
297
     * @param integer $lineNumber The line number
298
     *
299
     * @return void
300
     */
301 1
    public function setLineNumber($lineNumber)
302
    {
303 1
        $this->lineNumber = $lineNumber;
304 1
    }
305
306
    /**
307
     * Return's the actual line number.
308
     *
309
     * @return integer The line number
310
     */
311 9
    public function getLineNumber()
312
    {
313 9
        return $this->lineNumber;
314
    }
315
316
    /**
317
     * Return's the default callback mappings.
318
     *
319
     * @return array The default callback mappings
320
     */
321 1
    public function getDefaultCallbackMappings()
322
    {
323 1
        return array();
324
    }
325
326
    /**
327
     * Tries to format the passed value to a valid date with format 'Y-m-d H:i:s'.
328
     * If the passed value is NOT a valid date, NULL will be returned.
329
     *
330
     * @param string $value The value to format
331
     *
332
     * @return string|null The formatted date or NULL if the date is not valid
333
     */
334
    public function formatDate($value)
335
    {
336
        return $this->getDateConverter()->convert($value);
337
    }
338
339
    /**
340
     * Extracts the elements of the passed value by exploding them
341
     * with the also passed delimiter.
342
     *
343
     * @param string      $value     The value to extract
344
     * @param string|null $delimiter The delimiter used to extrace the elements
345
     *
346
     * @return array The exploded values
347
     */
348
    public function explode($value, $delimiter = null)
349
    {
350
        return $this->getImportAdapter()->explode($value, $delimiter);
351
    }
352
353
    /**
354
     * Queries whether or not debug mode is enabled or not, default is TRUE.
355
     *
356
     * @return boolean TRUE if debug mode is enabled, else FALSE
357
     */
358 1
    public function isDebugMode()
359
    {
360 1
        return $this->getConfiguration()->isDebugMode();
361
    }
362
363
    /**
364
     * Return's the subject's execution context configuration.
365
     *
366
     * @return \TechDivision\Import\ExecutionContextInterface The execution context configuration to use
367
     */
368
    public function getExecutionContext()
369
    {
370
        return $this->getConfiguration()->getPluginConfiguration()->getExecutionContext();
0 ignored issues
show
Bug introduced by
The method getExecutionContext() does not seem to exist on object<TechDivision\Impo...ConfigurationInterface>.

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...
371
    }
372
373
    /**
374
     * Set's the subject configuration.
375
     *
376
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $configuration The subject configuration
377
     *
378
     * @return void
379
     */
380 82
    public function setConfiguration(SubjectConfigurationInterface $configuration)
381
    {
382 82
        $this->configuration = $configuration;
383 82
    }
384
385
    /**
386
     * Return's the subject configuration.
387
     *
388
     * @return \TechDivision\Import\Configuration\SubjectConfigurationInterface The subject configuration
389
     */
390 82
    public function getConfiguration()
391
    {
392 82
        return $this->configuration;
393
    }
394
395
    /**
396
     * Set's the import adapter instance.
397
     *
398
     * @param \TechDivision\Import\Adapter\ImportAdapterInterface $importAdapter The import adapter instance
399
     *
400
     * @return void
401
     */
402 1
    public function setImportAdapter(ImportAdapterInterface $importAdapter)
403
    {
404 1
        $this->importAdapter = $importAdapter;
405 1
    }
406
407
    /**
408
     * Return's the import adapter instance.
409
     *
410
     * @return \TechDivision\Import\Adapter\ImportAdapterInterface The import adapter instance
411
     */
412 1
    public function getImportAdapter()
413
    {
414 1
        return $this->importAdapter;
415
    }
416
417
    /**
418
     * Return's the RegistryProcessor instance to handle the running threads.
419
     *
420
     * @return \TechDivision\Import\Services\RegistryProcessorInterface The registry processor instance
421
     */
422 82
    public function getRegistryProcessor()
423
    {
424 82
        return $this->registryProcessor;
425
    }
426
427
    /**
428
     * Set's the unique serial for this import process.
429
     *
430
     * @param string $serial The unique serial
431
     *
432
     * @return void
433
     */
434 9
    public function setSerial($serial)
435
    {
436 9
        $this->serial = $serial;
437 9
    }
438
439
    /**
440
     * Return's the unique serial for this import process.
441
     *
442
     * @return string The unique serial
443
     */
444 2
    public function getSerial()
445
    {
446 2
        return $this->serial;
447
    }
448
449
    /**
450
     * Return's the source date format to use.
451
     *
452
     * @return string The source date format
453
     */
454
    public function getSourceDateFormat()
455
    {
456
        return $this->getConfiguration()->getSourceDateFormat();
0 ignored issues
show
Bug introduced by
The method getSourceDateFormat() does not seem to exist on object<TechDivision\Impo...ConfigurationInterface>.

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...
457
    }
458
459
    /**
460
     * Return's the multiple field delimiter character to use, default value is comma (,).
461
     *
462
     * @return string The multiple field delimiter character
463
     */
464 1
    public function getMultipleFieldDelimiter()
465
    {
466 1
        return $this->getConfiguration()->getMultipleFieldDelimiter();
467
    }
468
469
    /**
470
     * Return's the multiple value delimiter character to use, default value is comma (|).
471
     *
472
     * @return string The multiple value delimiter character
473
     */
474 1
    public function getMultipleValueDelimiter()
475
    {
476 1
        return $this->getConfiguration()->getMultipleValueDelimiter();
477
    }
478
479
    /**
480
     * Intializes the previously loaded global data for exactly one bunch.
481
     *
482
     * @param string $serial The serial of the actual import
483
     *
484
     * @return void
485
     */
486 82
    public function setUp($serial)
487
    {
488
489
        // load the status of the actual import
490 82
        $status = $this->getRegistryProcessor()->getAttribute(RegistryKeys::STATUS);
491
492
        // load the global data, if prepared initially
493 82
        if (isset($status[RegistryKeys::GLOBAL_DATA])) {
494 82
            $this->stores = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::STORES];
495 82
            $this->defaultStore = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::DEFAULT_STORE];
496 82
            $this->storeWebsites  = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::STORE_WEBSITES];
497 82
            $this->rootCategories = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::ROOT_CATEGORIES];
498 82
            $this->coreConfigData = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::CORE_CONFIG_DATA];
499
        }
500
501
        // merge the callback mappings with the mappings from the child instance
502 82
        $this->callbackMappings = array_merge($this->callbackMappings, $this->getDefaultCallbackMappings());
503
504
        // merge the header mappings with the values found in the configuration
505 82
        $this->headerMappings = array_merge($this->headerMappings, $this->getConfiguration()->getHeaderMappings());
506
507
        // load the available callbacks from the configuration
508 82
        $availableCallbacks = $this->getConfiguration()->getCallbacks();
509
510
        // merge the callback mappings the the one from the configuration file
511 82
        foreach ($availableCallbacks as $callbackMappings) {
512 82
            foreach ($callbackMappings as $attributeCode => $mappings) {
513
                // write a log message, that default callback configuration will
514
                // be overwritten with the one from the configuration file
515 82
                if (isset($this->callbackMappings[$attributeCode])) {
516 82
                    $this->getSystemLogger()->notice(
517 82
                        sprintf('Now override callback mappings for attribute %s with values found in configuration file', $attributeCode)
518
                    );
519
                }
520
521
                // override the attributes callbacks
522 82
                $this->callbackMappings[$attributeCode] = $mappings;
523
            }
524
        }
525 82
    }
526
527
    /**
528
     * Clean up the global data after importing the variants.
529
     *
530
     * @param string $serial The serial of the actual import
531
     *
532
     * @return void
533
     */
534 1
    public function tearDown($serial)
535
    {
536
537
        // load the registry processor
538 1
        $registryProcessor = $this->getRegistryProcessor();
539
540
        // update the source directory for the next subject
541 1
        $registryProcessor->mergeAttributesRecursive(
542 1
            RegistryKeys::STATUS,
543
            array(
544 1
                RegistryKeys::FILES => array($this->getFilename() => array(RegistryKeys::STATUS => 1))
545
            )
546
        );
547
548
        // log a debug message with the new source directory
549
        $this->getSystemLogger()->debug(
550
            sprintf('Subject %s successfully updated status data for import %s', get_class($this), $serial)
551
        );
552
    }
553
554
    /**
555
     * Return's the target directory for the artefact export.
556
     *
557
     * @return string The target directory for the artefact export
558
     */
559 1 View Code Duplication
    public function getTargetDir()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
560
    {
561
562
        // load the status from the registry processor
563 1
        $status = $this->getRegistryProcessor()->getAttribute(RegistryKeys::STATUS);
564
565
        // query whether or not a target directory (mandatory) has been configured
566 1
        if (isset($status[RegistryKeys::TARGET_DIRECTORY])) {
567
            return $status[RegistryKeys::TARGET_DIRECTORY];
568
        }
569
570
        // throw an exception if the root category is NOT available
571 1
        throw new \Exception(sprintf('Can\'t find a target directory in status data for import %s', $this->getSerial()));
572
    }
573
574
    /**
575
     * Register the passed observer with the specific type.
576
     *
577
     * @param \TechDivision\Import\Observers\ObserverInterface $observer The observer to register
578
     * @param string                                           $type     The type to register the observer with
579
     *
580
     * @return void
581
     */
582 6
    public function registerObserver(ObserverInterface $observer, $type)
583
    {
584
585
        // query whether or not the array with the callbacks for the
586
        // passed type has already been initialized, or not
587 6
        if (!isset($this->observers[$type])) {
588 6
            $this->observers[$type] = array();
589
        }
590
591
        // append the callback with the instance of the passed type
592 6
        $this->observers[$type][] = $observer;
593 6
    }
594
595
    /**
596
     * Register the passed callback with the specific type.
597
     *
598
     * @param \TechDivision\Import\Callbacks\CallbackInterface $callback The subject to register the callbacks for
599
     * @param string                                           $type     The type to register the callback with
600
     *
601
     * @return void
602
     */
603 2
    public function registerCallback(CallbackInterface $callback, $type)
604
    {
605
606
        // query whether or not the array with the callbacks for the
607
        // passed type has already been initialized, or not
608 2
        if (!isset($this->callbacks[$type])) {
609 2
            $this->callbacks[$type] = array();
610
        }
611
612
        // append the callback with the instance of the passed type
613 2
        $this->callbacks[$type][] = $callback;
614 2
    }
615
616
    /**
617
     * Return's the array with callbacks for the passed type.
618
     *
619
     * @param string $type The type of the callbacks to return
620
     *
621
     * @return array The callbacks
622
     */
623 1
    public function getCallbacksByType($type)
624
    {
625
626
        // initialize the array for the callbacks
627 1
        $callbacks = array();
628
629
        // query whether or not callbacks for the type are available
630 1
        if (isset($this->callbacks[$type])) {
631 1
            $callbacks = $this->callbacks[$type];
632
        }
633
634
        // return the array with the type's callbacks
635 1
        return $callbacks;
636
    }
637
638
    /**
639
     * Return's the array with the available observers.
640
     *
641
     * @return array The observers
642
     */
643 6
    public function getObservers()
644
    {
645 6
        return $this->observers;
646
    }
647
648
    /**
649
     * Return's the array with the available callbacks.
650
     *
651
     * @return array The callbacks
652
     */
653 1
    public function getCallbacks()
654
    {
655 1
        return $this->callbacks;
656
    }
657
658
    /**
659
     * Return's the callback mappings for this subject.
660
     *
661
     * @return array The array with the subject's callback mappings
662
     */
663 2
    public function getCallbackMappings()
664
    {
665 2
        return $this->callbackMappings;
666
    }
667
668
    /**
669
     * Imports the content of the file with the passed filename.
670
     *
671
     *
672
     * @param string $serial   The serial of the actual import
673
     * @param string $filename The filename to process
674
     *
675
     * @return void
676
     * @throws \Exception Is thrown, if the import can't be processed
677
     */
678 2
    public function import($serial, $filename)
679
    {
680
681
        try {
682
            // initialize the serial/filename
683 2
            $this->setSerial($serial);
684 2
            $this->setFilename($filename);
685
686
            // invoke the events that has to be fired before the artfact will be processed
687 2
            $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...
688
689
            // load the system logger instance
690 2
            $systemLogger = $this->getSystemLogger();
691
692
            // prepare the flag filenames
693 2
            $inProgressFilename = sprintf('%s.inProgress', $filename);
694 2
            $importedFilename = sprintf('%s.imported', $filename);
695 2
            $failedFilename = sprintf('%s.failed', $filename);
696
697
            // query whether or not the file has already been imported
698 2
            if ($this->isFile($failedFilename) ||
699 1
                $this->isFile($importedFilename) ||
700 2
                $this->isFile($inProgressFilename)
701
            ) {
702
                // log a debug message and exit
703 1
                $systemLogger->debug(sprintf('Import running, found inProgress file %s', $inProgressFilename));
704 1
                return;
705
            }
706
707
            // flag file as in progress
708 1
            $this->touch($inProgressFilename);
709
710
            // track the start time
711 1
            $startTime = microtime(true);
712
713
            // initialize the last time we've logged the counter with the processed rows per minute
714 1
            $this->lastLog = time();
715
716
            // log a message that the file has to be imported
717 1
            $systemLogger->info(sprintf('Now start processing file %s', $filename));
718
719
            // let the adapter process the file
720 1
            $this->getImportAdapter()->import(array($this, 'importRow'), $filename);
721
722
            // track the time needed for the import in seconds
723 1
            $endTime = microtime(true) - $startTime;
724
725
            // log a message that the file has successfully been imported
726 1
            $systemLogger->info(sprintf('Successfully processed file %s with %d lines in %f s', $filename, $this->lineNumber, $endTime));
727
728
            // rename flag file, because import has been successfull
729 1
            if ($this->getConfiguration()->isCreatingImportedFile()) {
730 1
                $this->rename($inProgressFilename, $importedFilename);
731
            } else {
732
                $this->getFilesystemAdapter()->delete($inProgressFilename);
733
            }
734
735
            // invoke the events that has to be fired when the artfact has been successfully processed
736 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...
737
        } catch (\Exception $e) {
738
            // rename the flag file, because import failed and write the stack trace
739
            $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...
740
            $this->write($failedFilename, $e->__toString());
741
742
            // invoke the events that has to be fired when the artfact can't be processed
743
            $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...
744
745
            // do not wrap the exception if not already done
746
            if ($e instanceof WrappedColumnException) {
747
                throw $e;
748
            }
749
750
            // else wrap and throw the exception
751
            throw $this->wrapException(array(), $e);
752
        }
753 1
    }
754
755
    /**
756
     * Imports the passed row into the database. If the import failed, the exception
757
     * will be catched and logged, but the import process will be continued.
758
     *
759
     * @param array $row The row with the data to be imported
760
     *
761
     * @return void
762
     */
763 7
    public function importRow(array $row)
764
    {
765
766
        // initialize the row
767 7
        $this->row = $row;
768
769
        // raise the line number and reset the skip row flag
770 7
        $this->lineNumber++;
771 7
        $this->skipRow = false;
772
773
        // invoke the events that has to be fired before the artfact's row will be processed
774 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...
775
776
        // initialize the headers with the columns from the first line
777 7
        if (sizeof($this->headers) === 0) {
778 1
            foreach ($this->row as $value => $key) {
779 1
                $this->headers[$this->mapAttributeCodeByHeaderMapping($key)] = $value;
780
            }
781 1
            return;
782
        }
783
784
        // load the available observers
785 6
        $availableObservers = $this->getObservers();
786
787
        // process the observers
788 6
        foreach ($availableObservers as $observers) {
789
            // invoke the pre-import/import and post-import observers
790
            /** @var \TechDivision\Import\Observers\ObserverInterface $observer */
791 6
            foreach ($observers as $observer) {
792
                // query whether or not we have to skip the row
793 6
                if ($this->skipRow) {
794
                    // log a debug message with the actual line nr/file information
795 1
                    $this->getSystemLogger()->warning(
796 1
                        $this->appendExceptionSuffix(
797 1
                            sprintf(
798 1
                                'Skip processing operation "%s" after observer "%s"',
799 1
                                implode(' > ', $this->getConfiguration()->getConfiguration()->getOperationNames()),
800
                                get_class($observer)
801
                            )
802
                        )
803
                    );
804
805
                    // skip the row
806
                    break 2;
807
                }
808
809
                // if not, set the subject and process the observer
810 6
                if ($observer instanceof ObserverInterface) {
811 6
                    $this->row = $observer->handle($this);
812
                }
813
            }
814
        }
815
816
        // query whether or not a minute has been passed
817 5
        if ($this->lastLog < time() - 60) {
818
            // log the number processed rows per minute
819 5
            $this->getSystemLogger()->info(
820 5
                sprintf(
821 5
                    'Successfully processed "%d (%d)" rows per minute of file "%s"',
822 5
                    $this->lineNumber - $this->lastLineNumber,
823 5
                    $this->lineNumber,
824 5
                    $this->getFilename()
825
                )
826
            );
827
828
            // reset the last log time and the line number
829 5
            $this->lastLog = time();
830 5
            $this->lastLineNumber = $this->lineNumber;
831
        }
832
833
        // log a debug message with the actual line nr/file information
834 5
        $this->getSystemLogger()->debug(
835 5
            $this->appendExceptionSuffix(
836 5
                sprintf(
837 5
                    'Successfully processed operation "%s"',
838 5
                    implode(' > ', $this->getConfiguration()->getConfiguration()->getOperationNames())
839
                )
840
            )
841
        );
842
843
        // invoke the events that has to be fired when the artfact's row has been successfully processed
844
        $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...
845
    }
846
847
    /**
848
     * Queries whether or not that the subject needs an OK file to be processed.
849
     *
850
     * @return boolean TRUE if the subject needs an OK file, else FALSE
851
     */
852 1
    public function isOkFileNeeded()
853
    {
854 1
        return $this->getConfiguration()->isOkFileNeeded();
855
    }
856
857
    /**
858
     * Return's the default store.
859
     *
860
     * @return array The default store
861
     */
862
    public function getDefaultStore()
863
    {
864
        return $this->defaultStore;
865
    }
866
867
    /**
868
     * Return's the default store view code.
869
     *
870
     * @return array The default store view code
871
     */
872 4
    public function getDefaultStoreViewCode()
873
    {
874 4
        return $this->defaultStore[MemberNames::CODE];
875
    }
876
877
    /**
878
     * Set's the store view code the create the product/attributes for.
879
     *
880
     * @param string $storeViewCode The store view code
881
     *
882
     * @return void
883
     */
884 3
    public function setStoreViewCode($storeViewCode)
885
    {
886 3
        $this->storeViewCode = $storeViewCode;
887 3
    }
888
889
    /**
890
     * Return's the store view code the create the product/attributes for.
891
     *
892
     * @param string|null $default The default value to return, if the store view code has not been set
893
     *
894
     * @return string The store view code
895
     */
896 6
    public function getStoreViewCode($default = null)
897
    {
898
899
        // return the store view code, if available
900 6
        if ($this->storeViewCode !== null) {
901 2
            return $this->storeViewCode;
902
        }
903
904
        // if NOT and a default code is available
905 4
        if ($default !== null) {
906
            // return the default value
907 3
            return $default;
908
        }
909
910
        // return the default store view code
911 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...
912
    }
913
914
    /**
915
     * Prepare's the store view code in the subject. If the store_view_code row doesn't contain
916
     * any value, the default code of the default store view will be set.
917
     *
918
     * @return void
919
     */
920 1
    public function prepareStoreViewCode()
921
    {
922
923
        // re-set the store view code
924 1
        $this->setStoreViewCode(null);
925
926
        // initialize the store view code
927 1
        if ($storeViewCode = $this->getValue(ColumnKeys::STORE_VIEW_CODE)) {
928 1
            $this->setStoreViewCode($storeViewCode);
929
        }
930 1
    }
931
932
    /**
933
     * Return's the store ID of the store with the passed store view code
934
     *
935
     * @param string $storeViewCode The store view code to return the store ID for
936
     *
937
     * @return integer The ID of the store with the passed ID
938
     * @throws \Exception Is thrown, if the store with the actual code is not available
939
     */
940 2
    public function getStoreId($storeViewCode)
941
    {
942
943
        // query whether or not, the requested store is available
944 2
        if (isset($this->stores[$storeViewCode])) {
945 2
            return (integer) $this->stores[$storeViewCode][MemberNames::STORE_ID];
946
        }
947
948
        // throw an exception, if not
949
        throw new \Exception(
950
            sprintf(
951
                'Found invalid store view code %s in file %s on line %d',
952
                $storeViewCode,
953
                $this->getFilename(),
954
                $this->getLineNumber()
955
            )
956
        );
957
    }
958
959
    /**
960
     * Return's the store ID of the actual row, or of the default store
961
     * if no store view code is set in the CSV file.
962
     *
963
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
964
     *
965
     * @return integer The ID of the actual store
966
     * @throws \Exception Is thrown, if the store with the actual code is not available
967
     */
968 1
    public function getRowStoreId($default = null)
969
    {
970
971
        // initialize the default store view code, if not passed
972 1
        if ($default === null) {
973 1
            $default = $this->getDefaultStoreViewCode();
974
        }
975
976
        // load the store view code the create the product/attributes for
977 1
        return $this->getStoreId($this->getStoreViewCode($default));
0 ignored issues
show
Bug introduced by
It seems like $default defined by $this->getDefaultStoreViewCode() on line 973 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...
978
    }
979
980
    /**
981
     * Return's the root category for the actual view store.
982
     *
983
     * @return array The store's root category
984
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
985
     */
986 2
    public function getRootCategory()
987
    {
988
989
        // load the actual store view code
990 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...
991
992
        // query weather or not we've a root category or not
993 2
        if (isset($this->rootCategories[$storeViewCode])) {
994 1
            return $this->rootCategories[$storeViewCode];
995
        }
996
997
        // throw an exception if the root category is NOT available
998 1
        throw new \Exception(sprintf('Root category for %s is not available', $storeViewCode));
999
    }
1000
1001
    /**
1002
     * Return's the Magento configuration value.
1003
     *
1004
     * @param string  $path    The Magento path of the requested configuration value
1005
     * @param mixed   $default The default value that has to be returned, if the requested configuration value is not set
1006
     * @param string  $scope   The scope the configuration value has been set
1007
     * @param integer $scopeId The scope ID the configuration value has been set
1008
     *
1009
     * @return mixed The configuration value
1010
     * @throws \Exception Is thrown, if nor a value can be found or a default value has been passed
1011
     */
1012 5
    public function getCoreConfigData($path, $default = null, $scope = ScopeKeys::SCOPE_DEFAULT, $scopeId = 0)
1013
    {
1014
1015
        // initialize the core config data
1016
        $coreConfigData = array(
1017 5
            MemberNames::PATH => $path,
1018 5
            MemberNames::SCOPE => $scope,
1019 5
            MemberNames::SCOPE_ID => $scopeId
1020
        );
1021
1022
        // generate the UID from the passed data
1023 5
        $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1024
1025
        // iterate over the core config data and try to find the requested configuration value
1026 5
        if (isset($this->coreConfigData[$uniqueIdentifier])) {
1027 1
            return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1028
        }
1029
1030
        // query whether or not we've to query for the configuration value on fallback level 'websites' also
1031 4
        if ($scope === ScopeKeys::SCOPE_STORES) {
1032
            // query whether or not the website with the passed ID is available
1033 2
            foreach ($this->storeWebsites as $storeWebsite) {
1034 2
                if ($storeWebsite[MemberNames::WEBSITE_ID] === $scopeId) {
1035
                    // replace scope with 'websites' and website ID
1036 2
                    $coreConfigData = array_merge(
1037 2
                        $coreConfigData,
1038
                        array(
1039 2
                            MemberNames::SCOPE    => ScopeKeys::SCOPE_WEBSITES,
1040
                            MemberNames::SCOPE_ID => $storeWebsite[MemberNames::WEBSITE_ID]
1041
                        )
1042
                    );
1043
1044
                    // generate the UID from the passed data, merged with the 'websites' scope and ID
1045 2
                    $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1046
1047
                    // query whether or not, the configuration value on 'websites' level
1048 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...
1049 2
                        return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1050
                    }
1051
                }
1052
            }
1053
        }
1054
1055
        // replace scope with 'default' and scope ID '0'
1056 3
        $coreConfigData = array_merge(
1057 3
            $coreConfigData,
1058
            array(
1059 3
                MemberNames::SCOPE    => ScopeKeys::SCOPE_DEFAULT,
1060
                MemberNames::SCOPE_ID => 0
1061
            )
1062
        );
1063
1064
        // generate the UID from the passed data, merged with the 'default' scope and ID 0
1065 3
        $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1066
1067
        // query whether or not, the configuration value on 'default' level
1068 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...
1069 1
            return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1070
        }
1071
1072
        // if not, return the passed default value
1073 2
        if ($default !== null) {
1074 1
            return $default;
1075
        }
1076
1077
        // throw an exception if no value can be found
1078
        // in the Magento configuration
1079 1
        throw new \Exception(
1080 1
            sprintf(
1081 1
                'Can\'t find a value for configuration "%s-%s-%d" in "core_config_data"',
1082 1
                $path,
1083 1
                $scope,
1084 1
                $scopeId
1085
            )
1086
        );
1087
    }
1088
1089
    /**
1090
     * Resolve the original column name for the passed one.
1091
     *
1092
     * @param string $columnName The column name that has to be resolved
1093
     *
1094
     * @return string|null The original column name
1095
     */
1096 1
    public function resolveOriginalColumnName($columnName)
1097
    {
1098
1099
        // try to load the original data
1100 1
        $originalData = $this->getOriginalData();
1101
1102
        // query whether or not original data is available
1103 1
        if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES])) {
1104
            // query whether or not the original column name is available
1105
            if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES][$columnName])) {
1106
                return $originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES][$columnName];
1107
            }
1108
1109
            // query whether or a wildcard column name is available
1110
            if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES]['*'])) {
1111
                return $originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES]['*'];
1112
            }
1113
        }
1114
1115
        // return the original column name
1116 1
        return $columnName;
1117
    }
1118
1119
    /**
1120
     * Return's the original data if available, or an empty array.
1121
     *
1122
     * @return array The original data
1123
     */
1124 1
    public function getOriginalData()
1125
    {
1126
1127
        // initialize the array for the original data
1128 1
        $originalData = array();
1129
1130
        // query whether or not the column contains original data
1131 1
        if ($this->hasOriginalData()) {
1132
            // unerialize the original data from the column
1133
            $originalData = unserialize($this->row[$this->headers[ColumnKeys::ORIGINAL_DATA]]);
1134
        }
1135
1136
        // return an empty array, if not
1137 1
        return $originalData;
1138
    }
1139
1140
    /**
1141
     * Query's whether or not the actual column contains original data like
1142
     * filename, line number and column names.
1143
     *
1144
     * @return boolean TRUE if the actual column contains origin data, else FALSE
1145
     */
1146 1
    public function hasOriginalData()
1147
    {
1148 1
        return isset($this->headers[ColumnKeys::ORIGINAL_DATA]) && isset($this->row[$this->headers[ColumnKeys::ORIGINAL_DATA]]);
1149
    }
1150
1151
    /**
1152
     * Wraps the passed exeception into a new one by trying to resolve the original filname,
1153
     * line number and column names and use it for a detailed exception message.
1154
     *
1155
     * @param array      $columnNames The column names that should be resolved and wrapped
1156
     * @param \Exception $parent      The exception we want to wrap
1157
     * @param string     $className   The class name of the exception type we want to wrap the parent one
1158
     *
1159
     * @return \Exception the wrapped exception
1160
     */
1161
    public function wrapException(
1162
        array $columnNames = array(),
1163
        \Exception $parent = null,
1164
        $className = '\TechDivision\Import\Exceptions\WrappedColumnException'
1165
    ) {
1166
1167
        // initialize the message
1168
        $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...
1169
1170
        // query whether or not has been a result of invalid data of a previous column of a CSV file
1171
        if ($this->hasOriginalData()) {
1172
            // load the original data
1173
            $originalData = $this->getOriginalData();
1174
1175
            // replace old filename and line number of the original message
1176
            $message = $this->appendExceptionSuffix(
1177
                $this->stripExceptionSuffix($message),
1178
                $originalData[ColumnKeys::ORIGINAL_FILENAME],
1179
                $originalData[ColumnKeys::ORIGINAL_LINE_NUMBER]
1180
            );
1181
        } else {
1182
            // append filename and line number to the original message
1183
            $message = $this->appendExceptionSuffix(
1184
                $this->stripExceptionSuffix($message),
1185
                $this->filename,
1186
                $this->lineNumber
1187
            );
1188
        }
1189
1190
        // query whether or not, column names has been passed
1191
        if (sizeof($columnNames) > 0) {
1192
            // prepare the original column names
1193
            $originalColumnNames = array();
1194
            foreach ($columnNames as $columnName) {
1195
                $originalColumnNames[] = $this->resolveOriginalColumnName($columnName);
1196
            }
1197
1198
            // append the column information
1199
            $message = sprintf('%s in column(s) %s', $message, implode(', ', $originalColumnNames));
1200
        }
1201
1202
        // create a new exception and wrap the parent one
1203
        return new $className($message, null, $parent);
1204
    }
1205
1206
    /**
1207
     * Strip's the exception suffix containing filename and line number from the
1208
     * passed message.
1209
     *
1210
     * @param string $message The message to strip the exception suffix from
1211
     *
1212
     * @return mixed The message without the exception suffix
1213
     */
1214
    public function stripExceptionSuffix($message)
1215
    {
1216
        return str_replace($this->appendExceptionSuffix(), '', $message);
1217
    }
1218
1219
    /**
1220
     * Append's the exception suffix containing filename and line number to the
1221
     * passed message. If no message has been passed, only the suffix will be
1222
     * returned
1223
     *
1224
     * @param string|null $message    The message to append the exception suffix to
1225
     * @param string|null $filename   The filename used to create the suffix
1226
     * @param string|null $lineNumber The line number used to create the suffx
1227
     *
1228
     * @return string The message with the appended exception suffix
1229
     */
1230 8
    public function appendExceptionSuffix($message = null, $filename = null, $lineNumber = null)
1231
    {
1232
1233
        // query whether or not a filename has been passed
1234 8
        if ($filename === null) {
1235 8
            $filename = $this->getFilename();
1236
        }
1237
1238
        // query whether or not a line number has been passed
1239 8
        if ($lineNumber === null) {
1240 8
            $lineNumber = $this->getLineNumber();
1241
        }
1242
1243
        // if no message has been passed, only return the suffix
1244 8
        if ($message === null) {
1245
            return sprintf(' in file %s on line %d', $filename, $lineNumber);
1246
        }
1247
1248
        // concatenate the message with the suffix and return it
1249 8
        return sprintf('%s in file %s on line %d', $message, $filename, $lineNumber);
1250
    }
1251
1252
    /**
1253
     * Raises the value for the counter with the passed key by one.
1254
     *
1255
     * @param mixed $counterName The name of the counter to raise
1256
     *
1257
     * @return integer The counter's new value
1258
     */
1259 1
    public function raiseCounter($counterName)
1260
    {
1261
1262
        // raise the counter with the passed name
1263 1
        return $this->getRegistryProcessor()->raiseCounter(
1264 1
            RegistryKeys::COUNTERS,
1265 1
            $counterName
1266
        );
1267
    }
1268
1269
    /**
1270
     * Merge the passed array into the status of the actual import.
1271
     *
1272
     * @param array $status The status information to be merged
1273
     *
1274
     * @return void
1275
     */
1276 1
    public function mergeAttributesRecursive(array $status)
1277
    {
1278
1279
        // merge the passed status
1280 1
        return $this->getRegistryProcessor()->mergeAttributesRecursive(
1281 1
            RegistryKeys::STATUS,
1282 1
            $status
1283
        );
1284
    }
1285
1286
    /**
1287
     * Return's the entity type code to be used.
1288
     *
1289
     * @return string The entity type code to be used
1290
     */
1291
    public function getEntityTypeCode()
1292
    {
1293
1294
        // load the configuration specific entity type code from the plugin configuration
1295
        $entityTypeCode = $this->getExecutionContext()->getEntityTypeCode();
1296
1297
        // try to map the entity type code
1298
        if (isset($this->entityTypeCodeMappings[$entityTypeCode])) {
1299
            $entityTypeCode = $this->entityTypeCodeMappings[$entityTypeCode];
1300
        }
1301
1302
        // return the (mapped) entity type code
1303
        return $entityTypeCode;
1304
    }
1305
}
1306