Completed
Push — master ( 35f66c...a92d66 )
by Tim
11s
created

AbstractSubject::getMultipleFieldDelimiter()   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
c 0
b 0
f 0
ccs 0
cts 4
cp 0
rs 10
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
     * The name of the file to be imported.
86
     *
87
     * @var string
88
     */
89
    protected $filename;
90
91
    /**
92
     * Array with the subject's observers.
93
     *
94
     * @var array
95
     */
96
    protected $observers = array();
97
98
    /**
99
     * Array with the subject's callbacks.
100
     *
101
     * @var array
102
     */
103
    protected $callbacks = array();
104
105
    /**
106
     * Contain's the column names from the header line.
107
     *
108
     * @var array
109
     */
110
    protected $headers = array();
111
112
    /**
113
     * The virtual filesystem instance.
114
     *
115
     * @var \League\Flysystem\FilesystemInterface
116
     */
117
    protected $filesystem;
118
119
    /**
120
     * Set's the array containing header row.
121
     *
122
     * @param array $headers The array with the header row
123
     *
124
     * @return void
125
     */
126
    public function setHeaders(array $headers)
127
    {
128
        $this->headers = $headers;
129
    }
130
131
    /**
132
     * Return's the array containing header row.
133
     *
134
     * @return array The array with the header row
135
     */
136
    public function getHeaders()
137
    {
138
        return $this->headers;
139
    }
140
141
    /**
142
     * Queries whether or not the header with the passed name is available.
143
     *
144
     * @param string $name The header name to query
145
     *
146
     * @return boolean TRUE if the header is available, else FALSE
147
     */
148
    public function hasHeader($name)
149
    {
150
        return isset($this->headers[$name]);
151
    }
152
153
    /**
154
     * Return's the header value for the passed name.
155
     *
156
     * @param string $name The name of the header to return the value for
157
     *
158
     * @return mixed The header value
159
     * \InvalidArgumentException Is thrown, if the header with the passed name is NOT available
160
     */
161
    public function getHeader($name)
162
    {
163
164
        // query whether or not, the header is available
165
        if (isset($this->headers[$name])) {
166
            return $this->headers[$name];
167
        }
168
169
        // throw an exception, if not
170
        throw new \InvalidArgumentException(sprintf('Header %s is not available', $name));
171
    }
172
173
    /**
174
     * Add's the header with the passed name and position, if not NULL.
175
     *
176
     * @param string $name The header name to add
177
     *
178
     * @return integer The new headers position
179
     */
180
    public function addHeader($name)
181
    {
182
183
        // add the header
184
        $this->headers[$name] = $position = sizeof($this->headers);
185
186
        // return the new header's position
187
        return $position;
188
    }
189
190
    /**
191
     * Set's the system configuration.
192
     *
193
     * @param \TechDivision\Import\Configuration\Subject $configuration The system configuration
194
     *
195
     * @return void
196
     */
197
    public function setConfiguration(SubjectConfigurationInterface $configuration)
198
    {
199
        $this->configuration = $configuration;
200
    }
201
202
    /**
203
     * Return's the system configuration.
204
     *
205
     * @return \TechDivision\Import\Configuration\SubjectInterface The system configuration
206
     */
207
    public function getConfiguration()
208
    {
209
        return $this->configuration;
210
    }
211
212
    /**
213
     * Set's the system logger.
214
     *
215
     * @param \Psr\Log\LoggerInterface $systemLogger The system logger
216
     *
217
     * @return void
218
     */
219
    public function setSystemLogger(LoggerInterface $systemLogger)
220
    {
221
        $this->systemLogger = $systemLogger;
222
    }
223
224
    /**
225
     * Return's the system logger.
226
     *
227
     * @return \Psr\Log\LoggerInterface The system logger instance
228
     */
229
    public function getSystemLogger()
230
    {
231
        return $this->systemLogger;
232
    }
