Completed
Pull Request — master (#89)
by Tim
06:59
created

AbstractSubject::setFilename()   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 80
    public function __construct(
212
        RegistryProcessorInterface $registryProcessor,
213
        GeneratorInterface $coreConfigDataUidGenerator,
214
        array $systemLoggers
215
    ) {
216 80
        $this->systemLoggers = $systemLoggers;
217 80
        $this->registryProcessor = $registryProcessor;
218 80
        $this->coreConfigDataUidGenerator = $coreConfigDataUidGenerator;
219 80
    }
220
221
    /**
222
     * Return's the default callback mappings.
223
     *
224
     * @return array The default callback mappings
225
     */
226 1
    public function getDefaultCallbackMappings()
227
    {
228 1
        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 8
    public function getLineNumber()
257
    {
258 8
        return $this->lineNumber;
259
    }
260
261
    /**
262
     * Return's the actual operation name.
263
     *
264
     * @return string
265
     */
266 1
    public function getOperationName()
267
    {
268 1
        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 80
    public function setConfiguration(SubjectConfigurationInterface $configuration)
480
    {
481 80
        $this->configuration = $configuration;
482 80
    }
483
484
    /**
485
     * Return's the subject configuration.
486
     *
487
     * @return \TechDivision\Import\Configuration\SubjectConfigurationInterface The subject configuration
488
     */
489 80
    public function getConfiguration()
490
    {
491 80
        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 75
    public function getSystemLogger($name = LoggerKeys::SYSTEM)
503
    {
504
505
        // query whether or not, the requested logger is available
506 75
        if (isset($this->systemLoggers[$name])) {
507 75
            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 80
    public function getRegistryProcessor()
552
    {
553 80
        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 6
    public function setSerial($serial)
564
    {
565 6
        $this->serial = $serial;
566 6
    }
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 8
    public function getFilename()
596
    {
597 8
        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 4
    public function getSourceDateFormat()
606
    {
607 4
        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
     * Intializes the previously loaded global data for exactly one bunch.
632
     *
633
     * @param string $serial The serial of the actual import
634
     *
635
     * @return void
636
     * @see \Importer\Csv\Actions\ProductImportAction::prepare()
637
     */
638 80
    public function setUp($serial)
639
    {
640
641
        // load the status of the actual import
642 80
        $status = $this->getRegistryProcessor()->getAttribute($serial);
643
644
        // load the global data we've prepared initially
645 80
        $this->stores = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::STORES];
646 80
        $this->defaultStore = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::DEFAULT_STORE];
647 80
        $this->storeWebsites  = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::STORE_WEBSITES];
648 80
        $this->rootCategories = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::ROOT_CATEGORIES];
649 80
        $this->coreConfigData = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::CORE_CONFIG_DATA];
650
651
        // initialize the operation name
652 80
        $this->operationName = $this->getConfiguration()->getConfiguration()->getOperationName();
653
654
        // merge the callback mappings with the mappings from the child instance
655 80
        $this->callbackMappings = array_merge($this->callbackMappings, $this->getDefaultCallbackMappings());
656
657
        // merge the callback mappings the the one from the configuration file
658 80
        foreach ($this->getConfiguration()->getCallbacks() as $callbackMappings) {
659 80
            foreach ($callbackMappings as $attributeCode => $mappings) {
660
                // write a log message, that default callback configuration will
661
                // be overwritten with the one from the configuration file
662 80
                if (isset($this->callbackMappings[$attributeCode])) {
663 75
                    $this->getSystemLogger()->notice(
664 75
                        sprintf('Now override callback mappings for attribute %s with values found in configuration file', $attributeCode)
665
                    );
666
                }
667
668
                // override the attributes callbacks
669 80
                $this->callbackMappings[$attributeCode] = $mappings;
670
            }
671
        }
672 80
    }
673
674
    /**
675
     * Clean up the global data after importing the variants.
676
     *
677
     * @param string $serial The serial of the actual import
678
     *
679
     * @return void
680
     */
681 1
    public function tearDown($serial)
682
    {
683
684
        // load the registry processor
685 1
        $registryProcessor = $this->getRegistryProcessor();
686
687
        // update the source directory for the next subject
688 1
        $registryProcessor->mergeAttributesRecursive(
689 1
            $serial,
690 1
            array(RegistryKeys::SOURCE_DIRECTORY => $this->getNewSourceDir($serial))
691
        );
692
693
        // log a debug message with the new source directory
694 1
        $this->getSystemLogger()->debug(
695 1
            sprintf('Subject %s successfully updated source directory to %s', __CLASS__, $this->getNewSourceDir($serial))
696
        );
697 1
    }
698
699
    /**
700
     * Return's the next source directory, which will be the target directory
701
     * of this subject, in most cases.
702
     *
703
     * @param string $serial The serial of the actual import
704
     *
705
     * @return string The new source directory
706
     */
707 1
    protected function getNewSourceDir($serial)
708
    {
709 1
        return sprintf('%s/%s', $this->getConfiguration()->getTargetDir(), $serial);
710
    }
711
712
    /**
713
     * Register the passed observer with the specific type.
714
     *
715
     * @param \TechDivision\Import\Observers\ObserverInterface $observer The observer to register
716
     * @param string                                           $type     The type to register the observer with
717
     *
718
     * @return void
719
     */
720 6
    public function registerObserver(ObserverInterface $observer, $type)
721
    {
722
723
        // query whether or not the array with the callbacks for the
724
        // passed type has already been initialized, or not
725 6
        if (!isset($this->observers[$type])) {
726 6
            $this->observers[$type] = array();
727
        }
728
729
        // append the callback with the instance of the passed type
730 6
        $this->observers[$type][] = $observer;
731 6
    }
732
733
    /**
734
     * Register the passed callback with the specific type.
735
     *
736
     * @param \TechDivision\Import\Callbacks\CallbackInterface $callback The subject to register the callbacks for
737
     * @param string                                           $type     The type to register the callback with
738
     *
739
     * @return void
740
     */
741 2
    public function registerCallback(CallbackInterface $callback, $type)
742
    {
743
744
        // query whether or not the array with the callbacks for the
745
        // passed type has already been initialized, or not
746 2
        if (!isset($this->callbacks[$type])) {
747 2
            $this->callbacks[$type] = array();
748
        }
749
750
        // append the callback with the instance of the passed type
751 2
        $this->callbacks[$type][] = $callback;
752 2
    }
753
754
    /**
755
     * Return's the array with callbacks for the passed type.
756
     *
757
     * @param string $type The type of the callbacks to return
758
     *
759
     * @return array The callbacks
760
     */
761 1
    public function getCallbacksByType($type)
762
    {
763
764
        // initialize the array for the callbacks
765 1
        $callbacks = array();
766
767
        // query whether or not callbacks for the type are available
768 1
        if (isset($this->callbacks[$type])) {
769 1
            $callbacks = $this->callbacks[$type];
770
        }
771
772
        // return the array with the type's callbacks
773 1
        return $callbacks;
774
    }
775
776
    /**
777
     * Return's the array with the available observers.
778
     *
779
     * @return array The observers
780
     */
781 6
    public function getObservers()
782
    {
783 6
        return $this->observers;
784
    }
785
786
    /**
787
     * Return's the array with the available callbacks.
788
     *
789
     * @return array The callbacks
790
     */
791 1
    public function getCallbacks()
792
    {
793 1
        return $this->callbacks;
794
    }
795
796
    /**
797
     * Return's the callback mappings for this subject.
798
     *
799
     * @return array The array with the subject's callback mappings
800
     */
801 2
    public function getCallbackMappings()
802
    {
803 2
        return $this->callbackMappings;
804
    }
805
806
    /**
807
     * Imports the content of the file with the passed filename.
808
     *
809
     *
810
     * @param string $serial   The serial of the actual import
811
     * @param string $filename The filename to process
812
     *
813
     * @return void
814
     * @throws \Exception Is thrown, if the import can't be processed
815
     */
816 5
    public function import($serial, $filename)
817
    {
818
819
        try {
820
            // stop processing, if the filename doesn't match
821 5
            if (!$this->match($filename)) {
822 1
                return;
823
            }
824
825
            // load the system logger instance
826 4
            $systemLogger = $this->getSystemLogger();
827
828
            // prepare the flag filenames
829 4
            $inProgressFilename = sprintf('%s.inProgress', $filename);
830 4
            $importedFilename = sprintf('%s.imported', $filename);
831 4
            $failedFilename = sprintf('%s.failed', $filename);
832
833
            // query whether or not the file has already been imported
834 4
            if ($this->isFile($failedFilename) ||
835 3
                $this->isFile($importedFilename) ||
836 4
                $this->isFile($inProgressFilename)
837
            ) {
838
                // log a debug message and exit
839 1
                $systemLogger->debug(sprintf('Import running, found inProgress file %s', $inProgressFilename));
840 1
                return;
841
            }
842
843
            // flag file as in progress
844 3
            $this->touch($inProgressFilename);
845
846
            // track the start time
847 3
            $startTime = microtime(true);
848
849
            // initialize the serial/filename
850 3
            $this->setSerial($serial);
851 3
            $this->setFilename($filename);
852
853
            // log a message that the file has to be imported
854 3
            $systemLogger->debug(sprintf('Now start importing file %s', $filename));
855
856
            // let the adapter process the file
857 3
            $this->getImportAdapter()->import(array($this, 'importRow'), $filename);
858
859
            // track the time needed for the import in seconds
860 1
            $endTime = microtime(true) - $startTime;
861
862
            // log a message that the file has successfully been imported
863 1
            $systemLogger->debug(sprintf('Successfully imported file %s in %f s', $filename, $endTime));
864
865
            // rename flag file, because import has been successfull
866 1
            $this->rename($inProgressFilename, $importedFilename);
867
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
868 2
        } catch (\Exception $e) {
869
            // rename the flag file, because import failed and write the stack trace
870 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...
871 2
            $this->write($failedFilename, $e->__toString());
872
873
            // do not wrap the exception if not already done
874 2
            if ($e instanceof WrappedColumnException) {
875 1
                throw $e;
876
            }
877
878
            // else wrap and throw the exception
879 1
            throw $this->wrapException(array(), $e);
880
        }
881 1
    }
882
883
    /**
884
     * This method queries whether or not the passed filename matches
885
     * the pattern, based on the subjects configured prefix.
886
     *
887
     * @param string $filename The filename to match
888
     *
889
     * @return boolean TRUE if the filename matches, else FALSE
890
     */
891 5
    protected function match($filename)
892
    {
893
894
        // prepare the pattern to query whether the file has to be processed or not
895 5
        $pattern = sprintf(
896 5
            '/^.*\/%s.*\\.%s$/',
897 5
            $this->getConfiguration()->getPrefix(),
898 5
            $this->getConfiguration()->getSuffix()
899
        );
900
901
        // stop processing, if the filename doesn't match
902 5
        return (boolean) preg_match($pattern, $filename);
903
    }
904
905
    /**
906
     * Imports the passed row into the database. If the import failed, the exception
907
     * will be catched and logged, but the import process will be continued.
908
     *
909
     * @param array $row The row with the data to be imported
910
     *
911
     * @return void
912
     */
913 7
    public function importRow(array $row)
914
    {
915
916
        // initialize the row
917 7
        $this->row = $row;
918
919
        // raise the line number and reset the skip row flag
920 7
        $this->lineNumber++;
921 7
        $this->skipRow = false;
922
923
        // initialize the headers with the columns from the first line
924 7
        if (sizeof($this->headers) === 0) {
925 1
            foreach ($this->row as $value => $key) {
926 1
                $this->headers[$this->mapAttributeCodeByHeaderMapping($key)] = $value;
927
            }
928 1
            return;
929
        }
930
931
        // process the observers
932 6
        foreach ($this->getObservers() as $observers) {
933
            // invoke the pre-import/import and post-import observers
934 6
            foreach ($observers as $observer) {
935
                // query whether or not we have to skip the row
936 6
                if ($this->skipRow) {
937 1
                    break;
938
                }
939
940
                // if not, set the subject and process the observer
941 6
                if ($observer instanceof ObserverInterface) {
942 6
                    $this->row = $observer->handle($this);
943
                }
944
            }
945
        }
946
947
        // log a debug message with the actual line nr/file information
948 6
        $this->getSystemLogger()->debug(
949 6
            sprintf(
950 6
                'Successfully processed row (operation: %s) in file %s on line %d',
951 6
                $this->operationName,
952 6
                $this->filename,
953 6
                $this->lineNumber
954
            )
955
        );
956 6
    }
957
958
    /**
959
     * Map the passed attribute code, if a header mapping exists and return the
960
     * mapped mapping.
961
     *
962
     * @param string $attributeCode The attribute code to map
963
     *
964
     * @return string The mapped attribute code, or the original one
965
     */
966 2
    public function mapAttributeCodeByHeaderMapping($attributeCode)
967
    {
968
969
        // load the header mappings
970 2
        $headerMappings = $this->getHeaderMappings();
971
972
        // query weather or not we've a mapping, if yes, map the attribute code
973 2
        if (isset($headerMappings[$attributeCode])) {
974 1
            $attributeCode = $headerMappings[$attributeCode];
975
        }
976
977
        // return the (mapped) attribute code
978 2
        return $attributeCode;
979
    }
980
981
    /**
982
     * Queries whether or not that the subject needs an OK file to be processed.
983
     *
984
     * @return boolean TRUE if the subject needs an OK file, else FALSE
985
     */
986 1
    public function isOkFileNeeded()
987
    {
988 1
        return $this->getConfiguration()->isOkFileNeeded();
989
    }
990
991
    /**
992
     * Return's the default store.
993
     *
994
     * @return array The default store
995
     */
996 4
    public function getDefaultStore()
997
    {
998 4
        return $this->defaultStore;
999
    }
1000
1001
    /**
1002
     * Set's the store view code the create the product/attributes for.
1003
     *
1004
     * @param string $storeViewCode The store view code
1005
     *
1006
     * @return void
1007
     */
1008 4
    public function setStoreViewCode($storeViewCode)
1009
    {
1010 4
        $this->storeViewCode = $storeViewCode;
1011 4
    }
1012
1013
    /**
1014
     * Return's the store view code the create the product/attributes for.
1015
     *
1016
     * @param string|null $default The default value to return, if the store view code has not been set
1017
     *
1018
     * @return string The store view code
1019
     */
1020 8
    public function getStoreViewCode($default = null)
1021
    {
1022
1023
        // return the store view code, if available
1024 8
        if ($this->storeViewCode != null) {
1025 4
            return $this->storeViewCode;
1026
        }
1027
1028
        // if NOT and a default code is available
1029 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...
1030
            // return the default value
1031 3
            return $default;
1032
        }
1033 1
    }
1034
1035
    /**
1036
     * Return's the store ID of the store with the passed store view code
1037
     *
1038
     * @param string $storeViewCode The store view code to return the store ID for
1039
     *
1040
     * @return integer The ID of the store with the passed ID
1041
     * @throws \Exception Is thrown, if the store with the actual code is not available
1042
     */
1043 4
    public function getStoreId($storeViewCode)
1044
    {
1045
1046
        // query whether or not, the requested store is available
1047 4
        if (isset($this->stores[$storeViewCode])) {
1048 3
            return (integer) $this->stores[$storeViewCode][MemberNames::STORE_ID];
1049
        }
1050
1051
        // throw an exception, if not
1052 1
        throw new \Exception(
1053 1
            sprintf(
1054 1
                'Found invalid store view code %s in file %s on line %d',
1055 1
                $storeViewCode,
1056 1
                $this->getFilename(),
1057 1
                $this->getLineNumber()
1058
            )
1059
        );
1060
    }
1061
1062
    /**
1063
     * Return's the store ID of the actual row, or of the default store
1064
     * if no store view code is set in the CSV file.
1065
     *
1066
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
1067
     *
1068
     * @return integer The ID of the actual store
1069
     * @throws \Exception Is thrown, if the store with the actual code is not available
1070
     */
1071 2
    public function getRowStoreId($default = null)
1072
    {
1073
1074
        // initialize the default store view code, if not passed
1075 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...
1076 2
            $defaultStore = $this->getDefaultStore();
1077 2
            $default = $defaultStore[MemberNames::CODE];
1078
        }
1079
1080
        // load the store view code the create the product/attributes for
1081 2
        return $this->getStoreId($this->getStoreViewCode($default));
1082
    }
1083
1084
    /**
1085
     * Prepare's the store view code in the subject.
1086
     *
1087
     * @return void
1088
     */
1089 2
    public function prepareStoreViewCode()
1090
    {
1091
1092
        // re-set the store view code
1093 2
        $this->setStoreViewCode(null);
1094
1095
        // initialize the store view code
1096 2
        if ($storeViewCode = $this->getValue(ColumnKeys::STORE_VIEW_CODE)) {
1097 2
            $this->setStoreViewCode($storeViewCode);
1098
        }
1099 2
    }
1100
1101
    /**
1102
     * Return's the root category for the actual view store.
1103
     *
1104
     * @return array The store's root category
1105
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
1106
     */
1107 2
    public function getRootCategory()
1108
    {
1109
1110
        // load the default store
1111 2
        $defaultStore = $this->getDefaultStore();
1112
1113
        // load the actual store view code
1114 2
        $storeViewCode = $this->getStoreViewCode($defaultStore[MemberNames::CODE]);
1115
1116
        // query weather or not we've a root category or not
1117 2
        if (isset($this->rootCategories[$storeViewCode])) {
1118 1
            return $this->rootCategories[$storeViewCode];
1119
        }
1120
1121
        // throw an exception if the root category is NOT available
1122 1
        throw new \Exception(sprintf('Root category for %s is not available', $storeViewCode));
1123
    }
1124
1125
    /**
1126
     * Return's the Magento configuration value.
1127
     *
1128
     * @param string  $path    The Magento path of the requested configuration value
1129
     * @param mixed   $default The default value that has to be returned, if the requested configuration value is not set
1130
     * @param string  $scope   The scope the configuration value has been set
1131
     * @param integer $scopeId The scope ID the configuration value has been set
1132
     *
1133
     * @return mixed The configuration value
1134
     * @throws \Exception Is thrown, if nor a value can be found or a default value has been passed
1135
     */
1136 5
    public function getCoreConfigData($path, $default = null, $scope = ScopeKeys::SCOPE_DEFAULT, $scopeId = 0)
1137
    {
1138
1139
        // initialize the core config data
1140
        $coreConfigData = array(
1141 5
            MemberNames::PATH => $path,
1142 5
            MemberNames::SCOPE => $scope,
1143 5
            MemberNames::SCOPE_ID => $scopeId
1144
        );
1145
1146
        // generate the UID from the passed data
1147 5
        $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1148
1149
        // iterate over the core config data and try to find the requested configuration value
1150 5
        if (isset($this->coreConfigData[$uniqueIdentifier])) {
1151 1
            return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1152
        }
1153
1154
        // query whether or not we've to query for the configuration value on fallback level 'websites' also
1155 4
        if ($scope === ScopeKeys::SCOPE_STORES) {
1156
            // query whether or not the website with the passed ID is available
1157 2
            foreach ($this->storeWebsites as $storeWebsite) {
1158 2
                if ($storeWebsite[MemberNames::WEBSITE_ID] === $scopeId) {
1159
                    // replace scope with 'websites' and website ID
1160 2
                    $coreConfigData = array_merge(
1161 2
                        $coreConfigData,
1162
                        array(
1163 2
                            MemberNames::SCOPE    => ScopeKeys::SCOPE_WEBSITES,
1164 2
                            MemberNames::SCOPE_ID => $storeWebsite[MemberNames::WEBSITE_ID]
1165
                        )
1166
                    );
1167
1168
                    // generate the UID from the passed data, merged with the 'websites' scope and ID
1169 2
                    $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1170
1171
                    // query whether or not, the configuration value on 'websites' level
1172 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...
1173 2
                        return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1174
                    }
1175
                }
1176
            }
1177
        }
1178
1179
        // replace scope with 'default' and scope ID '0'
1180 3
        $coreConfigData = array_merge(
1181 3
            $coreConfigData,
1182
            array(
1183 3
                MemberNames::SCOPE    => ScopeKeys::SCOPE_DEFAULT,
1184 3
                MemberNames::SCOPE_ID => 0
1185
            )
1186
        );
1187
1188
        // generate the UID from the passed data, merged with the 'default' scope and ID 0
1189 3
        $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
1190
1191
        // query whether or not, the configuration value on 'default' level
1192 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...
1193 1
            return $this->coreConfigData[$uniqueIdentifier][MemberNames::VALUE];
1194
        }
1195
1196
        // if not, return the passed default value
1197 2
        if ($default !== null) {
1198 1
            return $default;
1199
        }
1200
1201
        // throw an exception if no value can be found
1202
        // in the Magento configuration
1203 1
        throw new \Exception(
1204 1
            sprintf(
1205 1
                'Can\'t find a value for configuration "%s-%s-%d" in "core_config_data"',
1206 1
                $path,
1207 1
                $scope,
1208 1
                $scopeId
1209
            )
1210
        );
1211
    }
