Completed
Push — 15.x ( 53b7d0...60340d )
by Tim
04:40
created

AbstractSubject::setUp()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 40
c 0
b 0
f 0
ccs 0
cts 24
cp 0
rs 8.9688
cc 5
nc 8
nop 1
crap 30
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\Generators\GeneratorInterface;
34
use TechDivision\Import\Callbacks\CallbackInterface;
35
use TechDivision\Import\Observers\ObserverInterface;
36
use TechDivision\Import\Adapter\ImportAdapterInterface;
37
use TechDivision\Import\Exceptions\WrappedColumnException;
38
use TechDivision\Import\Services\RegistryProcessorInterface;
39
use TechDivision\Import\Configuration\SubjectConfigurationInterface;
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, FilesystemSubjectInterface, DateConverterSubjectInterface
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 date converting functionality.
69
     *
70
     * @var \TechDivision\Import\DateConverterTrait
71
     */
72
    use DateConverterTrait;
73
74
    /**
75
     * The trait that provides header handling functionality.
76
     *
77
     * @var \TechDivision\Import\HeaderTrait
78
     */
79
    use HeaderTrait;
80
81
    /**
82
     * The trait that provides row handling functionality.
83
     *
84
     * @var \TechDivision\Import\RowTrait
85
     */
86
    use RowTrait;
87
88
    /**
89
     * The name of the file to be imported.
90
     *
91
     * @var string
92
     */
93
    protected $filename;
94
95
    /**
96
     * The actual line number.
97
     *
98
     * @var integer
99
     */
100
    protected $lineNumber = 0;
101
102
    /**
103
     * The import adapter instance.
104
     *
105
     * @var \TechDivision\Import\Adapter\ImportAdapterInterface
106
     */
107
    protected $importAdapter;
108
109
    /**
110
     * The system configuration.
111
     *
112
     * @var \TechDivision\Import\Configuration\SubjectConfigurationInterface
113
     */
114
    protected $configuration;
115
116
    /**
117
     * The RegistryProcessor instance to handle running threads.
118
     *
119
     * @var \TechDivision\Import\Services\RegistryProcessorInterface
120
     */
121
    protected $registryProcessor;
122
123
    /**
124
     * The actions unique serial.
125
     *
126
     * @var string
127
     */
128
    protected $serial;
129
130
    /**
131
     * Array with the subject's observers.
132
     *
133
     * @var array
134
     */
135
    protected $observers = array();
136
137
    /**
138
     * Array with the subject's callbacks.
139
     *
140
     * @var array
141
     */
142
    protected $callbacks = array();
143
144
    /**
145
     * The subject's callback mappings.
146
     *
147
     * @var array
148
     */
149
    protected $callbackMappings = array();
150
151
    /**
152
     * The available root categories.
153
     *
154
     * @var array
155
     */
156
    protected $rootCategories = array();
157
158
    /**
159
     * The Magento configuration.
160
     *
161
     * @var array
162
     */
163
    protected $coreConfigData = array();
164
165
    /**
166
     * The available stores.
167
     *
168
     * @var array
169
     */
170
    protected $stores = array();
171
172
    /**
173
     * The available websites.
174
     *
175
     * @var array
176
     */
177
    protected $storeWebsites = array();
178
179
    /**
180
     * The default store.
181
     *
182
     * @var array
183
     */
184
    protected $defaultStore;
185
186
    /**
187
     * The store view code the create the product/attributes for.
188
     *
189
     * @var string
190
     */
191
    protected $storeViewCode;
192
193
    /**
194
     * The UID generator for the core config data.
195
     *
196
     * @var \TechDivision\Import\Utils\Generators\GeneratorInterface
197
     */
198
    protected $coreConfigDataUidGenerator;
199
200
    /**
201
     * UNIX timestamp with the date the last row counter has been logged.
202
     *
203
     * @var integer
204
     */
205
    protected $lastLog = 0;
206
207
    /**
208
     * The number of the last line that has been logged with the row counter
209
     * @var integer
210
     */
211
    protected $lastLineNumber = 0;
212
213
    /**
214
     * The event emitter instance.
215
     *
216
     * @var \League\Event\EmitterInterface
217
     */
218
    protected $emitter;
219
220
    /**
221
     * Initialize the subject instance.
222
     *
223
     * @param \TechDivision\Import\Services\RegistryProcessorInterface $registryProcessor          The registry processor instance
224
     * @param \TechDivision\Import\Utils\Generators\GeneratorInterface $coreConfigDataUidGenerator The UID generator for the core config data
225
     * @param \Doctrine\Common\Collections\Collection                  $systemLoggers              The array with the system loggers instances
226
     * @param \League\Event\EmitterInterface                           $emitter                    The event emitter instance
227
     */