233
234
    /**
235
     * Set's root directory for the virtual filesystem.
236
     *
237
     * @param string $rootDir The root directory for the virtual filesystem
238
     *
239
     * @return void
240
     */
241
    public function setRootDir($rootDir)
242
    {
243
        $this->rootDir = $rootDir;
244
    }
245
246
    /**
247
     * Return's the root directory for the virtual filesystem.
248
     *
249
     * @return string The root directory for the virtual filesystem
250
     */
251
    public function getRootDir()
252
    {
253
        return $this->rootDir;
254
    }
255
256
    /**
257
     * Set's the virtual filesystem instance.
258
     *
259
     * @param \League\Flysystem\FilesystemInterface $filesystem The filesystem instance
260
     *
261
     * @return void
262
     */
263
    public function setFilesystem(FilesystemInterface $filesystem)
264
    {
265
        $this->filesystem = $filesystem;
266
    }
267
268
    /**
269
     * Return's the virtual filesystem instance.
270
     *
271
     * @return \League\Flysystem\FilesystemInterface The filesystem instance
272
     */
273
    public function getFilesystem()
274
    {
275
        return $this->filesystem;
276
    }
277
278
    /**
279
     * Sets's the RegistryProcessor instance to handle the running threads.
280
     *
281
     * @param \TechDivision\Import\Services\RegistryProcessorInterface $registryProcessor The registry processor instance
282
     *
283
     * @return void
284
     */
285
    public function setRegistryProcessor(RegistryProcessorInterface $registryProcessor)
286
    {
287
        $this->registryProcessor = $registryProcessor;
288
    }
289
290
    /**
291
     * Return's the RegistryProcessor instance to handle the running threads.
292
     *
293
     * @return \TechDivision\Import\Services\RegistryProcessorInterface The registry processor instance
294
     */
295
    public function getRegistryProcessor()
296
    {
297
        return $this->registryProcessor;
298
    }
299
300
    /**
301
     * Set's the unique serial for this import process.
302
     *
303
     * @param string $serial The unique serial
304
     *
305
     * @return void
306
     */
307
    public function setSerial($serial)
308
    {
309
        $this->serial = $serial;
310
    }
311
312
    /**
313
     * Return's the unique serial for this import process.
314
     *
315
     * @return string The unique serial
316
     */
317
    public function getSerial()
318
    {
319
        return $this->serial;
320
    }
321
322
    /**
323
     * Set's the name of the file to import
324
     *
325
     * @param string $filename The filename
326
     *
327
     * @return void
328
     */
329
    public function setFilename($filename)
330
    {
331
        $this->filename = $filename;
332
    }
333
334
    /**
335
     * Return's the name of the file to import.
336
     *
337
     * @return string The filename
338
     */
339
    public function getFilename()
340
    {
341
        return $this->filename;
342
    }
343
344
    /**
345
     * Return's the source date format to use.
346
     *
347
     * @return string The source date format
348
     */
349
    public function getSourceDateFormat()
350
    {
351
        return $this->getConfiguration()->getSourceDateFormat();
352
    }
353
354
    /**
355
     * Return's the multiple field delimiter character to use, default value is comma (,).
356
     *
357
     * @return string The multiple field delimiter character
358
     */
359
    public function getMultipleFieldDelimiter()
360
    {
361
        return $this->getConfiguration()->getMultipleFieldDelimiter();
0 ignored issues
show
Bug introduced by
The method getMultipleFieldDelimiter() does not seem to exist on object<TechDivision\Impo...ation\SubjectInterface>.

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...
362
    }
363
364
    /**
365
     * Return's the initialized PDO connection.
366
     *
367
     * @return \PDO The initialized PDO connection
368
     */
369
    public function getConnection()
370
    {
371
        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...
372
    }
373
374
    /**
375
     * Intializes the previously loaded global data for exactly one bunch.
376
     *
377
     * @return void
378
     * @see \Importer\Csv\Actions\ProductImportAction::prepare()
379
     */
380
    public function setUp()