1212
1213
    /**
1214
     * Resolve the original column name for the passed one.
1215
     *
1216
     * @param string $columnName The column name that has to be resolved
1217
     *
1218
     * @return string|null The original column name
1219
     */
1220 2
    public function resolveOriginalColumnName($columnName)
1221
    {
1222
1223
        // try to load the original data
1224 2
        $originalData = $this->getOriginalData();
1225
1226
        // query whether or not original data is available
1227 2
        if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES])) {
1228
            // query whether or not the original column name is available
1229 1
            if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES][$columnName])) {
1230 1
                return $originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES][$columnName];
1231
            }
1232
1233
            // query whether or a wildcard column name is available
1234 1
            if (isset($originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES]['*'])) {
1235 1
                return $originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES]['*'];
1236
            }
1237
        }
1238
1239
        // return the original column name
1240 1
        return $columnName;
1241
    }
1242
1243
    /**
1244
     * Return's the original data if available, or an empty array.
1245
     *
1246
     * @return array The original data
1247
     */
1248 2
    public function getOriginalData()
1249
    {
1250
1251
        // initialize the array for the original data
1252 2
        $originalData = array();
1253
1254
        // query whether or not the column contains original data
1255 2
        if ($this->hasOriginalData()) {
1256
            // unerialize the original data from the column
1257 1
            $originalData = unserialize($this->row[$this->headers[ColumnKeys::ORIGINAL_DATA]]);
1258
        }
1259
1260
        // return an empty array, if not
1261 2
        return $originalData;
1262
    }