228
    public function __construct(
229
        RegistryProcessorInterface $registryProcessor,
230
        GeneratorInterface $coreConfigDataUidGenerator,
231
        Collection $systemLoggers,
232
        EmitterInterface $emitter
233
    ) {
234
        $this->emitter = $emitter;
235
        $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...
236
        $this->registryProcessor = $registryProcessor;
237
        $this->coreConfigDataUidGenerator = $coreConfigDataUidGenerator;
238
    }
239
240
    /**
241
     * Return's the event emitter instance.
242
     *
243
     * @return \League\Event\EmitterInterface The event emitter instance
244
     */
245
    public function getEmitter()
246
    {
247
        return $this->emitter;
248
    }
249
250
    /**
251
     * Set's the name of the file to import
252
     *
253
     * @param string $filename The filename
254
     *
255
     * @return void
256
     */
257
    public function setFilename($filename)
258
    {
259
        $this->filename = $filename;
260
    }
261
262
    /**
263
     * Return's the name of the file to import.
264
     *
265
     * @return string The filename
266
     */
267
    public function getFilename()
268
    {
269
        return $this->filename;
270
    }
271
272
    /**
273
     * Set's the actual line number.
274
     *
275
     * @param integer $lineNumber The line number
276
     *
277
     * @return void
278
     */
279
    public function setLineNumber($lineNumber)
280
    {
281
        $this->lineNumber = $lineNumber;
282
    }
283
284
    /**
285
     * Return's the actual line number.
286
     *
287
     * @return integer The line number
288
     */
289
    public function getLineNumber()
290
    {
291
        return $this->lineNumber;
292
    }
293
294
    /**
295
     * Return's the default callback mappings.
296
     *
297
     * @return array The default callback mappings
298
     */
299
    public function getDefaultCallbackMappings()
300
    {
301
        return array();
302
    }
303
304
    /**
305
     * Tries to format the passed value to a valid date with format 'Y-m-d H:i:s'.
306
     * If the passed value is NOT a valid date, NULL will be returned.
307
     *
308
     * @param string $value The value to format
309
     *
310
     * @return string|null The formatted date or NULL if the date is not valid
311
     */
312
    public function formatDate($value)
313
    {
314
        return $this->getDateConverter()->convert($value);
315
    }
316
317
    /**
318
     * Extracts the elements of the passed value by exploding them
319
     * with the also passed delimiter.
320
     *
321
     * @param string      $value     The value to extract
322
     * @param string|null $delimiter The delimiter used to extrace the elements
323
     *
324
     * @return array The exploded values
325
     */
326
    public function explode($value, $delimiter = null)
327
    {
328
        return $this->getImportAdapter()->explode($value, $delimiter);
329
    }
330
331
    /**
332
     * Queries whether or not debug mode is enabled or not, default is TRUE.
333
     *
334
     * @return boolean TRUE if debug mode is enabled, else FALSE
335
     */
336
    public function isDebugMode()
337
    {
338
        return $this->getConfiguration()->isDebugMode();
339
    }
340
341
    /**
342
     * Set's the subject configuration.
343
     *
344
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $configuration The subject configuration
345
     *
346
     * @return void
347
     */
348
    public function setConfiguration(SubjectConfigurationInterface $configuration)
349
    {
350
        $this->configuration = $configuration;
351
    }
352
353
    /**
354
     * Return's the subject configuration.
355
     *
356
     * @return \TechDivision\Import\Configuration\SubjectConfigurationInterface The subject configuration
357
     */
358
    public function getConfiguration()
359
    {
360
        return $this->configuration;
361
    }
362
363
    /**
364
     * Set's the import adapter instance.
365
     *
366
     * @param \TechDivision\Import\Adapter\ImportAdapterInterface $importAdapter The import adapter instance
367
     *
368
     * @return void
369
     */
370
    public function setImportAdapter(ImportAdapterInterface $importAdapter)
371
    {
372
        $this->importAdapter = $importAdapter;
373
    }
374
375
    /**
376
     * Return's the import adapter instance.
377
     *
378
     * @return \TechDivision\Import\Adapter\ImportAdapterInterface The import adapter instance
379
     */
380
    public function getImportAdapter()
381
    {
382
        return $this->importAdapter;
383
    }
384
385
    /**
386
     * Return's the RegistryProcessor instance to handle the running threads.
387
     *
388
     * @return \TechDivision\Import\Services\RegistryProcessorInterface The registry processor instance
389
     */
390
    public function getRegistryProcessor()
391
    {
392
        return $this->registryProcessor;
393
    }