381
    {
382
383
        // initialize the filesystems root directory
384
        $this->setRootDir(
385
            $this->getConfiguration()->getParam(ConfigurationKeys::ROOT_DIRECTORY, getcwd())
386
        );
387
388
        // initialize the filesystem
389
        $this->setFilesystem(new Filesystem(new Local($this->getRootDir())));
390
    }
391
392
    /**
393
     * This method tries to resolve the passed path and returns it. If the path
394
     * is relative, the actual working directory will be prepended.
395
     *
396
     * @param string $path The path to be resolved
397
     *
398
     * @return string The resolved path
399
     * @throws \InvalidArgumentException Is thrown, if the path can not be resolved
400
     */
401
    public function resolvePath($path)
402
    {
403
        // if we've an absolute path, return it immediately
404
        if ($this->getFilesystem()->has($path)) {
405
            return $path;
406
        }
407
408
        // try to prepend the actual working directory, assuming we've a relative path
409
        if ($this->getFilesystem()->has($path = getcwd() . DIRECTORY_SEPARATOR . $path)) {
410
            return $path;
411
        }
412
413
        // throw an exception if the passed directory doesn't exists
414
        throw new \InvalidArgumentException(
415
            sprintf('Directory %s doesn\'t exist', $path)
416
        );
417
    }
418
419
    /**
420
     * Clean up the global data after importing the variants.
421
     *
422
     * @return void
423
     */
424
    public function tearDown()
425
    {
426
    }
427
428
    /**
429
     * Register the passed observer with the specific type.
430
     *
431
     * @param \TechDivision\Import\Observers\ObserverInterface $observer The observer to register
432
     * @param string                                           $type     The type to register the observer with
433
     *
434
     * @return void
435
     */
436
    public function registerObserver(ObserverInterface $observer, $type)
437
    {
438
439
        // query whether or not the array with the callbacks for the
440
        // passed type has already been initialized, or not
441
        if (!isset($this->observers[$type])) {
442
            $this->observers[$type] = array();
443
        }
444
445
        // append the callback with the instance of the passed type
446
        $this->observers[$type][] = $observer;
447
    }
448
449
    /**
450
     * Register the passed callback with the specific type.
451
     *
452
     * @param \TechDivision\Import\Callbacks\CallbackInterface $callback The subject to register the callbacks for
453
     * @param string                                           $type     The type to register the callback with
454
     *
455
     * @return void
456
     */
457
    public function registerCallback(CallbackInterface $callback, $type)
458
    {
459
460
        // query whether or not the array with the callbacks for the
461
        // passed type has already been initialized, or not
462
        if (!isset($this->callbacks[$type])) {
463
            $this->callbacks[$type] = array();
464
        }
465
466
        // append the callback with the instance of the passed type
467
        $this->callbacks[$type][] = $callback;
468
    }
469
470
    /**
471
     * Return's the array with callbacks for the passed type.
472
     *
473
     * @param string $type The type of the callbacks to return
474
     *
475
     * @return array The callbacks
476
     */
477
    public function getCallbacksByType($type)
478
    {
479
480
        // initialize the array for the callbacks
481
        $callbacks = array();
482
483
        // query whether or not callbacks for the type are available
484
        if (isset($this->callbacks[$type])) {
485
            $callbacks = $this->callbacks[$type];
486
        }
487
488
        // return the array with the type's callbacks
489
        return $callbacks;
490
    }
491
492
    /**
493
     * Return's the array with the available observers.
494
     *
495
     * @return array The observers
496
     */
497
    public function getObservers()
498
    {
499
        return $this->observers;
500
    }
501
502
    /**
503
     * Return's the array with the available callbacks.
504
     *
505
     * @return array The callbacks
506
     */
507
    public function getCallbacks()
508
    {
509
        return $this->callbacks;
510
    }
