Completed
Pull Request — master (#89)
by Tim
02:41
created

AbstractSubject::setHeaders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
 * TechDivision\Import\Subjects\AbstractSubject
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2016 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/techdivision/import
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Subjects;
22
23
use Psr\Log\LoggerInterface;
24
use TechDivision\Import\Utils\ScopeKeys;
25
use TechDivision\Import\Utils\LoggerKeys;
26
use TechDivision\Import\Utils\ColumnKeys;
27
use TechDivision\Import\Utils\MemberNames;
28
use TechDivision\Import\Utils\RegistryKeys;
29
use TechDivision\Import\Utils\Generators\GeneratorInterface;
30
use TechDivision\Import\Services\RegistryProcessor;
31
use TechDivision\Import\Callbacks\CallbackInterface;
32
use TechDivision\Import\Observers\ObserverInterface;
33
use TechDivision\Import\Adapter\ImportAdapterInterface;
34
use TechDivision\Import\Exceptions\WrappedColumnException;
35
use TechDivision\Import\Services\RegistryProcessorInterface;
36
use TechDivision\Import\Configuration\SubjectConfigurationInterface;
37
38
/**
39
 * An abstract subject implementation.
40
 *
41
 * @author    Tim Wagner <[email protected]>
42
 * @copyright 2016 TechDivision GmbH <[email protected]>
43
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
44
 * @link      https://github.com/techdivision/import
45
 * @link      http://www.techdivision.com
46
 */
47
abstract class AbstractSubject implements SubjectInterface
48
{
49
50
    /**
51
     * The trait that provides basic filesystem handling functionality.
52
     *
53
     * @var TechDivision\Import\Subjects\FilesystemTrait
54
     */
55
    use FilesystemTrait;
56
57
    /**
58
     * The import adapter instance.
59
     *
60
     * @var \TechDivision\Import\Adapter\AdapterInterface
61
     */
62
    protected $importAdapter;
63
64
    /**
65
     * The system configuration.
66
     *
67
     * @var \TechDivision\Import\Configuration\SubjectConfigurationInterface
68
     */
69
    protected $configuration;
70
71
    /**
72
     * The array with the system logger instances.
73
     *
74
     * @var array
75
     */
76
    protected $systemLoggers = array();
77
78
    /**
79
     * The RegistryProcessor instance to handle running threads.
80
     *
81
     * @var \TechDivision\Import\Services\RegistryProcessorInterface
82
     */
83
    protected $registryProcessor;
84
85
    /**
86
     * The actions unique serial.
87
     *
88
     * @var string
89
     */
90
    protected $serial;
91
92
    /**
93
     * The name of the file to be imported.
94
     *
95
     * @var string
96
     */
97
    protected $filename;
98
99
    /**
100
     * Array with the subject's observers.
101
     *
102
     * @var array
103
     */
104
    protected $observers = array();
105
106
    /**
107
     * Array with the subject's callbacks.
108
     *
109
     * @var array
110
     */
111
    protected $callbacks = array();
112
113
    /**
114
     * The subject's callback mappings.
115
     *
116
     * @var array
117
     */
118
    protected $callbackMappings = array();
119
120
    /**
121
     * Contain's the column names from the header line.
122
     *
123
     * @var array
124
     */
125
    protected $headers = array();
126
127
    /**
128
     * The actual line number.
129
     *
130
     * @var integer
131
     */
132
    protected $lineNumber = 0;
133
134
    /**
135
     * The actual operation name.
136
     *
137
     * @var string
138
     */
139
    protected $operationName ;
140
141
    /**
142
     * The flag that stop's overserver execution on the actual row.
143
     *
144
     * @var boolean
145
     */
146
    protected $skipRow = false;
147
148
    /**
149
     * The available root categories.
150
     *
151
     * @var array
152
     */
153
    protected $rootCategories = array();
154
155
    /**
156
     * The Magento configuration.
157
     *
158
     * @var array
159
     */
160
    protected $coreConfigData = array();
161
162
    /**
163
     * The available stores.
164
     *
165
     * @var array
166
     */
167
    protected $stores = array();
168
169
    /**
170
     * The available websites.
171
     *
172
     * @var array
173
     */
174
    protected $storeWebsites = array();
175
176
    /**
177
     * The default store.
178
     *
179
     * @var array
180
     */
181
    protected $defaultStore;
182
183
    /**
184
     * The store view code the create the product/attributes for.
185
     *
186
     * @var string
187
     */
188
    protected $storeViewCode;
189
190
    /**
191
     * The UID generator for the core config data.
192
     *
193
     * @var \TechDivision\Import\Utils\Generators\GeneratorInterface
194
     */
195
    protected $coreConfigDataUidGenerator;
196
197
    /**
198
     * The actual row.
199
     *
200
     * @var array
201
     */
202
    protected $row = array();
203
204
    /**
205
     * Initialize the subject instance.
206
     *
207
     * @param \TechDivision\Import\Services\RegistryProcessorInterface $registryProcessor          The registry processor instance
208
     * @param \TechDivision\Import\Utils\Generators\GeneratorInterface $coreConfigDataUidGenerator The UID generator for the core config data
209
     * @param array                                                    $systemLoggers              The array with the system loggers instances
210
     */
211 47
    public function __construct(
212
        RegistryProcessorInterface $registryProcessor,
213
        GeneratorInterface $coreConfigDataUidGenerator,
214
        array $systemLoggers
215
    ) {
216 47
        $this->systemLoggers = $systemLoggers;
217 47
        $this->registryProcessor = $registryProcessor;
218 47
        $this->coreConfigDataUidGenerator = $coreConfigDataUidGenerator;
219 47
    }
220
221
    /**
222
     * Return's the default callback mappings.
223
     *
224
     * @return array The default callback mappings
225
     */
226 47
    public function getDefaultCallbackMappings()
227
    {
228 47
        return array();
229
    }
230
231
    /**
232
     * Return's the actual row.
233
     *
234
     * @return array The actual row
235
     */
236 4
    public function getRow()
237
    {
238 4
        return $this->row;
239
    }
240
241
    /**
242
     * Stop's observer execution on the actual row.
243
     *
244
     * @return void
245
     */
246 1
    public function skipRow()
247
    {
248 1
        $this->skipRow = true;
249 1
    }
250
251
    /**
252
     * Return's the actual line number.
253
     *
254
     * @return integer The line number
255
     */
256 3
    public function getLineNumber()
257
    {
258 3
        return $this->lineNumber;
259
    }
260
261
    /**
262
     * Return's the actual operation name.
263
     *
264
     * @return string
265
     */
266
    public function getOperationName()
267
    {
268
        return $this->operationName;
269
    }
270
271
    /**
272
     * Set's the array containing header row.
273
     *
274
     * @param array $headers The array with the header row
275
     *
276
     * @return void
277
     */
278 9
    public function setHeaders(array $headers)
279
    {
280 9
        $this->headers = $headers;
281 9
    }
282
283
    /**
284
     * Return's the array containing header row.
285
     *
286
     * @return array The array with the header row
287
     */
288 2
    public function getHeaders()
289
    {
290 2
        return $this->headers;
291
    }
292
293
    /**
294
     * Queries whether or not the header with the passed name is available.
295
     *
296
     * @param string $name The header name to query
297
     *
298
     * @return boolean TRUE if the header is available, else FALSE
299
     */
300 8
    public function hasHeader($name)
301
    {
302 8
        return isset($this->headers[$name]);
303
    }
304
305
    /**
306
     * Return's the header value for the passed name.
307
     *
308
     * @param string $name The name of the header to return the value for
309
     *
310
     * @return mixed The header value
311
     * \InvalidArgumentException Is thrown, if the header with the passed name is NOT available
312
     */
313 7
    public function getHeader($name)
314
    {
315
316
        // query whether or not, the header is available
317 7
        if (isset($this->headers[$name])) {
318 6
            return $this->headers[$name];
319
        }
320
321
        // throw an exception, if not
322 1
        throw new \InvalidArgumentException(sprintf('Header %s is not available', $name));
323
    }
324
325
    /**
326
     * Add's the header with the passed name and position, if not NULL.
327
     *
328
     * @param string $name The header name to add
329
     *
330
     * @return integer The new headers position
331
     */
332 2
    public function addHeader($name)
333
    {
334
335
        // add the header
336 2
        $this->headers[$name] = $position = sizeof($this->headers);
337
338
        // return the new header's position
339 2
        return $position;
340
    }
341
342
    /**
343
     * Query whether or not a value for the column with the passed name exists.
344
     *
345
     * @param string $name The column name to query for a valid value
346
     *
347
     * @return boolean TRUE if the value is set, else FALSE
348
     */
349 2 View Code Duplication
    public function hasValue($name)
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...
350
    {
351
352
        // query whether or not the header is available
353 2
        if ($this->hasHeader($name)) {
354
            // load the key for the row
355 1
            $headerValue = $this->getHeader($name);
356
357
            // query whether the rows column has a vaild value
358 1
            return (isset($this->row[$headerValue]) && $this->row[$headerValue] != '');
359
        }
360
361
        // return FALSE if not
362 1
        return false;
363
    }
364
365
    /**
366
     * Set the value in the passed column name.
367
     *
368
     * @param string $name  The column name to set the value for
369
     * @param mixed  $value The value to set
370
     *
371
     * @return void
372
     */
373 2
    public function setValue($name, $value)
374
    {
375 2
        $this->row[$this->getHeader($name)] = $value;
376 2
    }
377
378
    /**
379
     * Resolve's the value with the passed colum name from the actual row. If a callback will
380
     * be passed, the callback will be invoked with the found value as parameter. If
381
     * the value is NULL or empty, the default value will be returned.
382
     *
383
     * @param string        $name     The name of the column to return the value for
384
     * @param mixed|null    $default  The default value, that has to be returned, if the row's value is empty
385
     * @param callable|null $callback The callback that has to be invoked on the value, e. g. to format it
386
     *
387
     * @return mixed|null The, almost formatted, value
388
     */
389 5 View Code Duplication
    public function getValue($name, $default = null, callable $callback = null)
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...
390
    {
391
392
        // initialize the value
393 5
        $value = null;
394
395
        // query whether or not the header is available
396 5
        if ($this->hasHeader($name)) {
397
            // load the header value
398 4
            $headerValue = $this->getHeader($name);
399
            // query wheter or not, the value with the requested key is available
400 4
            if ((isset($this->row[$headerValue]) && $this->row[$headerValue] != '')) {
401 4
                $value = $this->row[$headerValue];
402
            }
403
        }
404
405
        // query whether or not, a callback has been passed
406 5
        if ($value != null && is_callable($callback)) {
407 1
            $value = call_user_func($callback, $value);
408
        }
409
410
        // query whether or not
411 5
        if ($value == null && $default !== null) {
412 1
            $value = $default;
413
        }
414
415
        // return the value
416 5
        return $value;
417
    }
418
419
    /**
420
     * Tries to format the passed value to a valid date with format 'Y-m-d H:i:s'.
421
     * If the passed value is NOT a valid date, NULL will be returned.
422
     *
423
     * @param string $value The value to format
424
     *
425
     * @return string|null The formatted date or NULL if the date is not valid
426
     */
427 2
    public function formatDate($value)
428
    {
429
430
        // create a DateTime instance from the passed value
431 2
        if ($dateTime = \DateTime::createFromFormat($this->getSourceDateFormat(), $value)) {
432 1
            return $dateTime->format('Y-m-d H:i:s');
433
        }
434
435
        // return NULL, if the passed value is NOT a valid date
436 1
        return null;
437
    }
438
439
    /**
440
     * Extracts the elements of the passed value by exploding them
441
     * with the also passed delimiter.
442
     *
443
     * @param string      $value     The value to extract
444
     * @param string|null $delimiter The delimiter used to extrace the elements
445
     *
446
     * @return array The exploded values
447
     */
448 2
    public function explode($value, $delimiter = null)
449
    {
450
        // load the global configuration
451 2
        $configuration = $this->getConfiguration();
452
453
        // initializet delimiter, enclosure and escape char
454 2
        $delimiter = $delimiter ? $delimiter : $configuration->getDelimiter();
455 2
        $enclosure = $configuration->getEnclosure();
456 2
        $escape = $configuration->getEscape();
457
458
        // parse and return the found data as array
459 2
        return str_getcsv($value, $delimiter, $enclosure, $escape);
460
    }
461
462
    /**
463
     * Queries whether or not debug mode is enabled or not, default is TRUE.
464
     *
465
     * @return boolean TRUE if debug mode is enabled, else FALSE
466
     */
467 1
    public function isDebugMode()
468
    {
469 1
        return $this->getConfiguration()->isDebugMode();
470
    }
471
472
    /**
473
     * Set's the subject configuration.
474
     *
475
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $configuration The subject configuration
476
     *
477
     * @return void
478
     */
479 47
    public function setConfiguration(SubjectConfigurationInterface $configuration)
480
    {
481 47
        $this->configuration = $configuration;
482 47
    }
483
484
    /**
485
     * Return's the subject configuration.
486
     *
487
     * @return \TechDivision\Import\Configuration\SubjectConfigurationInterface The subject configuration
488
     */
489 47
    public function getConfiguration()
490
    {
491 47
        return $this->configuration;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->configuration; (TechDivision\Import\Conf...tConfigurationInterface) is incompatible with the return type declared by the interface TechDivision\Import\Subj...rface::getConfiguration of type TechDivision\Import\Configuration\SubjectInterface.

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...
492
    }
493
494
    /**
495
     * Return's the logger with the passed name, by default the system logger.
496
     *
497
     * @param string $name The name of the requested system logger
498
     *
499
     * @return \Psr\Log\LoggerInterface The logger instance
500
     * @throws \Exception Is thrown, if the requested logger is NOT available
501
     */
502 11
    public function getSystemLogger($name = LoggerKeys::SYSTEM)
503
    {
504
505
        // query whether or not, the requested logger is available
506 11
        if (isset($this->systemLoggers[$name])) {
507 10
            return $this->systemLoggers[$name];
508
        }
509
510
        // throw an exception if the requested logger is NOT available
511 1
        throw new \Exception(sprintf('The requested logger \'%s\' is not available', $name));
512
    }
513
514
    /**
515
     * Set's the import adapter instance.
516
     *
517
     * @param \TechDivision\Import\Adapter\ImportAdapterInterface $importAdapter The import adapter instance
518
     *
519
     * @return void
520
     */
521 3
    public function setImportAdapter(ImportAdapterInterface $importAdapter)
522
    {
523 3
        $this->importAdapter = $importAdapter;
0 ignored issues
show
Documentation Bug introduced by
It seems like $importAdapter of type object<TechDivision\Impo...ImportAdapterInterface> is incompatible with the declared type object<TechDivision\Impo...apter\AdapterInterface> of property $importAdapter.

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...
524 3
    }
525
526
    /**
527
     * Return's the import adapter instance.
528
     *
529
     * @return \TechDivision\Import\Adapter\ImportAdapterInterface The import adapter instance
530
     */
531 3
    public function getImportAdapter()
532
    {
533 3
        return $this->importAdapter;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->importAdapter; (TechDivision\Import\Adapter\AdapterInterface) is incompatible with the return type declared by the interface TechDivision\Import\Subj...rface::getImportAdapter of type TechDivision\Import\Adapter\ImportAdapterInterface.

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...
534
    }
535
536
    /**
537
     * Return's the array with the system logger instances.
538
     *
539
     * @return array The logger instance
540
     */
541 1
    public function getSystemLoggers()
542
    {
543 1
        return $this->systemLoggers;
544
    }
545
546
    /**
547
     * Return's the RegistryProcessor instance to handle the running threads.
548
     *
549
     * @return \TechDivision\Import\Services\RegistryProcessorInterface The registry processor instance
550
     */
551 47
    public function getRegistryProcessor()
552
    {
553 47
        return $this->registryProcessor;
554
    }
555
556
    /**
557
     * Set's the unique serial for this import process.
558
     *
559
     * @param string $serial The unique serial
560
     *
561
     * @return void
562
     */
563 3
    public function setSerial($serial)
564
    {
565 3
        $this->serial = $serial;
566 3
    }
567
568
    /**
569
     * Return's the unique serial for this import process.
570
     *
571
     * @return string The unique serial
572
     */
573 3
    public function getSerial()
574
    {
575 3
        return $this->serial;
576
    }
577
578
    /**
579
     * Set's the name of the file to import
580
     *
581
     * @param string $filename The filename
582
     *
583
     * @return void
584
     */
585 10
    public function setFilename($filename)
586
    {
587 10
        $this->filename = $filename;
588 10
    }
589
590
    /**
591
     * Return's the name of the file to import.
592
     *
593
     * @return string The filename
594
     */
595 3
    public function getFilename()
596
    {
597 3
        return $this->filename;
598
    }
599
600
    /**
601
     * Return's the source date format to use.
602
     *
603
     * @return string The source date format
604
     */
605 2
    public function getSourceDateFormat()
606
    {
607 2
        return $this->getConfiguration()->getSourceDateFormat();
608
    }
609
610
    /**
611
     * Return's the multiple field delimiter character to use, default value is comma (,).
612
     *
613
     * @return string The multiple field delimiter character
614
     */
615 1
    public function getMultipleFieldDelimiter()
616
    {
617 1
        return $this->getConfiguration()->getMultipleFieldDelimiter();
618
    }
619
620
    /**
621
     * Return's the multiple value delimiter character to use, default value is comma (|).
622
     *
623
     * @return string The multiple value delimiter character
624
     */
625 1
    public function getMultipleValueDelimiter()
626
    {
627 1
        return $this->getConfiguration()->getMultipleValueDelimiter();
628
    }
629
630
    /**
631
     * Return's the initialized PDO connection.
632
     *
633
     * @return \PDO The initialized PDO connection
634
     */
635
    public function getConnection()
636
    {
637
        return $this->getProductProcessor()->getConnection();
0 ignored issues
show
Bug introduced by
The method getProductProcessor() does not seem to exist on object<TechDivision\Impo...bjects\AbstractSubject>.

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

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

Loading history...
638
    }
639
640
    /**
641
     * Intializes the previously loaded global data for exactly one bunch.
642
     *
643
     * @param string $serial The serial of the actual import
644
     *
645
     * @return void
646
     * @see \Importer\Csv\Actions\ProductImportAction::prepare()
647
     */
648 47
    public function setUp($serial)
649
    {
650
651
        // load the status of the actual import
652 47
        $status = $this->getRegistryProcessor()->getAttribute($serial);
653
654
        // load the global data we've prepared initially
655 47
        $this->stores = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::STORES];
656 47
        $this->defaultStore = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::DEFAULT_STORE];
657 47
        $this->storeWebsites  = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::STORE_WEBSITES];