394
395
    /**
396
     * Set's the unique serial for this import process.
397
     *
398
     * @param string $serial The unique serial
399
     *
400
     * @return void
401
     */
402
    public function setSerial($serial)
403
    {
404
        $this->serial = $serial;
405
    }
406
407
    /**
408
     * Return's the unique serial for this import process.
409
     *
410
     * @return string The unique serial
411
     */
412
    public function getSerial()
413
    {
414
        return $this->serial;
415
    }
416
417
    /**
418
     * Return's the source date format to use.
419
     *
420
     * @return string The source date format
421
     */
422
    public function getSourceDateFormat()
423
    {
424
        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...
425
    }
426
427
    /**
428
     * Return's the multiple field delimiter character to use, default value is comma (,).
429
     *
430
     * @return string The multiple field delimiter character
431
     */
432
    public function getMultipleFieldDelimiter()
433
    {
434
        return $this->getConfiguration()->getMultipleFieldDelimiter();
435
    }
436
437
    /**
438
     * Return's the multiple value delimiter character to use, default value is comma (|).
439
     *
440
     * @return string The multiple value delimiter character
441
     */
442
    public function getMultipleValueDelimiter()
443
    {
444
        return $this->getConfiguration()->getMultipleValueDelimiter();
445
    }
446
447
    /**
448
     * Intializes the previously loaded global data for exactly one bunch.
449
     *
450
     * @param string $serial The serial of the actual import
451
     *
452
     * @return void
453
     */
454
    public function setUp($serial)
455
    {
456
457
        // load the status of the actual import
458
        $status = $this->getRegistryProcessor()->getAttribute(RegistryKeys::STATUS);
459
460
        // load the global data, if prepared initially
461
        if (isset($status[RegistryKeys::GLOBAL_DATA])) {
462
            $this->stores = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::STORES];
463
            $this->defaultStore = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::DEFAULT_STORE];
464
            $this->storeWebsites  = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::STORE_WEBSITES];
465
            $this->rootCategories = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::ROOT_CATEGORIES];
466
            $this->coreConfigData = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::CORE_CONFIG_DATA];
467
        }
468
469
        // merge the callback mappings with the mappings from the child instance
470
        $this->callbackMappings = array_merge($this->callbackMappings, $this->getDefaultCallbackMappings());
471
472
        // merge the header mappings with the values found in the configuration
473
        $this->headerMappings = array_merge($this->headerMappings, $this->getConfiguration()->getHeaderMappings());
474
475
        // load the available callbacks from the configuration
476
        $availableCallbacks = $this->getConfiguration()->getCallbacks();
477
478
        // merge the callback mappings the the one from the configuration file
479
        foreach ($availableCallbacks as $callbackMappings) {
480
            foreach ($callbackMappings as $attributeCode => $mappings) {
481
                // write a log message, that default callback configuration will
482
                // be overwritten with the one from the configuration file
483
                if (isset($this->callbackMappings[$attributeCode])) {
484
                    $this->getSystemLogger()->notice(
485
                        sprintf('Now override callback mappings for attribute %s with values found in configuration file', $attributeCode)
486
                    );
487
                }
488
489
                // override the attributes callbacks
490
                $this->callbackMappings[$attributeCode] = $mappings;
491
            }
492
        }
493
    }
494
495
    /**
496
     * Clean up the global data after importing the variants.
497
     *
498
     * @param string $serial The serial of the actual import
499
     *
500
     * @return void
501
     */
502
    public function tearDown($serial)
503
    {
504
505
        // load the registry processor
506
        $registryProcessor = $this->getRegistryProcessor();
507
508
        // update the source directory for the next subject
509
        $registryProcessor->mergeAttributesRecursive(
510
            RegistryKeys::STATUS,
511
            array(
512
                RegistryKeys::SOURCE_DIRECTORY => $newSourceDir = $this->getNewSourceDir($serial),
513
                RegistryKeys::FILES => array($this->getFilename() => array(RegistryKeys::STATUS => 1))
514
            )
515
        );
516
517
        // log a debug message with the new source directory
518
        $this->getSystemLogger()->debug(
519
            sprintf('Subject %s successfully updated source directory to %s', get_class($this), $newSourceDir)
520
        );
521
    }
522
523
    /**
524
     * Return's the target directory for the artefact export.
525
     *
526
     * @return string The target directory for the artefact export
527
     */
528
    public function getTargetDir()
529
    {
530
        return $this->getNewSourceDir($this->getSerial());
531
    }
532
533
    /**
534
     * Return's the next source directory, which will be the target directory
535
     * of this subject, in most cases.
536
     *
537
     * @param string $serial The serial of the actual import
538
     *
539
     * @return string The new source directory
540
     */