511
512
    /**
513
     * Imports the content of the file with the passed filename.
514
     *
515
     * @param string $serial   The unique process serial
516
     * @param string $filename The filename to process
517
     *
518
     * @return void
519
     * @throws \Exception Is thrown, if the import can't be processed
520
     */
521
    public function import($serial, $filename)
522
    {
523
524
        try {
525
            // track the start time
526
            $startTime = microtime(true);
527
528
            // initialize serial and filename
529
            $this->setSerial($serial);
530
            $this->setFilename($filename);
531
532
            // load the system logger
533
            $systemLogger = $this->getSystemLogger();
534
535
            // initialize the global global data to import a bunch
536
            $this->setUp();
537
538
            // initialize the lexer instance itself
539
            $lexer = new Lexer($this->getLexerConfig());
540
541
            // initialize the interpreter
542
            $interpreter = new Interpreter();
543
            $interpreter->addObserver(array($this, 'importRow'));
544
545
            // query whether or not we want to use the strict mode
546
            if (!$this->getConfiguration()->isStrictMode()) {
547
                $interpreter->unstrict();
548
            }
549
550
            // log a message that the file has to be imported
551
            $systemLogger->debug(sprintf('Now start importing file %s', $filename));
552
553
            // parse the CSV file to be imported
554
            $lexer->parse($filename, $interpreter);
555
556
            // track the time needed for the import in seconds
557
            $endTime = microtime(true) - $startTime;
558
559
            // clean up the data after importing the bunch
560
            $this->tearDown();
561
562
            // log a message that the file has successfully been imported
563
            $systemLogger->debug(sprintf('Succesfully imported file %s in %f s', $filename, $endTime));
564
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
565
        } catch (\Exception $e) {
566
            // clean up the data after importing the bunch
567
            $this->tearDown();
568
569
            // re-throw the exception
570
            throw $e;
571
        }
572
    }
573
574
    /**
575
     * Initialize and return the lexer configuration.
576
     *
577
     * @return \Goodby\CSV\Import\Standard\LexerConfig The lexer configuration
578
     */
579
    protected function getLexerConfig()
580
    {
581
582
        // initialize the lexer configuration
583
        $config = new LexerConfig();
584
585
        // query whether or not a delimiter character has been configured
586
        if ($delimiter = $this->getConfiguration()->getDelimiter()) {
587
            $config->setDelimiter($delimiter);
588
        }
589
590
        // query whether or not a custom escape character has been configured
591
        if ($escape = $this->getConfiguration()->getEscape()) {
592
            $config->setEscape($escape);
593
        }
594
595
        // query whether or not a custom enclosure character has been configured
596
        if ($enclosure = $this->getConfiguration()->getEnclosure()) {
597
            $config->setEnclosure($enclosure);
598
        }
599
600
        // query whether or not a custom source charset has been configured
601
        if ($fromCharset = $this->getConfiguration()->getFromCharset()) {
602
            $config->setFromCharset($fromCharset);
603
        }
604
605
        // query whether or not a custom target charset has been configured
606
        if ($toCharset = $this->getConfiguration()->getToCharset()) {
607
            $config->setToCharset($toCharset);
608
        }
609
610
        // return the lexer configuratio
611
        return $config;
612
    }
613
614
    /**
615
     * Imports the passed row into the database.
616
     *
617
     * If the import failed, the exception will be catched and logged,
618
     * but the import process will be continued.
619
     *
620
     * @param array $row The row with the data to be imported
621
     *
622
     * @return void
623
     */
624
    public function importRow(array $row)
625
    {
626
627
        // initialize the headers with the columns from the first line
628
        if (sizeof($this->getHeaders()) === 0) {
629
            $this->setHeaders(array_flip($row));
630
            return;
631
        }
632
633
        // process the observers
634
        foreach ($this->getObservers() as $observers) {
635
            // invoke the pre-import/import and post-import observers
636
            foreach ($observers as $observer) {
637
                if ($observer instanceof ObserverInterface) {
638
                    $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...
639
                }
640
            }
641
        }
642
    }
643
}
644