658 47
        $this->rootCategories = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::ROOT_CATEGORIES];
659 47
        $this->coreConfigData = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::CORE_CONFIG_DATA];
660
661
        // initialize the operation name
662 47
        $this->operationName = $this->getConfiguration()->getConfiguration()->getOperationName();
663
664
        // merge the callback mappings with the mappings from the child instance
665 47
        $this->callbackMappings = array_merge($this->callbackMappings, $this->getDefaultCallbackMappings());
666
667
        // merge the callback mappings the the one from the configuration file
668 47
        foreach ($this->getConfiguration()->getCallbacks() as $callbackMappings) {
669
            foreach ($callbackMappings as $attributeCode => $mappings) {
670
                // write a log message, that default callback configuration will
671
                // be overwritten with the one from the configuration file
672
                if (isset($this->callbackMappings[$attributeCode])) {
673
                    $this->getSystemLogger()->notice(
674
                        sprintf('Now override callback mappings for attribute %s with values found in configuration file', $attributeCode)
675
                    );
676
                }
677
678
                // override the attributes callbacks
679
                $this->callbackMappings[$attributeCode] = $mappings;
680
            }
681
        }
682 47
    }
683
684
    /**
685
     * Clean up the global data after importing the variants.
686
     *
687
     * @param string $serial The serial of the actual import
688
     *
689
     * @return void
690
     */