541
    public function getNewSourceDir($serial)
542
    {
543
        return sprintf('%s/%s', $this->getConfiguration()->getTargetDir(), $serial);
544
    }
545
546
    /**
547
     * Register the passed observer with the specific type.
548
     *
549
     * @param \TechDivision\Import\Observers\ObserverInterface $observer The observer to register
550
     * @param string                                           $type     The type to register the observer with
551
     *
552
     * @return void
553
     */
554
    public function registerObserver(ObserverInterface $observer, $type)
555
    {
556
557
        // query whether or not the array with the callbacks for the
558
        // passed type has already been initialized, or not
559
        if (!isset($this->observers[$type])) {
560
            $this->observers[$type] = array();
561
        }
562
563
        // append the callback with the instance of the passed type
564
        $this->observers[$type][] = $observer;
565
    }
566
567
    /**
568
     * Register the passed callback with the specific type.
569
     *
570
     * @param \TechDivision\Import\Callbacks\CallbackInterface $callback The subject to register the callbacks for
571
     * @param string                                           $type     The type to register the callback with
572
     *
573
     * @return void
574
     */
575
    public function registerCallback(CallbackInterface $callback, $type)
576
    {
577
578
        // query whether or not the array with the callbacks for the
579
        // passed type has already been initialized, or not
580
        if (!isset($this->callbacks[$type])) {
581
            $this->callbacks[$type] = array();
582
        }
583
584
        // append the callback with the instance of the passed type
585
        $this->callbacks[$type][] = $callback;
586
    }
587
588
    /**
589
     * Return's the array with callbacks for the passed type.
590
     *
591
     * @param string $type The type of the callbacks to return
592
     *
593
     * @return array The callbacks
594
     */
595
    public function getCallbacksByType($type)
596
    {
597
598
        // initialize the array for the callbacks
599
        $callbacks = array();
600
601
        // query whether or not callbacks for the type are available
602
        if (isset($this->callbacks[$type])) {
603
            $callbacks = $this->callbacks[$type];
604
        }
605
606
        // return the array with the type's callbacks
607
        return $callbacks;
608
    }
609
610
    /**
611
     * Return's the array with the available observers.
612
     *
613
     * @return array The observers
614
     */
615
    public function getObservers()
616
    {
617
        return $this->observers;
618
    }
619
620
    /**
621
     * Return's the array with the available callbacks.
622
     *
623
     * @return array The callbacks
624
     */
625
    public function getCallbacks()
626
    {
627
        return $this->callbacks;
628
    }
629
630
    /**
631
     * Return's the callback mappings for this subject.
632
     *
633
     * @return array The array with the subject's callback mappings
634
     */
635
    public function getCallbackMappings()
636
    {
637
        return $this->callbackMappings;
638
    }
639
640
    /**
641
     * Imports the content of the file with the passed filename.
642
     *
643
     *
644
     * @param string $serial   The serial of the actual import
645
     * @param string $filename The filename to process
646
     *
647
     * @return void
648
     * @throws \Exception Is thrown, if the import can't be processed
649
     */
650
    public function import($serial, $filename)
651
    {
652
653
        try {
654
            // initialize the serial/filename
655
            $this->setSerial($serial);
656
            $this->setFilename($filename);
657
658
            // invoke the events that has to be fired before the artfact will be processed
659
            $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...
660
661
            // load the system logger instance
662
            $systemLogger = $this->getSystemLogger();
663
664
            // prepare the flag filenames
665
            $inProgressFilename = sprintf('%s.inProgress', $filename);
666
            $importedFilename = sprintf('%s.imported', $filename);
667
            $failedFilename = sprintf('%s.failed', $filename);
668
669
            // query whether or not the file has already been imported
670
            if ($this->isFile($failedFilename) ||
671
                $this->isFile($importedFilename) ||
672
                $this->isFile($inProgressFilename)
673
            ) {
674
                // log a debug message and exit
675
                $systemLogger->debug(sprintf('Import running, found inProgress file %s', $inProgressFilename));
676
                return;
677
            }
678
679
            // flag file as in progress
680
            $this->touch($inProgressFilename);
681
682
            // track the start time
683
            $startTime = microtime(true);
684
685
            // initialize the last time we've logged the counter with the processed rows per minute
686
            $this->lastLog = time();
687
688
            // log a message that the file has to be imported
689
            $systemLogger->info(sprintf('Now start processing file %s', $filename));
690
691
            // let the adapter process the file
692
            $this->getImportAdapter()->import(array($this, 'importRow'), $filename);
693
694
            // track the time needed for the import in seconds
695
            $endTime = microtime(true) - $startTime;
696
697
            // log a message that the file has successfully been imported
698
            $systemLogger->info(sprintf('Successfully processed file %s with %d lines in %f s', $filename, $this->lineNumber, $endTime));
699
700
            // rename flag file, because import has been successfull
701
            if ($this->getConfiguration()->isCreatingImportedFile()) {
702
                $this->rename($inProgressFilename, $importedFilename);
703
            } else {
704
                $this->getFilesystemAdapter()->delete($inProgressFilename);
705
            }
706
707
            // invoke the events that has to be fired when the artfact has been successfully processed
708
            $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...
709
        } catch (\Exception $e) {
710
            // rename the flag file, because import failed and write the stack trace
711
            $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...
712
            $this->write($failedFilename, $e->__toString());
713
714
            // invoke the events that has to be fired when the artfact can't be processed
715
            $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...
716
717
            // do not wrap the exception if not already done
718
            if ($e instanceof WrappedColumnException) {
719
                throw $e;
720
            }
721
722
            // else wrap and throw the exception
723
            throw $this->wrapException(array(), $e);
724
        }
725
    }