1263
1264
    /**
1265
     * Query's whether or not the actual column contains original data like
1266
     * filename, line number and column names.
1267
     *
1268
     * @return boolean TRUE if the actual column contains origin data, else FALSE
1269
     */
1270 3
    public function hasOriginalData()
1271
    {
1272 3
        return isset($this->headers[ColumnKeys::ORIGINAL_DATA]) && isset($this->row[$this->headers[ColumnKeys::ORIGINAL_DATA]]);
1273
    }
1274
1275
    /**
1276
     * Wraps the passed exeception into a new one by trying to resolve the original filname,
1277
     * line number and column names and use it for a detailed exception message.
1278
     *
1279
     * @param array      $columnNames The column names that should be resolved and wrapped
1280
     * @param \Exception $parent      The exception we want to wrap
1281
     * @param string     $className   The class name of the exception type we want to wrap the parent one
1282
     *
1283
     * @return \Exception the wrapped exception
1284
     */
1285 2
    public function wrapException(
1286
        array $columnNames = array(),
1287
        \Exception $parent = null,
1288
        $className = '\TechDivision\Import\Exceptions\WrappedColumnException'
1289
    ) {
1290
1291
        // initialize the message
1292 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...
1293
1294
        // query whether or not has been a result of invalid data of a previous column of a CSV file
1295 2
        if ($this->hasOriginalData()) {
1296
            // load the original data
1297 1
            $originalData = $this->getOriginalData();
1298
1299
            // replace old filename and line number of the original message
1300 1
            $message = $this->appendExceptionSuffix(
1301 1
                $this->stripExceptionSuffix($message),
1302 1
                $originalData[ColumnKeys::ORIGINAL_FILENAME],
1303 1
                $originalData[ColumnKeys::ORIGINAL_LINE_NUMBER]
1304
            );
1305
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
1306
        } else {
1307
            // append filename and line number to the original message
1308 1
            $message = $this->appendExceptionSuffix(
1309 1
                $this->stripExceptionSuffix($message),
1310 1
                $this->filename,
1311 1
                $this->lineNumber
1312
            );
1313
        }
1314
1315
        // query whether or not, column names has been passed
1316 2
        if (sizeof($columnNames) > 0) {
1317
            // prepare the original column names
1318 1
            $originalColumnNames = array();
1319 1
            foreach ($columnNames as $columnName) {
1320 1
                $originalColumnNames[] = $this->resolveOriginalColumnName($columnName);
1321
            }
1322
1323
            // append the column information
1324 1
            $message = sprintf('%s in column(s) %s', $message, implode(', ', $originalColumnNames));
1325
        }
1326
1327
        // create a new exception and wrap the parent one
1328 2
        return new $className($message, null, $parent);
1329
    }