691
    public function tearDown($serial)
692
    {
693
694
        // load the registry processor
695
        $registryProcessor = $this->getRegistryProcessor();
696
697
        // update the source directory for the next subject
698
        $registryProcessor->mergeAttributesRecursive(
699
            $serial,
700
            array(RegistryKeys::SOURCE_DIRECTORY => $this->getNewSourceDir($serial))
701
        );
702
703
        // log a debug message with the new source directory
704
        $this->getSystemLogger()->debug(
705
            sprintf('Subject %s successfully updated source directory to %s', __CLASS__, $this->getNewSourceDir($serial))
706
        );
707
    }
708
709
    /**
710
     * Return's the next source directory, which will be the target directory
711
     * of this subject, in most cases.
712
     *
713
     * @param string $serial The serial of the actual import
714
     *
715
     * @return string The new source directory
716
     */
717
    protected function getNewSourceDir($serial)
718
    {
719
        return sprintf('%s/%s', $this->getConfiguration()->getTargetDir(), $serial);
720
    }
721
722
    /**
723
     * Register the passed observer with the specific type.
724
     *
725
     * @param \TechDivision\Import\Observers\ObserverInterface $observer The observer to register
726
     * @param string                                           $type     The type to register the observer with
727
     *
728
     * @return void
729
     */