726
727
    /**
728
     * Imports the passed row into the database. If the import failed, the exception
729
     * will be catched and logged, but the import process will be continued.
730
     *
731
     * @param array $row The row with the data to be imported
732
     *
733
     * @return void
734
     */
735
    public function importRow(array $row)
736
    {
737
738
        // initialize the row
739
        $this->row = $row;
740
741
        // raise the line number and reset the skip row flag
742
        $this->lineNumber++;
743
        $this->skipRow = false;
744
745
        // invoke the events that has to be fired before the artfact's row will be processed
746
        $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...
747
748
        // initialize the headers with the columns from the first line
749
        if (sizeof($this->headers) === 0) {
750
            foreach ($this->row as $value => $key) {
751
                $this->headers[$this->mapAttributeCodeByHeaderMapping($key)] = $value;
752
            }
753
            return;
754
        }
755
756
        // load the available observers
757
        $availableObservers = $this->getObservers();
758
759
        // process the observers
760
        foreach ($availableObservers as $observers) {
761
            // invoke the pre-import/import and post-import observers
762
            /** @var \TechDivision\Import\Observers\ObserverInterface $observer */
763
            foreach ($observers as $observer) {
764
                // query whether or not we have to skip the row
765
                if ($this->skipRow) {
766
                    // log a debug message with the actual line nr/file information
767
                    $this->getSystemLogger()->warning(
768
                        $this->appendExceptionSuffix(
769
                            sprintf(
770
                                'Skip processing operation "%s" after observer "%s"',
771
                                implode(' > ', $this->getConfiguration()->getConfiguration()->getOperationNames()),
772
                                get_class($observer)
773
                            )
774
                        )
775
                    );
776
777
                    // skip the row
778
                    break 2;
779
                }
780
781
                // if not, set the subject and process the observer
782
                if ($observer instanceof ObserverInterface) {
783
                    $this->row = $observer->handle($this);
784
                }
785
            }
786
        }
787
788
        // query whether or not a minute has been passed
789
        if ($this->lastLog < time() - 60) {
790
            // log the number processed rows per minute
791
            $this->getSystemLogger()->info(
792
                sprintf(
793
                    'Successfully processed "%d (%d)" rows per minute of file "%s"',
794
                    $this->lineNumber - $this->lastLineNumber,
795
                    $this->lineNumber,
796
                    $this->getFilename()
797
                )
798
            );
799
800
            // reset the last log time and the line number
801
            $this->lastLog = time();
802
            $this->lastLineNumber = $this->lineNumber;
803
        }
804
805
        // log a debug message with the actual line nr/file information
806
        $this->getSystemLogger()->debug(
807
            $this->appendExceptionSuffix(
808
                sprintf(
809
                    'Successfully processed operation "%s"',
810
                    implode(' > ', $this->getConfiguration()->getConfiguration()->getOperationNames())
811
                )
812
            )
813
        );
814
815
        // invoke the events that has to be fired when the artfact's row has been successfully processed
816
        $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...
817
    }
818
819
    /**
820
     * Queries whether or not that the subject needs an OK file to be processed.
821
     *
822
     * @return boolean TRUE if the subject needs an OK file, else FALSE
823
     */
824
    public function isOkFileNeeded()
825
    {
826
        return $this->getConfiguration()->isOkFileNeeded();
827
    }
828
829
    /**
830
     * Return's the default store.
831
     *
832
     * @return array The default store
833
     */
834
    public function getDefaultStore()