1330
1331
    /**
1332
     * Strip's the exception suffix containing filename and line number from the
1333
     * passed message.
1334
     *
1335
     * @param string $message The message to strip the exception suffix from
1336
     *
1337
     * @return mixed The message without the exception suffix
1338
     */
1339 2
    public function stripExceptionSuffix($message)
1340
    {
1341 2
        return str_replace($this->appendExceptionSuffix(), '', $message);
1342
    }
1343
1344
    /**
1345
     * Append's the exception suffix containing filename and line number to the
1346
     * passed message. If no message has been passed, only the suffix will be
1347
     * returned
1348
     *
1349
     * @param string|null $message    The message to append the exception suffix to
1350
     * @param string|null $filename   The filename used to create the suffix
1351
     * @param string|null $lineNumber The line number used to create the suffx
1352
     *
1353
     * @return string The message with the appended exception suffix
1354
     */
1355 7
    public function appendExceptionSuffix($message = null, $filename = null, $lineNumber = null)
1356
    {
1357
1358
        // query whether or not a filename has been passed
1359 7
        if ($filename === null) {
1360 7
            $filename = $this->getFilename();
1361
        }
1362
1363
        // query whether or not a line number has been passed
1364 7
        if ($lineNumber === null) {
1365 7
            $lineNumber = $this->getLineNumber();
1366
        }
1367
1368
        // if no message has been passed, only return the suffix
1369 7
        if ($message === null) {
1370 2
            return sprintf(' in file %s on line %d', $filename, $lineNumber);
1371
        }
1372
1373
        // concatenate the message with the suffix and return it
1374 7
        return sprintf('%s in file %s on line %d', $message, $filename, $lineNumber);
1375
    }
1376
1377
    /**
1378
     * Raises the value for the counter with the passed key by one.
1379
     *
1380
     * @param mixed $counterName The name of the counter to raise
1381
     *
1382
     * @return integer The counter's new value
1383
     */
1384 1
    public function raiseCounter($counterName)
1385
    {
1386
1387
        // raise the counter with the passed name
1388 1
        return $this->getRegistryProcessor()->raiseCounter(
1389 1
            $this->getSerial(),
1390 1
            $counterName
1391
        );
1392
    }
1393
1394
    /**
1395
     * Merge the passed array into the status of the actual import.
1396
     *
1397
     * @param array $status The status information to be merged
1398
     *
1399
     * @return void
1400
     */
1401 1
    public function mergeAttributesRecursive(array $status)
1402
    {
1403
1404
        // merge the passed status
1405 1
        $this->getRegistryProcessor()->mergeAttributesRecursive(
1406 1
            $this->getSerial(),
1407 1
            $status
1408
        );
1409 1
    }
1410
}
1411