730 6
    public function registerObserver(ObserverInterface $observer, $type)
731
    {
732
733
        // query whether or not the array with the callbacks for the
734
        // passed type has already been initialized, or not
735 6
        if (!isset($this->observers[$type])) {
736 6
            $this->observers[$type] = array();
737
        }
738
739
        // append the callback with the instance of the passed type
740 6
        $this->observers[$type][] = $observer;
741 6
    }
742
743
    /**
744
     * Register the passed callback with the specific type.
745
     *
746
     * @param \TechDivision\Import\Callbacks\CallbackInterface $callback The subject to register the callbacks for
747
     * @param string                                           $type     The type to register the callback with
748
     *
749
     * @return void
750
     */
751
    public function registerCallback(CallbackInterface $callback, $type)
752
    {
753
754
        // query whether or not the array with the callbacks for the
755
        // passed type has already been initialized, or not
756
        if (!isset($this->callbacks[$type])) {
757
            $this->callbacks[$type] = array();
758
        }
759
760
        // append the callback with the instance of the passed type
761
        $this->callbacks[$type][] = $callback;
762
    }
763
764
    /**
765
     * Return's the array with callbacks for the passed type.
766
     *
767
     * @param string $type The type of the callbacks to return
768
     *
769
     * @return array The callbacks
770
     */