835
    {
836
        return $this->defaultStore;
837
    }
838
839
    /**
840
     * Return's the default store view code.
841
     *
842
     * @return array The default store view code
843
     */
844
    public function getDefaultStoreViewCode()
845
    {
846
        return $this->defaultStore[MemberNames::CODE];
847
    }
848
849
    /**
850
     * Set's the store view code the create the product/attributes for.
851
     *
852
     * @param string $storeViewCode The store view code
853
     *
854
     * @return void
855
     */
856
    public function setStoreViewCode($storeViewCode)
857
    {
858
        $this->storeViewCode = $storeViewCode;
859
    }
860
861
    /**
862
     * Return's the store view code the create the product/attributes for.
863
     *
864
     * @param string|null $default The default value to return, if the store view code has not been set
865
     *
866
     * @return string The store view code
867
     */
868
    public function getStoreViewCode($default = null)
869
    {
870
871
        // return the store view code, if available
872
        if ($this->storeViewCode !== null) {
873
            return $this->storeViewCode;
874
        }
875
876
        // if NOT and a default code is available
877
        if ($default !== null) {
878
            // return the default value
879
            return $default;
880
        }
881
882
        // return the default store view code
883
        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...
884
    }
885
886
    /**
887
     * Prepare's the store view code in the subject. If the store_view_code row doesn't contain
888
     * any value, the default code of the default store view will be set.
889
     *
890
     * @return void
891
     */
892
    public function prepareStoreViewCode()
893
    {
894
895
        // re-set the store view code
896
        $this->setStoreViewCode(null);
897
898
        // initialize the store view code
899
        if ($storeViewCode = $this->getValue(ColumnKeys::STORE_VIEW_CODE)) {
900
            $this->setStoreViewCode($storeViewCode);
901
        }
902
    }
903
904
    /**
905
     * Return's the store ID of the store with the passed store view code
906
     *
907
     * @param string $storeViewCode The store view code to return the store ID for
908
     *
909
     * @return integer The ID of the store with the passed ID
910
     * @throws \Exception Is thrown, if the store with the actual code is not available
911
     */
912
    public function getStoreId($storeViewCode)
913
    {
914
915
        // query whether or not, the requested store is available
916
        if (isset($this->stores[$storeViewCode])) {
917
            return (integer) $this->stores[$storeViewCode][MemberNames::STORE_ID];
918
        }
919
920
        // throw an exception, if not
921
        throw new \Exception(
922
            sprintf(
923
                'Found invalid store view code %s in file %s on line %d',
924
                $storeViewCode,
925
                $this->getFilename(),
926
                $this->getLineNumber()
927
            )
928
        );
929
    }
930
931
    /**
932
     * Return's the store ID of the actual row, or of the default store
933
     * if no store view code is set in the CSV file.
934
     *
935
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
936
     *
937
     * @return integer The ID of the actual store
938
     * @throws \Exception Is thrown, if the store with the actual code is not available
939
     */
940
    public function getRowStoreId($default = null)
941
    {
942
943
        // initialize the default store view code, if not passed
944
        if ($default === null) {
945
            $default = $this->getDefaultStoreViewCode();
946
        }
947
948
        // load the store view code the create the product/attributes for
949
        return $this->getStoreId($this->getStoreViewCode($default));
0 ignored issues
show
Bug introduced by
It seems like $default defined by $this->getDefaultStoreViewCode() on line 945 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...
950
    }
951
952
    /**
953
     * Return's the root category for the actual view store.
954
     *
955
     * @return array The store's root category
956
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
957
     */
958
    public function getRootCategory()
959
    {
960
961
        // load the actual store view code
962
        $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...
963
964
        // query weather or not we've a root category or not
965
        if (isset($this->rootCategories[$storeViewCode])) {
966
            return $this->rootCategories[$storeViewCode];
967
        }
968
969
        // throw an exception if the root category is NOT available
970
        throw new \Exception(sprintf('Root category for %s is not available', $storeViewCode));
971
    }
972
973
    /**
974
     * Return's the Magento configuration value.
975
     *
976
     * @param string  $path    The Magento path of the requested configuration value
977
     * @param mixed   $default The default value that has to be returned, if the requested configuration value is not set
978
     * @param string  $scope   The scope the configuration value has been set
979
     * @param integer $scopeId The scope ID the configuration value has been set
980
     *
981
     * @return mixed The configuration value
982
     * @throws \Exception Is thrown, if nor a value can be found or a default value has been passed
983
     */
984
    public function getCoreConfigData($path, $default = null, $scope = ScopeKeys::SCOPE_DEFAULT, $scopeId = 0)
