Completed
Pull Request — master (#7)
by Tim
03:19
created

AbstractSubject::getSystemLogger()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * TechDivision\Import\Subjects\AbstractSubject
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2016 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/techdivision/import
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Subjects;
22
23
use Psr\Log\LoggerInterface;
24
use League\Flysystem\Filesystem;
25
use League\Flysystem\Adapter\Local;
26
use League\Flysystem\FilesystemInterface;
27
use Goodby\CSV\Import\Standard\Lexer;
28
use Goodby\CSV\Import\Standard\LexerConfig;
29
use Goodby\CSV\Import\Standard\Interpreter;
30
use TechDivision\Import\Utils\ConfigurationKeys;
31
use TechDivision\Import\Services\RegistryProcessor;
32
use TechDivision\Import\Callbacks\CallbackInterface;
33
use TechDivision\Import\Observers\ObserverInterface;
34
use TechDivision\Import\Services\RegistryProcessorInterface;
35
use TechDivision\Import\Configuration\SubjectInterface as SubjectConfigurationInterface;
36
37
/**
38
 * An abstract subject implementation.
39
 *
40
 * @author    Tim Wagner <[email protected]>
41
 * @copyright 2016 TechDivision GmbH <[email protected]>
42
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
43
 * @link      https://github.com/techdivision/import
44
 * @link      http://www.techdivision.com
45
 */
46
abstract class AbstractSubject implements SubjectInterface
47
{
48
49
    /**
50
     * The root directory for the virtual filesystem.
51
     *
52
     * @var string
53
     */
54
    protected $rootDir;
55
56
    /**
57
     * The system configuration.
58
     *
59
     * @var \TechDivision\Import\Configuration\SubjectInterface
60
     */
61
    protected $configuration;
62
63
    /**
64
     * The system logger implementation.
65
     *
66
     * @var \Psr\Log\LoggerInterface
67
     */
68
    protected $systemLogger;
69
70
    /**
71
     * The RegistryProcessor instance to handle running threads.
72
     *
73
     * @var \TechDivision\Import\Services\RegistryProcessorInterface
74
     */
75
    protected $registryProcessor;
76
77
    /**
78
     * The actions unique serial.
79
     *
80
     * @var string
81
     */
82
    protected $serial;
83
84
    /**
85
     * Array with the subject's observers.
86
     *
87
     * @var array
88
     */
89
    protected $observers = array();
90
91
    /**
92
     * Array with the subject's callbacks.
93
     *
94
     * @var array
95
     */
96
    protected $callbacks = array();
97
98
    /**
99
     * Contain's the column names from the header line.
100
     *
101
     * @var array
102
     */
103
    protected $headers = array();
104
105
    /**
106
     * The virtual filesystem instance.
107
     *
108
     * @var \League\Flysystem\FilesystemInterface
109
     */
110
    protected $filesystem;
111
112
    /**
113
     * Set's the array containing header row.
114
     *
115
     * @param array $headers The array with the header row
116
     *
117
     * @return void
118
     */
119
    public function setHeaders(array $headers)
120
    {
121
        $this->headers = $headers;
122
    }
123
124
    /**
125
     * Return's the array containing header row.
126
     *
127
     * @return array The array with the header row
128
     */
129
    public function getHeaders()
130
    {
131
        return $this->headers;
132
    }
133
134
    /**
135
     * Set's the system configuration.
136
     *
137
     * @param \TechDivision\Import\Configuration\Subject $configuration The system configuration
138
     *
139
     * @return void
140
     */
141
    public function setConfiguration(SubjectConfigurationInterface $configuration)
142
    {
143
        $this->configuration = $configuration;
144
    }
145
146
    /**
147
     * Return's the system configuration.
148
     *
149
     * @return \TechDivision\Import\Configuration\SubjectInterface The system configuration
150
     */
151
    public function getConfiguration()
152
    {
153
        return $this->configuration;
154
    }
155
156
    /**
157
     * Set's the system logger.
158
     *
159
     * @param \Psr\Log\LoggerInterface $systemLogger The system logger
160
     *
161
     * @return void
162
     */
163
    public function setSystemLogger(LoggerInterface $systemLogger)
164
    {
165
        $this->systemLogger = $systemLogger;
166
    }
167
168
    /**
169
     * Return's the system logger.
170
     *
171
     * @return \Psr\Log\LoggerInterface The system logger instance
172
     */
173
    public function getSystemLogger()
174
    {
175
        return $this->systemLogger;
176
    }
177
178
    /**
179
     * Set's root directory for the virtual filesystem.
180
     *
181
     * @param string $rootDir The root directory for the virtual filesystem
182
     *
183
     * @return void
184
     */
185
    public function setRootDir($rootDir)
186
    {
187
        $this->rootDir = $rootDir;
188
    }
189
190
    /**
191
     * Return's the root directory for the virtual filesystem.
192
     *
193
     * @return string The root directory for the virtual filesystem
194
     */
195
    public function getRootDir()
196
    {
197
        return $this->rootDir;
198
    }
199
200
    /**
201
     * Set's the virtual filesystem instance.
202
     *
203
     * @param \League\Flysystem\FilesystemInterface $filesystem The filesystem instance
204
     *
205
     * @return void
206
     */
207
    public function setFilesystem(FilesystemInterface $filesystem)
208
    {
209
        $this->filesystem = $filesystem;
210
    }
211
212
    /**
213
     * Return's the virtual filesystem instance.
214
     *
215
     * @return \League\Flysystem\FilesystemInterface The filesystem instance
216
     */
217
    public function getFilesystem()
218
    {
219
        return $this->filesystem;
220
    }
221
222
    /**
223
     * Sets's the RegistryProcessor instance to handle the running threads.
224
     *
225
     * @param \TechDivision\Import\Services\RegistryProcessorInterface $registryProcessor The registry processor instance
226
     *
227
     * @return void
228
     */
229
    public function setRegistryProcessor(RegistryProcessorInterface $registryProcessor)
230
    {
231
        $this->registryProcessor = $registryProcessor;
232
    }
233
234
    /**
235
     * Return's the RegistryProcessor instance to handle the running threads.
236
     *
237
     * @return \TechDivision\Import\Services\RegistryProcessorInterface The registry processor instance
238
     */
239
    public function getRegistryProcessor()
240
    {
241
        return $this->registryProcessor;
242
    }
243
244
    /**
245
     * Set's the unique serial for this import process.
246
     *
247
     * @param string $serial The unique serial
248
     *
249
     * @return void
250
     */
251
    public function setSerial($serial)
252
    {
253
        $this->serial = $serial;
254
    }
255
256
    /**
257
     * Return's the unique serial for this import process.
258
     *
259
     * @return string The unique serial
260
     */
261
    public function getSerial()
262
    {
263
        return $this->serial;
264
    }
265
266
    /**
267
     * Return's the source date format to use.
268
     *
269
     * @return string The source date format
270
     */
271
    public function getSourceDateFormat()
272
    {
273
        return $this->getConfiguration()->getSourceDateFormat();
274
    }
275
276
    /**
277
     * Return's the initialized PDO connection.
278
     *
279
     * @return \PDO The initialized PDO connection
280
     */
281
    public function getConnection()
282
    {
283
        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...
284
    }
285
286
    /**
287
     * Intializes the previously loaded global data for exactly one bunch.
288
     *
289
     * @return void
290
     * @see \Importer\Csv\Actions\ProductImportAction::prepare()
291
     */
292
    public function setUp()
293
    {
294
295
        // initialize the filesystems root directory
296
        $this->setRootDir(
297
            $this->getConfiguration()->getParam(ConfigurationKeys::ROOT_DIRECTORY, getcwd())
298
        );
299
300
        // initialize the filesystem
301
        $this->setFilesystem(new Filesystem(new Local($this->getRootDir())));
302
    }
303
304
    /**
305
     * This method tries to resolve the passed path and returns it. If the path
306
     * is relative, the actual working directory will be prepended.
307
     *
308
     * @param string $path The path to be resolved
309
     *
310
     * @return string The resolved path
311
     * @throws \InvalidArgumentException Is thrown, if the path can not be resolved
312
     */
313
    public function resolvePath($path)
314
    {
315
        // if we've an absolute path, return it immediately
316
        if ($this->getFilesystem()->has($path)) {
317
            return $path;
318
        }
319
320
        // try to prepend the actual working directory, assuming we've a relative path
321
        if ($this->getFilesystem()->has($path = getcwd() . DIRECTORY_SEPARATOR . $path)) {
322
            return $path;
323
        }
324
325
        // throw an exception if the passed directory doesn't exists
326
        throw new \InvalidArgumentException(
327
            sprintf('Directory %s doesn\'t exist', $path)
328
        );
329
    }
330
331
    /**
332
     * Clean up the global data after importing the variants.
333
     *
334
     * @return void
335
     */
336
    public function tearDown()
337
    {
338
    }
339
340
    /**
341
     * Register the passed observer with the specific type.
342
     *
343
     * @param \TechDivision\Import\Observers\ObserverInterface $observer The observer to register
344
     * @param string                                           $type     The type to register the observer with
345
     *
346
     * @return void
347
     */
348
    public function registerObserver(ObserverInterface $observer, $type)
349
    {
350
351
        // query whether or not the array with the callbacks for the
352
        // passed type has already been initialized, or not
353
        if (!isset($this->observers[$type])) {
354
            $this->observers[$type] = array();
355
        }
356
357
        // append the callback with the instance of the passed type
358
        $this->observers[$type][] = $observer;
359
    }
360
361
    /**
362
     * Register the passed callback with the specific type.
363
     *
364
     * @param \TechDivision\Import\Callbacks\CallbackInterface $callback The subject to register the callbacks for
365
     * @param string                                           $type     The type to register the callback with
366
     *
367
     * @return void
368
     */
369
    public function registerCallback(CallbackInterface $callback, $type)
370
    {
371
372
        // query whether or not the array with the callbacks for the
373
        // passed type has already been initialized, or not
374
        if (!isset($this->callbacks[$type])) {
375
            $this->callbacks[$type] = array();
376
        }
377
378
        // append the callback with the instance of the passed type
379
        $this->callbacks[$type][] = $callback;
380
    }
381
382
    /**
383
     * Return's the array with callbacks for the passed type.
384
     *
385
     * @param string $type The type of the callbacks to return
386
     *
387
     * @return array The callbacks
388
     */
389
    public function getCallbacksByType($type)
390
    {
391
392
        // initialize the array for the callbacks
393
        $callbacks = array();
394
395
        // query whether or not callbacks for the type are available
396
        if (isset($this->callbacks[$type])) {
397
            $callbacks = $this->callbacks[$type];
398
        }
399
400
        // return the array with the type's callbacks
401
        return $callbacks;
402
    }
403
404
    /**
405
     * Return's the array with the available observers.
406
     *
407
     * @return array The observers
408
     */
409
    public function getObservers()
410
    {
411
        return $this->observers;
412
    }
413
414
    /**
415
     * Return's the array with the available callbacks.
416
     *
417
     * @return array The callbacks
418
     */
419
    public function getCallbacks()
420
    {
421
        return $this->callbacks;
422
    }
423
424
    /**
425
     * Imports the content of the file with the passed filename.
426
     *
427
     * @param string $serial   The unique process serial
428
     * @param string $filename The filename to process
429
     *
430
     * @return void
431
     */
432
    public function import($serial, $filename)
433
    {
434
435
        try {
436
            // track the start time
437
            $startTime = microtime(true);
438
439
            // initialize serial and file UID
440
            $this->setSerial($serial);
441
442
            // load the system logger
443
            $systemLogger = $this->getSystemLogger();
444
445
            // initialize the global global data to import a bunch
446
            $this->setUp();
447
448
            // initialize the lexer instance itself
449
            $lexer = new Lexer($this->getLexerConfig());
450
451
            // initialize the interpreter
452
            $interpreter = new Interpreter();
453
            $interpreter->addObserver(array($this, 'importRow'));
454
455
            // query whether or not we want to use the strict mode
456
            if (!$this->getConfiguration()->isStrictMode()) {
457
                $interpreter->unstrict();
458
            }
459
460
            // log a message that the file has to be imported
461
            $systemLogger->debug(sprintf('Now start importing file %s', $filename));
462
463
            // parse the CSV file to be imported
464
            $lexer->parse($filename, $interpreter);
465
466
            // track the time needed for the import in seconds
467
            $endTime = microtime(true) - $startTime;
468
469
            // log a message that the file has successfully been imported
470
            $systemLogger->debug(sprintf('Succesfully imported file %s in %f s', $filename, $endTime));
471
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
472
        } catch (\Exception $e) {
473
            // log a message with the stack trace
474
            $systemLogger->error($e->__toString());
0 ignored issues
show
Bug introduced by
The variable $systemLogger 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...
475
476
            // re-throw the exception
477
            throw $e;
478
        }
479
480
        // clean up the data after importing the bunch
481
        $this->tearDown();
482
    }
483
484
    /**
485
     * Initialize and return the lexer configuration.
486
     *
487
     * @return \Goodby\CSV\Import\Standard\LexerConfig The lexer configuration
488
     */
489
    protected function getLexerConfig()
490
    {
491
492
        // initialize the lexer configuration
493
        $config = new LexerConfig();
494
495
        // query whether or not a delimiter character has been configured
496
        if ($delimiter = $this->getConfiguration()->getDelimiter()) {
497
            $config->setDelimiter($delimiter);
498
        }
499
500
        // query whether or not a custom escape character has been configured
501
        if ($escape = $this->getConfiguration()->getEscape()) {
502
            $config->setEscape($escape);
503
        }
504
505
        // query whether or not a custom enclosure character has been configured
506
        if ($enclosure = $this->getConfiguration()->getEnclosure()) {
507
            $config->setEnclosure($enclosure);
508
        }
509
510
        // query whether or not a custom source charset has been configured
511
        if ($fromCharset = $this->getConfiguration()->getFromCharset()) {
512
            $config->setFromCharset($fromCharset);
513
        }
514
515
        // query whether or not a custom target charset has been configured
516
        if ($toCharset = $this->getConfiguration()->getToCharset()) {
517
            $config->setToCharset($toCharset);
518
        }
519
520
        // return the lexer configuratio
521
        return $config;
522
    }
523
524
    /**
525
     * Imports the passed row into the database.
526
     *
527
     * If the import failed, the exception will be catched and logged,
528
     * but the import process will be continued.
529
     *
530
     * @param array $row The row with the data to be imported
531
     *
532
     * @return void
533
     */
534
    public function importRow(array $row)
535
    {
536
537
        // initialize the headers with the columns from the first line
538
        if (sizeof($this->getHeaders()) === 0) {
539
            $this->setHeaders(array_flip($row));
540
            return;
541
        }
542
543
        // process the observers
544
        foreach ($this->getObservers() as $observers) {
545
            // invoke the pre-import/import and post-import observers
546
            foreach ($observers as $observer) {
547
                if ($observer instanceof ObserverInterface) {
548
                    $row = $observer->handle($row);
0 ignored issues
show
Bug introduced by
The method handle() does not seem to exist on object<TechDivision\Impo...vers\ObserverInterface>.

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...
549
                }
550
            }
551
        }
552
    }
553
}
554