771
    public function getCallbacksByType($type)
772
    {
773
774
        // initialize the array for the callbacks
775
        $callbacks = array();
776
777
        // query whether or not callbacks for the type are available
778
        if (isset($this->callbacks[$type])) {
779
            $callbacks = $this->callbacks[$type];
780
        }
781
782
        // return the array with the type's callbacks
783
        return $callbacks;
784
    }
785
786
    /**
787
     * Return's the array with the available observers.
788
     *
789
     * @return array The observers
790
     */
791 6
    public function getObservers()
792
    {
793 6
        return $this->observers;
794
    }
795
796
    /**
797
     * Return's the array with the available callbacks.
798
     *
799
     * @return array The callbacks
800
     */
801
    public function getCallbacks()
802
    {
803
        return $this->callbacks;
804
    }
805
806
    /**
807
     * Return's the callback mappings for this subject.
808
     *
809
     * @return array The array with the subject's callback mappings
810
     */
811
    public function getCallbackMappings()
812
    {
813
        return $this->callbackMappings;
814
    }
815
816
    /**
817
     * Imports the content of the file with the passed filename.
818
     *
819
     *
820
     * @param string $serial   The serial of the actual import
821
     * @param string $filename The filename to process
822
     *
823
     * @return void
824
     * @throws \Exception Is thrown, if the import can't be processed
825
     */
826 5
    public function import($serial, $filename)
827
    {
828
829
        try {
830
            // stop processing, if the filename doesn't match
831 5
            if (!$this->match($filename)) {
832 1
                return;
833
            }
834
835
            // load the system logger instance
836 4
            $systemLogger = $this->getSystemLogger();
837
838
            // prepare the flag filenames
839 4
            $inProgressFilename = sprintf('%s.inProgress', $filename);
840 4
            $importedFilename = sprintf('%s.imported', $filename);
841 4
            $failedFilename = sprintf('%s.failed', $filename);
842
843
            // query whether or not the file has already been imported
844 4
            if ($this->isFile($failedFilename) ||
845 3
                $this->isFile($importedFilename) ||
846 4
                $this->isFile($inProgressFilename)
847
            ) {
848
                // log a debug message and exit
849 1
                $systemLogger->debug(sprintf('Import running, found inProgress file %s', $inProgressFilename));
850 1
                return;
851
            }
852
853
            // flag file as in progress
854 3
            $this->touch($inProgressFilename);
855
856
            // track the start time
857 3
            $startTime = microtime(true);
858
859
            // initialize the serial/filename
860 3
            $this->setSerial($serial);
861 3
            $this->setFilename($filename);
862
863
            // log a message that the file has to be imported
864 3
            $systemLogger->debug(sprintf('Now start importing file %s', $filename));
865
866
            // let the adapter process the file
867 3
            $this->getImportAdapter()->import(array($this, 'importRow'), $filename);
868
869
            // track the time needed for the import in seconds
870 1
            $endTime = microtime(true) - $startTime;
871
872
            // log a message that the file has successfully been imported
873 1
            $systemLogger->debug(sprintf('Successfully imported file %s in %f s', $filename, $endTime));
874
875
            // rename flag file, because import has been successfull
876 1
            $this->rename($inProgressFilename, $importedFilename);
877
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
878 2
        } catch (\Exception $e) {
879
            // rename the flag file, because import failed and write the stack trace
880 2
            $this->rename($inProgressFilename, $failedFilename);
0 ignored issues
show
Bug introduced by
The variable $inProgressFilename does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
881 2
            $this->write($failedFilename, $e->__toString());
882
883
            // do not wrap the exception if not already done
884 2
            if ($e instanceof WrappedColumnException) {
885 1
                throw $e;
886
            }
887
888
            // else wrap and throw the exception
889 1
            throw $this->wrapException(array(), $e);
890
        }
891 1
    }
892
893
    /**
894
     * This method queries whether or not the passed filename matches
895
     * the pattern, based on the subjects configured prefix.
896
     *
897
     * @param string $filename The filename to match
898
     *
899
     * @return boolean TRUE if the filename matches, else FALSE
900
     */
901 5
    protected function match($filename)
902
    {
903
904
        // prepare the pattern to query whether the file has to be processed or not
905 5
        $pattern = sprintf(
906 5
            '/^.*\/%s.*\\.%s$/',
907 5
            $this->getConfiguration()->getPrefix(),
908 5
            $this->getConfiguration()->getSuffix()
909
        );
910
911
        // stop processing, if the filename doesn't match
912 5
        return (boolean) preg_match($pattern, $filename);
913
    }
914
915
    /**
916
     * Imports the passed row into the database. If the import failed, the exception
917
     * will be catched and logged, but the import process will be continued.
918
     *
919
     * @param array $row The row with the data to be imported
920
     *
921
     * @return void
922
     */
923 7
    public function importRow(array $row)