985
    {
986
987
        // initialize the core config data
988
        $coreConfigData = array(
989
            MemberNames::PATH => $path,
990
            MemberNames::SCOPE => $scope,
991
            MemberNames::SCOPE_ID => $scopeId
992
        );
993
994
        // generate the UID from the passed data
995
        $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
996
997
        // iterate over the core config data and try to find the requested configuration value
998
        if (isset($this->coreConfigData[$uniqueIdentifier])) {
999
            return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1000
        }
1001
1002
        // query whether or not we've to query for the configuration value on fallback level 'websites' also
1003
        if ($scope === ScopeKeys::SCOPE_STORES) {
1004
            // query whether or not the website with the passed ID is available
1005
            foreach ($this->storeWebsites as $storeWebsite) {
1006
                if ($storeWebsite[MemberNames::WEBSITE_ID] === $scopeId) {
1007
                    // replace scope with 'websites' and website ID
1008
                    $coreConfigData = array_merge(
1009
                        $coreConfigData,
1010
                        array(
1011
                            MemberNames::SCOPE    => ScopeKeys::SCOPE_WEBSITES,
1012
                            MemberNames::SCOPE_ID => $storeWebsite[MemberNames::WEBSITE_ID]
1013
                        )
1014
                    );
1015
1016
                    // generate the UID from the passed data, merged with the 'websites' scope and ID
1017
                    $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1018
1019
                    // query whether or not, the configuration value on 'websites' level
1020 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...
1021
                        return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1022
                    }
1023
                }
1024
            }
1025
        }
1026
1027
        // replace scope with 'default' and scope ID '0'
1028
        $coreConfigData = array_merge(
1029
            $coreConfigData,
1030
            array(
1031
                MemberNames::SCOPE    => ScopeKeys::SCOPE_DEFAULT,
1032
                MemberNames::SCOPE_ID => 0
1033
            )
1034
        );
1035
1036
        // generate the UID from the passed data, merged with the 'default' scope and ID 0
1037
        $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1038
1039
        // query whether or not, the configuration value on 'default' level
1040 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...
1041
            return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1042
        }
1043
1044
        // if not, return the passed default value
1045
        if ($default !== null) {
1046
            return $default;
1047
        }
1048
1049
        // throw an exception if no value can be found
1050
        // in the Magento configuration
1051
        throw new \Exception(
1052
            sprintf(
1053
                'Can\'t find a value for configuration "%s-%s-%d" in "core_config_data"',
1054
                $path,
1055
                $scope,
1056
                $scopeId
1057
            )
1058
        );
1059
    }
1060
1061
    /**
1062
     * Resolve the original column name for the passed one.
1063
     *
1064
     * @param string $columnName The column name that has to be resolved
1065
     *
1066
     * @return string|null The original column name
1067
     */
1068
    public function resolveOriginalColumnName($columnName)
1069
    {
1070
1071
        // try to load the original data
1072
        $originalData = $this->getOriginalData();
1073
1074
        // query whether or not original data is available
1075
        if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES])) {
1076
            // query whether or not the original column name is available
1077
            if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES][$columnName])) {
1078
                return $originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES][$columnName];
1079
            }
1080
1081
            // query whether or a wildcard column name is available
1082
            if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES]['*'])) {
1083
                return $originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES]['*'];
1084
            }
1085
        }
1086
1087
        // return the original column name
1088
        return $columnName;
1089
    }
1090
1091
    /**
1092
     * Return's the original data if available, or an empty array.
1093
     *
1094
     * @return array The original data
1095
     */
1096
    public function getOriginalData()
1097
    {
1098
1099
        // initialize the array for the original data
1100
        $originalData = array();
1101
1102
        // query whether or not the column contains original data
1103
        if ($this->hasOriginalData()) {
1104
            // unerialize the original data from the column
1105
            $originalData = unserialize($this->row[$this->headers[ColumnKeys::ORIGINAL_DATA]]);
1106
        }
1107
1108
        // return an empty array, if not
1109
        return $originalData;
1110
    }
1111
1112
    /**
1113
     * Query's whether or not the actual column contains original data like
1114
     * filename, line number and column names.
1115
     *
1116
     * @return boolean TRUE if the actual column contains origin data, else FALSE
1117
     */
1118
    public function hasOriginalData()
1119
    {
1120
        return isset($this->headers[ColumnKeys::ORIGINAL_DATA]) && isset($this->row[$this->headers[ColumnKeys::ORIGINAL_DATA]]);
1121
    }