924
    {
925
926
        // initialize the row
927 7
        $this->row = $row;
928
929
        // raise the line number and reset the skip row flag
930 7
        $this->lineNumber++;
931 7
        $this->skipRow = false;
932
933
        // initialize the headers with the columns from the first line
934 7
        if (sizeof($this->headers) === 0) {
935 1
            foreach ($this->row as $value => $key) {
936 1
                $this->headers[$this->mapAttributeCodeByHeaderMapping($key)] = $value;
937
            }
938 1
            return;
939
        }
940
941
        // process the observers
942 6
        foreach ($this->getObservers() as $observers) {
943
            // invoke the pre-import/import and post-import observers
944 6
            foreach ($observers as $observer) {
945
                // query whether or not we have to skip the row
946 6
                if ($this->skipRow) {
947 1
                    break;
948
                }
949
950
                // if not, set the subject and process the observer
951 6
                if ($observer instanceof ObserverInterface) {
952 6
                    $this->row = $observer->handle($this);
953
                }
954
            }
955
        }
956
957
        // log a debug message with the actual line nr/file information
958 6
        $this->getSystemLogger()->debug(
959 6
            sprintf(
960 6
                'Successfully processed row (operation: %s) in file %s on line %d',
961 6
                $this->operationName,
962 6
                $this->filename,
963 6
                $this->lineNumber
964
            )
965
        );
966 6
    }
967
968
    /**
969
     * Map the passed attribute code, if a header mapping exists and return the
970
     * mapped mapping.
971
     *
972
     * @param string $attributeCode The attribute code to map
973
     *
974
     * @return string The mapped attribute code, or the original one
975
     */
976 2
    public function mapAttributeCodeByHeaderMapping($attributeCode)
977
    {
978
979
        // load the header mappings
980 2
        $headerMappings = $this->getHeaderMappings();
981
982
        // query weather or not we've a mapping, if yes, map the attribute code
983 2
        if (isset($headerMappings[$attributeCode])) {
984 1
            $attributeCode = $headerMappings[$attributeCode];
985
        }
986
987
        // return the (mapped) attribute code
988 2
        return $attributeCode;
989
    }
990
991
    /**
992
     * Queries whether or not that the subject needs an OK file to be processed.
993
     *
994
     * @return boolean TRUE if the subject needs an OK file, else FALSE
995
     */
996 1
    public function isOkFileNeeded()
997
    {
998 1
        return $this->getConfiguration()->isOkFileNeeded();
999
    }
1000
1001
    /**
1002
     * Return's the default store.
1003
     *
1004
     * @return array The default store
1005
     */
1006 4
    public function getDefaultStore()
1007
    {
1008 4
        return $this->defaultStore;
1009
    }
1010
1011
    /**
1012
     * Set's the store view code the create the product/attributes for.
1013
     *
1014
     * @param string $storeViewCode The store view code
1015
     *
1016
     * @return void
1017
     */
1018 4
    public function setStoreViewCode($storeViewCode)
1019
    {
1020 4
        $this->storeViewCode = $storeViewCode;
1021 4
    }
1022
1023
    /**
1024
     * Return's the store view code the create the product/attributes for.
1025
     *
1026
     * @param string|null $default The default value to return, if the store view code has not been set
1027
     *
1028
     * @return string The store view code
1029
     */
1030 8
    public function getStoreViewCode($default = null)
1031
    {
1032
1033
        // return the store view code, if available
1034 8
        if ($this->storeViewCode != null) {
1035 4
            return $this->storeViewCode;
1036
        }
1037
1038
        // if NOT and a default code is available
1039 4
        if ($default != null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $default of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
1040
            // return the default value
1041 3
            return $default;
1042
        }
1043 1
    }
1044
1045
    /**
1046
     * Return's the store ID of the store with the passed store view code
1047
     *
1048
     * @param string $storeViewCode The store view code to return the store ID for
1049
     *
1050
     * @return integer The ID of the store with the passed ID
1051
     * @throws \Exception Is thrown, if the store with the actual code is not available
1052
     */
1053 4
    public function getStoreId($storeViewCode)
1054
    {
1055
1056
        // query whether or not, the requested store is available
1057 4
        if (isset($this->stores[$storeViewCode])) {
1058 3
            return (integer) $this->stores[$storeViewCode][MemberNames::STORE_ID];
1059
        }
1060
1061
        // throw an exception, if not
1062 1
        throw new \Exception(
1063 1
            sprintf(
1064 1
                'Found invalid store view code %s in file %s on line %d',
1065 1
                $storeViewCode,
1066 1
                $this->getFilename(),
1067 1
                $this->getLineNumber()
1068
            )
1069
        );
1070
    }
1071
1072
    /**
1073
     * Return's the store ID of the actual row, or of the default store
1074
     * if no store view code is set in the CSV file.
1075
     *
1076
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
1077
     *
1078
     * @return integer The ID of the actual store
1079
     * @throws \Exception Is thrown, if the store with the actual code is not available
1080
     */
1081 2
    public function getRowStoreId($default = null)
1082
    {
1083
1084
        // initialize the default store view code, if not passed
1085 2
        if ($default == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $default of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1086 2
            $defaultStore = $this->getDefaultStore();
1087 2
            $default = $defaultStore[MemberNames::CODE];
1088
        }
1089
1090
        // load the store view code the create the product/attributes for
1091 2
        return $this->getStoreId($this->getStoreViewCode($default));
1092
    }
1093
1094
    /**
1095
     * Prepare's the store view code in the subject.
1096
     *
1097
     * @return void
1098
     */
1099 2
    public function prepareStoreViewCode()
1100
    {
1101
1102
        // re-set the store view code
1103 2
        $this->setStoreViewCode(null);
1104
1105
        // initialize the store view code
1106 2
        if ($storeViewCode = $this->getValue(ColumnKeys::STORE_VIEW_CODE)) {
1107 2
            $this->setStoreViewCode($storeViewCode);
1108
        }
1109 2
    }
1110
1111
    /**
1112
     * Return's the root category for the actual view store.
1113
     *
1114
     * @return array The store's root category
1115
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
1116
     */
1117 2
    public function getRootCategory()
1118
    {
1119
1120
        // load the default store
1121 2
        $defaultStore = $this->getDefaultStore();
1122
1123
        // load the actual store view code
1124 2
        $storeViewCode = $this->getStoreViewCode($defaultStore[MemberNames::CODE]);
1125
1126
        // query weather or not we've a root category or not
1127 2
        if (isset($this->rootCategories[$storeViewCode])) {
1128 1
            return $this->rootCategories[$storeViewCode];
1129
        }
1130
1131
        // throw an exception if the root category is NOT available
1132 1
        throw new \Exception(sprintf('Root category for %s is not available', $storeViewCode));
1133
    }
1134
1135
    /**
1136
     * Return's the Magento configuration value.
1137
     *
1138
     * @param string  $path    The Magento path of the requested configuration value
1139
     * @param mixed   $default The default value that has to be returned, if the requested configuration value is not set
1140
     * @param string  $scope   The scope the configuration value has been set
1141
     * @param integer $scopeId The scope ID the configuration value has been set
1142
     *
1143
     * @return mixed The configuration value
1144
     * @throws \Exception Is thrown, if nor a value can be found or a default value has been passed
1145
     */
1146 5
    public function getCoreConfigData($path, $default = null, $scope = ScopeKeys::SCOPE_DEFAULT, $scopeId = 0)
1147
    {
1148
1149
        // initialize the core config data
1150
        $coreConfigData = array(
1151 5
            MemberNames::PATH => $path,
1152 5
            MemberNames::SCOPE => $scope,
1153 5
            MemberNames::SCOPE_ID => $scopeId
1154
        );
1155
1156
        // generate the UID from the passed data
1157 5
        $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1158
1159
        // iterate over the core config data and try to find the requested configuration value
1160 5
        if (isset($this->coreConfigData[$uniqueIdentifier])) {
1161 1
            return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1162
        }
1163
1164
        // query whether or not we've to query for the configuration value on fallback level 'websites' also
1165 4
        if ($scope === ScopeKeys::SCOPE_STORES) {
1166
            // query whether or not the website with the passed ID is available
1167 2
            foreach ($this->storeWebsites as $storeWebsite) {
1168 2
                if ($storeWebsite[MemberNames::WEBSITE_ID] === $scopeId) {
1169
                    // replace scope with 'websites' and website ID
1170 2
                    $coreConfigData = array_merge(
1171 2
                        $coreConfigData,
1172
                        array(
1173 2
                            MemberNames::SCOPE    => ScopeKeys::SCOPE_WEBSITES,
1174
                            MemberNames::SCOPE_ID => $storeWebsite[MemberNames::WEBSITE_ID]
1175
                        )
1176
                    );
1177
1178
                    // generate the UID from the passed data, merged with the 'websites' scope and ID
1179 2
                    $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1180
1181
                    // query whether or not, the configuration value on 'websites' level
1182 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...
1183 2
                        return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1184
                    }
1185
                }
1186
            }
1187
        }
1188
1189
        // replace scope with 'default' and scope ID '0'
1190 3
        $coreConfigData = array_merge(
1191 3
            $coreConfigData,
1192
            array(
1193 3
                MemberNames::SCOPE    => ScopeKeys::SCOPE_DEFAULT,
1194 3
                MemberNames::SCOPE_ID => 0
1195
            )
1196
        );
1197
1198
        // generate the UID from the passed data, merged with the 'default' scope and ID 0
1199 3
        $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1200
1201
        // query whether or not, the configuration value on 'default' level
1202 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...
1203 1
            return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1204
        }
1205
1206
        // if not, return the passed default value
1207 2
        if ($default !== null) {
1208 1
            return $default;
1209
        }
1210
1211
        // throw an exception if no value can be found
1212
        // in the Magento configuration
1213 1
        throw new \Exception(
1214 1
            sprintf(
1215 1
                'Can\'t find a value for configuration "%s-%s-%d" in "core_config_data"',
1216 1
                $path,
1217 1
                $scope,
1218 1
                $scopeId
1219
            )
1220
        );
1221
    }
1222
1223
    /**
1224
     * Resolve the original column name for the passed one.
1225
     *
1226
     * @param string $columnName The column name that has to be resolved
1227
     *
1228
     * @return string|null The original column name
1229
     */
1230 2
    public function resolveOriginalColumnName($columnName)
1231
    {
1232
1233
        // try to load the original data
1234 2
        $originalData = $this->getOriginalData();
1235
1236
        // query whether or not original data is available
1237 2
        if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES])) {
1238
            // query whether or not the original column name is available
1239 1
            if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES][$columnName])) {
1240 1
                return $originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES][$columnName];
1241
            }
1242
1243
            // query whether or a wildcard column name is available
1244 1
            if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES]['*'])) {
1245 1
                return $originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES]['*'];
1246
            }
1247
        }
1248
1249
        // return the original column name
1250 1
        return $columnName;
1251
    }
1252
1253
    /**
1254
     * Return's the original data if available, or an empty array.
1255
     *
1256
     * @return array The original data
1257
     */
1258 2
    public function getOriginalData()
1259
    {
1260
1261
        // initialize the array for the original data
1262 2
        $originalData = array();
1263
1264
        // query whether or not the column contains original data
1265 2
        if ($this->hasOriginalData()) {
1266
            // unerialize the original data from the column
1267 1
            $originalData = unserialize($this->row[$this->headers[ColumnKeys::ORIGINAL_DATA]]);
1268
        }
1269
1270
        // return an empty array, if not
1271 2
        return $originalData;
1272
    }