1122
1123
    /**
1124
     * Wraps the passed exeception into a new one by trying to resolve the original filname,
1125
     * line number and column names and use it for a detailed exception message.
1126
     *
1127
     * @param array      $columnNames The column names that should be resolved and wrapped
1128
     * @param \Exception $parent      The exception we want to wrap
1129
     * @param string     $className   The class name of the exception type we want to wrap the parent one
1130
     *
1131
     * @return \Exception the wrapped exception
1132
     */
1133
    public function wrapException(
1134
        array $columnNames = array(),
1135
        \Exception $parent = null,
1136
        $className = '\TechDivision\Import\Exceptions\WrappedColumnException'
1137
    ) {
1138
1139
        // initialize the message
1140
        $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...
1141
1142
        // query whether or not has been a result of invalid data of a previous column of a CSV file
1143
        if ($this->hasOriginalData()) {
1144
            // load the original data
1145
            $originalData = $this->getOriginalData();
1146
1147
            // replace old filename and line number of the original message
1148
            $message = $this->appendExceptionSuffix(
1149
                $this->stripExceptionSuffix($message),
1150
                $originalData[ColumnKeys::ORIGINAL_FILENAME],
1151
                $originalData[ColumnKeys::ORIGINAL_LINE_NUMBER]
1152
            );
1153
        } else {
1154
            // append filename and line number to the original message
1155
            $message = $this->appendExceptionSuffix(
1156
                $this->stripExceptionSuffix($message),
1157
                $this->filename,
1158
                $this->lineNumber
1159
            );
1160
        }
1161
1162
        // query whether or not, column names has been passed
1163
        if (sizeof($columnNames) > 0) {
1164
            // prepare the original column names
1165
            $originalColumnNames = array();
1166
            foreach ($columnNames as $columnName) {
1167
                $originalColumnNames[] = $this->resolveOriginalColumnName($columnName);
1168
            }
1169
1170
            // append the column information
1171
            $message = sprintf('%s in column(s) %s', $message, implode(', ', $originalColumnNames));
1172
        }
1173
1174
        // create a new exception and wrap the parent one
1175
        return new $className($message, null, $parent);
1176
    }
1177
1178
    /**
1179
     * Strip's the exception suffix containing filename and line number from the
1180
     * passed message.
1181
     *
1182
     * @param string $message The message to strip the exception suffix from
1183
     *
1184
     * @return mixed The message without the exception suffix
1185
     */
1186
    public function stripExceptionSuffix($message)
1187
    {
1188
        return str_replace($this->appendExceptionSuffix(), '', $message);
1189
    }
1190
1191
    /**
1192
     * Append's the exception suffix containing filename and line number to the
1193
     * passed message. If no message has been passed, only the suffix will be
1194
     * returned
1195
     *
1196
     * @param string|null $message    The message to append the exception suffix to
1197
     * @param string|null $filename   The filename used to create the suffix
1198
     * @param string|null $lineNumber The line number used to create the suffx
1199
     *
1200
     * @return string The message with the appended exception suffix
1201
     */
1202
    public function appendExceptionSuffix($message = null, $filename = null, $lineNumber = null)
1203
    {
1204
1205
        // query whether or not a filename has been passed
1206
        if ($filename === null) {
1207
            $filename = $this->getFilename();
1208
        }
1209
1210
        // query whether or not a line number has been passed
1211
        if ($lineNumber === null) {
1212
            $lineNumber = $this->getLineNumber();
1213
        }
1214
1215
        // if no message has been passed, only return the suffix
1216
        if ($message === null) {
1217
            return sprintf(' in file %s on line %d', $filename, $lineNumber);
1218
        }
1219
1220
        // concatenate the message with the suffix and return it
1221
        return sprintf('%s in file %s on line %d', $message, $filename, $lineNumber);
1222
    }
1223
1224
    /**
1225
     * Raises the value for the counter with the passed key by one.
1226
     *
1227
     * @param mixed $counterName The name of the counter to raise
1228
     *
1229
     * @return integer The counter's new value
1230
     */
1231
    public function raiseCounter($counterName)
1232
    {
1233
1234
        // raise the counter with the passed name
1235
        return $this->getRegistryProcessor()->raiseCounter(
1236
            RegistryKeys::STATUS,
1237
            $counterName
1238
        );
1239
    }
1240
1241
    /**
1242
     * Merge the passed array into the status of the actual import.
1243
     *
1244
     * @param array $status The status information to be merged
1245
     *
1246
     * @return void
1247
     */
1248
    public function mergeAttributesRecursive(array $status)
1249
    {
1250
1251
        // merge the passed status
1252
        $this->getRegistryProcessor()->mergeAttributesRecursive(
1253
            RegistryKeys::STATUS,
1254
            $status
1255
        );
1256
    }
1257
}
1258