1273
1274
    /**
1275
     * Query's whether or not the actual column contains original data like
1276
     * filename, line number and column names.
1277
     *
1278
     * @return boolean TRUE if the actual column contains origin data, else FALSE
1279
     */
1280 3
    public function hasOriginalData()
1281
    {
1282 3
        return isset($this->headers[ColumnKeys::ORIGINAL_DATA]) && isset($this->row[$this->headers[ColumnKeys::ORIGINAL_DATA]]);
1283
    }
1284
1285
    /**
1286
     * Wraps the passed exeception into a new one by trying to resolve the original filname,
1287
     * line number and column names and use it for a detailed exception message.
1288
     *
1289
     * @param array      $columnNames The column names that should be resolved and wrapped
1290
     * @param \Exception $parent      The exception we want to wrap
1291
     * @param string     $className   The class name of the exception type we want to wrap the parent one
1292
     *
1293
     * @return \Exception the wrapped exception
1294
     */
1295 2
    public function wrapException(
1296
        array $columnNames = array(),
1297
        \Exception $parent = null,
1298
        $className = '\TechDivision\Import\Exceptions\WrappedColumnException'
1299
    ) {
1300
1301
        // initialize the message
1302 2
        $message = $parent->getMessage();
0 ignored issues
show
Bug introduced by
It seems like $parent is not always an object, but can also be of type null. Maybe add an additional type check?

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

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
1303
1304
        // query whether or not has been a result of invalid data of a previous column of a CSV file
1305 2
        if ($this->hasOriginalData()) {
1306
            // load the original data
1307 1
            $originalData = $this->getOriginalData();
1308
1309
            // replace old filename and line number of the original message
1310 1
            $message = $this->appendExceptionSuffix(
1311 1
                $this->stripExceptionSuffix($message),
1312 1
                $originalData[ColumnKeys::ORIGINAL_FILENAME],
1313 1
                $originalData[ColumnKeys::ORIGINAL_LINE_NUMBER]
1314
            );
1315
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
1316
        } else {
1317
            // append filename and line number to the original message
1318 1
            $message = $this->appendExceptionSuffix(
1319 1
                $this->stripExceptionSuffix($message),
1320 1
                $this->filename,
1321 1
                $this->lineNumber
1322
            );
1323
        }
1324
1325
        // query whether or not, column names has been passed
1326 2
        if (sizeof($columnNames) > 0) {
1327
            // prepare the original column names
1328 1
            $originalColumnNames = array();
1329 1
            foreach ($columnNames as $columnName) {
1330 1
                $originalColumnNames[] = $this->resolveOriginalColumnName($columnName);
1331
            }
1332
1333
            // append the column information
1334 1
            $message = sprintf('%s in column(s) %s', $message, implode(', ', $originalColumnNames));
1335
        }
1336
1337
        // create a new exception and wrap the parent one
1338 2
        return new $className($message, null, $parent);
1339
    }
1340
1341
    /**
1342
     * Strip's the exception suffix containing filename and line number from the
1343
     * passed message.
1344
     *
1345
     * @param string $message The message to strip the exception suffix from
1346
     *
1347
     * @return mixed The message without the exception suffix
1348
     */
1349 2
    public function stripExceptionSuffix($message)
1350
    {
1351 2
        return str_replace($this->appendExceptionSuffix(), '', $message);
1352
    }
1353
1354
    /**
1355
     * Append's the exception suffix containing filename and line number to the
1356
     * passed message. If no message has been passed, only the suffix will be
1357
     * returned
1358
     *
1359
     * @param string|null $message    The message to append the exception suffix to
1360
     * @param string|null $filename   The filename used to create the suffix
1361
     * @param string|null $lineNumber The line number used to create the suffx
1362
     *
1363
     * @return string The message with the appended exception suffix
1364
     */
1365 2
    public function appendExceptionSuffix($message = null, $filename = null, $lineNumber = null)
1366
    {
1367
1368
        // query whether or not a filename has been passed
1369 2
        if ($filename === null) {
1370 2
            $filename = $this->getFilename();
1371
        }
1372
1373
        // query whether or not a line number has been passed
1374 2
        if ($lineNumber === null) {
1375 2
            $lineNumber = $this->getLineNumber();
1376
        }
1377
1378
        // if no message has been passed, only return the suffix
1379 2
        if ($message === null) {
1380 2
            return sprintf(' in file %s on line %d', $filename, $lineNumber);
1381
        }
1382
1383
        // concatenate the message with the suffix and return it
1384 2
        return sprintf('%s in file %s on line %d', $message, $filename, $lineNumber);
1385
    }
1386
1387
    /**
1388
     * Raises the value for the counter with the passed key by one.
1389
     *
1390
     * @param mixed $counterName The name of the counter to raise
1391
     *
1392
     * @return integer The counter's new value
1393
     */
1394 1
    public function raiseCounter($counterName)
1395
    {
1396
1397
        // raise the counter with the passed name
1398 1
        return $this->getRegistryProcessor()->raiseCounter(
1399 1
            $this->getSerial(),
1400 1
            $counterName
1401
        );
1402
    }
1403
1404
    /**
1405
     * Merge the passed array into the status of the actual import.
1406
     *
1407
     * @param array $status The status information to be merged
1408
     *
1409
     * @return void
1410
     */
1411 1
    public function mergeAttributesRecursive(array $status)
1412
    {
1413
1414
        // merge the passed status
1415 1
        $this->getRegistryProcessor()->mergeAttributesRecursive(
1416 1
            $this->getSerial(),
1417 1
            $status
1418
        );
1419 1
    }
1420
}
1421