Completed
Push — 16.x ( f4dd8c...ba0a20 )
by Tim
01:27
created

Simple::process()   F

Complexity

Conditions 12
Paths 490

Size

Total Lines 168

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 168
rs 2.8066
c 0
b 0
f 0
cc 12
nc 490
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * TechDivision\Import\App\Simple
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-app-simple
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\App;
22
23
use Psr\Log\LogLevel;
24
use League\Event\EmitterInterface;
25
use Doctrine\Common\Collections\Collection;
26
use Symfony\Component\Console\Output\OutputInterface;
27
use Symfony\Component\Console\Helper\FormatterHelper;
28
use Symfony\Component\DependencyInjection\TaggedContainerInterface;
29
use TechDivision\Import\Utils\LoggerKeys;
30
use TechDivision\Import\Utils\EventNames;
31
use TechDivision\Import\ApplicationInterface;
32
use TechDivision\Import\App\Utils\DependencyInjectionKeys;
33
use TechDivision\Import\ConfigurationInterface;
34
use TechDivision\Import\Exceptions\LineNotFoundException;
35
use TechDivision\Import\Exceptions\FileNotFoundException;
36
use TechDivision\Import\Exceptions\ImportAlreadyRunningException;
37
use TechDivision\Import\Services\ImportProcessorInterface;
38
use TechDivision\Import\Services\RegistryProcessorInterface;
39
use TechDivision\Import\Exceptions\ApplicationStoppedException;
40
41
/**
42
 * The M2IF - Simple Application implementation.
43
 *
44
 * This is a example application implementation that should give developers an impression
45
 * on how the M2IF could be used to implement their own Magento 2 importer.
46
 *
47
 * @author    Tim Wagner <[email protected]>
48
 * @copyright 2016 TechDivision GmbH <[email protected]>
49
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
50
 * @link      https://github.com/techdivision/import-app-simple
51
 * @link      http://www.techdivision.com
52
 */
53
class Simple implements ApplicationInterface
54
{
55
56
    /**
57
     * The default style to write messages to the symfony console.
58
     *
59
     * @var string
60
     */
61
    const DEFAULT_STYLE = 'info';
62
63
    /**
64
     * The log level => console style mapping.
65
     *
66
     * @var array
67
     */
68
    protected $logLevelStyleMapping = array(
69
        LogLevel::INFO      => 'info',
70
        LogLevel::DEBUG     => 'comment',
71
        LogLevel::ERROR     => 'error',
72
        LogLevel::ALERT     => 'error',
73
        LogLevel::CRITICAL  => 'error',
74
        LogLevel::EMERGENCY => 'error',
75
        LogLevel::WARNING   => 'error',
76
        LogLevel::NOTICE    => 'info'
77
    );
78
79
    /**
80
     * The PID for the running processes.
81
     *
82
     * @var array
83
     */
84
    protected $pid;
85
86
    /**
87
     * The actions unique serial.
88
     *
89
     * @var string
90
     */
91
    protected $serial;
92
93
    /**
94
     * The array with the system logger instances.
95
     *
96
     * @var \Doctrine\Common\Collections\Collection
97
     */
98
    protected $systemLoggers;
99
100
    /**
101
     * The RegistryProcessor instance to handle running threads.
102
     *
103
     * @var \TechDivision\Import\Services\RegistryProcessorInterface
104
     */
105
    protected $registryProcessor;
106
107
    /**
108
     * The processor to read/write the necessary import data.
109
     *
110
     * @var \TechDivision\Import\Services\ImportProcessorInterface
111
     */
112
    protected $importProcessor;
113
114
    /**
115
     * The DI container builder instance.
116
     *
117
     * @var \Symfony\Component\DependencyInjection\TaggedContainerInterface
118
     */
119
    protected $container;
120
121
    /**
122
     * The system configuration.
123
     *
124
     * @var \TechDivision\Import\ConfigurationInterface
125
     */
126
    protected $configuration;
127
128
    /**
129
     * The output stream to write console information to.
130
     *
131
     * @var \Symfony\Component\Console\Output\OutputInterface
132
     */
133
    protected $output;
134
135
    /**
136
     * The plugins to be processed.
137
     *
138
     * @var array
139
     */
140
    protected $plugins = array();
141
142
    /**
143
     * The flag that stop's processing the operation.
144
     *
145
     * @var boolean
146
     */
147
    protected $stopped = false;
148
149
    /**
150
     * The filehandle for the PID file.
151
     *
152
     * @var resource
153
     */
154
    protected $fh;
155
156
    /**
157
     * The array with the module instances.
158
     *
159
     * @var \TechDivision\Import\Modules\ModuleInterface[]
160
     */
161
    protected $modules;
162
163
    /**
164
     * The event emitter instance.
165
     *
166
     * @var \League\Event\EmitterInterface
167
     */
168
    protected $emitter;
169
170
    /**
171
     * The constructor to initialize the instance.
172
     *
173
     * @param \Symfony\Component\DependencyInjection\TaggedContainerInterface $container         The DI container instance
174
     * @param \TechDivision\Import\Services\RegistryProcessorInterface        $registryProcessor The registry processor instance
175
     * @param \TechDivision\Import\Services\ImportProcessorInterface          $importProcessor   The import processor instance
176
     * @param \TechDivision\Import\ConfigurationInterface                     $configuration     The system configuration
177
     * @param \Symfony\Component\Console\Output\OutputInterface               $output            The output instance
178
     * @param \Doctrine\Common\Collections\Collection                         $systemLoggers     The array with the system logger instances
179
     * @param \League\Event\EmitterInterface                                  $emitter           The event emitter instance
180
     * @param \Traversable                                                    $modules           The modules that provides the business logic
181
     */
182
    public function __construct(
183
        TaggedContainerInterface $container,
184
        RegistryProcessorInterface $registryProcessor,
185
        ImportProcessorInterface $importProcessor,
186
        ConfigurationInterface $configuration,
187
        OutputInterface $output,
188
        Collection $systemLoggers,
189
        EmitterInterface $emitter,
190
        \Traversable $modules
191
    ) {
192
193
        // register the shutdown function
194
        register_shutdown_function(array($this, 'shutdown'));
195
196
        // initialize the instance with the passed values
197
        $this->setOutput($output);
198
        $this->setEmitter($emitter);
199
        $this->setModules($modules);
200
        $this->setContainer($container);
201
        $this->setConfiguration($configuration);
202
        $this->setSystemLoggers($systemLoggers);
203
        $this->setImportProcessor($importProcessor);
204
        $this->setRegistryProcessor($registryProcessor);
205
    }
206
207
    /**
208
     * Set's the event emitter instance.
209
     *
210
     * @param \League\Event\EmitterInterface $emitter The event emitter instance
211
     *
212
     * @return void
213
     */
214
    public function setEmitter(EmitterInterface $emitter)
215
    {
216
        $this->emitter = $emitter;
217
    }
218
219
    /**
220
     * Return's the event emitter instance.
221
     *
222
     * @return \League\Event\EmitterInterface The event emitter instance
223
     */
224
    public function getEmitter()
225
    {
226
        return $this->emitter;
227
    }
228
229
    /**
230
     * Set's the container instance.
231
     *
232
     * @param \Symfony\Component\DependencyInjection\TaggedContainerInterface $container The container instance
233
     *
234
     * @return void
235
     */
236
    public function setContainer(TaggedContainerInterface $container)
237
    {
238
        $this->container = $container;
239
    }
240
241
    /**
242
     * Return's the container instance.
243
     *
244
     * @return \Symfony\Component\DependencyInjection\TaggedContainerInterface The container instance
245
     */
246
    public function getContainer()
247
    {
248
        return $this->container;
249
    }
250
251
    /**
252
     * Set's the output stream to write console information to.
253
     *
254
     * @param \Symfony\Component\Console\Output\OutputInterface $output The output stream
255
     *
256
     * @return void
257
     */
258
    public function setOutput(OutputInterface $output)
259
    {
260
        $this->output = $output;
261
    }
262
263
    /**
264
     * Return's the output stream to write console information to.
265
     *
266
     * @return \Symfony\Component\Console\Output\OutputInterface The output stream
267
     */
268
    public function getOutput()
269
    {
270
        return $this->output;
271
    }
272
273
    /**
274
     * Set's the system configuration.
275
     *
276
     * @param \TechDivision\Import\ConfigurationInterface $configuration The system configuration
277
     *
278
     * @return void
279
     */
280
    public function setConfiguration(ConfigurationInterface $configuration)
281
    {
282
        $this->configuration = $configuration;
283
    }
284
285
    /**
286
     * Return's the system configuration.
287
     *
288
     * @return \TechDivision\Import\ConfigurationInterface The system configuration
289
     */
290
    public function getConfiguration()
291
    {
292
        return $this->configuration;
293
    }
294
295
    /**
296
     * Set's the RegistryProcessor instance to handle the running threads.
297
     *
298
     * @param \TechDivision\Import\Services\RegistryProcessor $registryProcessor The registry processor instance
299
     *
300
     * @return void
301
     */
302
    public function setRegistryProcessor(RegistryProcessorInterface $registryProcessor)
303
    {
304
        $this->registryProcessor = $registryProcessor;
305
    }
306
307
    /**
308
     * Return's the RegistryProcessor instance to handle the running threads.
309
     *
310
     * @return \TechDivision\Import\Services\RegistryProcessor The registry processor instance
311
     */
312
    public function getRegistryProcessor()
313
    {
314
        return $this->registryProcessor;
315
    }
316
317
    /**
318
     * Set's the import processor instance.
319
     *
320
     * @param \TechDivision\Import\Services\ImportProcessorInterface $importProcessor The import processor instance
321
     *
322
     * @return void
323
     */
324
    public function setImportProcessor(ImportProcessorInterface $importProcessor)
325
    {
326
        $this->importProcessor = $importProcessor;
327
    }
328
329
    /**
330
     * Return's the import processor instance.
331
     *
332
     * @return \TechDivision\Import\Services\ImportProcessorInterface The import processor instance
333
     */
334
    public function getImportProcessor()
335
    {
336
        return $this->importProcessor;
337
    }
338
339
    /**
340
     * The array with the system loggers.
341
     *
342
     * @param \Doctrine\Common\Collections\Collection $systemLoggers The system logger instances
343
     *
344
     * @return void
345
     */
346
    public function setSystemLoggers(Collection $systemLoggers)
347
    {
348
        $this->systemLoggers = $systemLoggers;
349
    }
350
351
    /**
352
     * Set's the module instances.
353
     *
354
     * @param \Traversable $modules The modules instances
355
     *
356
     * @return void
357
     */
358
    public function setModules(\Traversable $modules)
359
    {
360
        $this->modules = $modules;
0 ignored issues
show
Documentation Bug introduced by
It seems like $modules of type object<Traversable> is incompatible with the declared type array<integer,object<Tec...dules\ModuleInterface>> of property $modules.

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...
361
    }
362
363
    /**
364
     * Return's the module instances.
365
     *
366
     * @return \Traversable The module instances
367
     */
368
    public function getModules()
369
    {
370
        return $this->modules;
371
    }
372
373
    /**
374
     * Return's the logger with the passed name, by default the system logger.
375
     *
376
     * @param string $name The name of the requested system logger
377
     *
378
     * @return \Psr\Log\LoggerInterface The logger instance
379
     * @throws \Exception Is thrown, if the requested logger is NOT available
380
     */
381
    public function getSystemLogger($name = LoggerKeys::SYSTEM)
382
    {
383
384
        // query whether or not, the requested logger is available
385
        if (isset($this->systemLoggers[$name])) {
386
            return $this->systemLoggers[$name];
387
        }
388
389
        // throw an exception if the requested logger is NOT available
390
        throw new \Exception(
391
            sprintf(
392
                'The requested logger \'%s\' is not available',
393
                $name
394
            )
395
        );
396
    }
397
398
    /**
399
     * Returns the actual application version.
400
     *
401
     * @return string The application's version
402
     */
403
    public function getVersion()
404
    {
405
        return $this->getContainer()->get(DependencyInjectionKeys::APPLICATION)->getVersion();
406
    }
407
408
    /**
409
     * Returns the actual application name.
410
     *
411
     * @return string The application's name
412
     */
413
    public function getName()
414
    {
415
        return $this->getContainer()->get(DependencyInjectionKeys::APPLICATION)->getName();
416
    }
417
418
    /**
419
     * Query whether or not the system logger with the passed name is available.
420
     *
421
     * @param string $name The name of the requested system logger
422
     *
423
     * @return boolean TRUE if the logger with the passed name exists, else FALSE
424
     */
425
    public function hasSystemLogger($name = LoggerKeys::SYSTEM)
426
    {
427
        return isset($this->systemLoggers[$name]);
428
    }
429
430
    /**
431
     * Return's the array with the system logger instances.
432
     *
433
     * @return \Doctrine\Common\Collections\Collection The logger instance
434
     */
435
    public function getSystemLoggers()
436
    {
437
        return $this->systemLoggers;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->systemLoggers; (Doctrine\Common\Collections\Collection) is incompatible with the return type declared by the interface TechDivision\Import\Appl...rface::getSystemLoggers of type array.

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...
438
    }
439
440
    /**
441
     * Return's the unique serial for this import process.
442
     *
443
     * @return string The unique serial
444
     */
445
    public function getSerial()
446
    {
447
        return $this->serial;
448
    }
449
450
    /**
451
     * The shutdown handler to catch fatal errors.
452
     *
453
     * This method is need to make sure, that an existing PID file will be removed
454
     * if a fatal error has been triggered.
455
     *
456
     * @return void
457
     */
458
    public function shutdown()
459
    {
460
461
        // check if there was a fatal error caused shutdown
462
        if ($lastError = error_get_last()) {
463
            // initialize error type and message
464
            $type = 0;
465
            $message = '';
466
            // extract the last error values
467
            extract($lastError);
468
            // query whether we've a fatal/user error
469
            if ($type === E_ERROR || $type === E_USER_ERROR) {
470
                // clean-up the PID file
471
                $this->unlock();
472
                // log the fatal error message
473
                $this->log($message, LogLevel::ERROR);
474
            }
475
        }
476
    }
477
478
    /**
479
     * Persist the UUID of the actual import process to the PID file.
480
     *
481
     * @return void
482
     * @throws \Exception Is thrown, if the PID can not be locked or the PID can not be added
483
     * @throws \TechDivision\Import\Exceptions\ImportAlreadyRunningException Is thrown, if a import process is already running
484
     */
485
    public function lock()
486
    {
487
488
        // query whether or not, the PID has already been set
489
        if ($this->pid === $this->getSerial()) {
490
            return;
491
        }
492
493
        // if not, initialize the PID
494
        $this->pid = $this->getSerial();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getSerial() of type string is incompatible with the declared type array of property $pid.

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...
495
496
        // open the PID file
497
        $this->fh = fopen($filename = $this->getPidFilename(), 'a+');
498
499
        // try to lock the PID file exclusive
500
        if (!flock($this->fh, LOCK_EX|LOCK_NB)) {
501
            throw new ImportAlreadyRunningException(sprintf('PID file %s is already in use', $filename));
502
        }
503
504
        // append the PID to the PID file
505
        if (fwrite($this->fh, $this->pid . PHP_EOL) === false) {
506
            throw new \Exception(sprintf('Can\'t write PID %s to PID file %s', $this->pid, $filename));
507
        }
508
    }
509
510
    /**
511
     * Remove's the UUID of the actual import process from the PID file.
512
     *
513
     * @return void
514
     * @throws \Exception Is thrown, if the PID can not be removed
515
     */
516
    public function unlock()
517
    {
518
        try {
519
            // remove the PID from the PID file if set
520
            if ($this->pid === $this->getSerial() && is_resource($this->fh)) {
521
                // remove the PID from the file
522
                $this->removeLineFromFile($this->pid, $this->fh);
523
524
                // finally unlock/close the PID file
525
                flock($this->fh, LOCK_UN);
526
                fclose($this->fh);
527
528
                // if the PID file is empty, delete the file
529
                if (filesize($filename = $this->getPidFilename()) === 0) {
530
                    unlink($filename);
531
                }
532
            }
533
        } catch (FileNotFoundException $fnfe) {
534
            $this->getSystemLogger()->notice(sprintf('PID file %s doesn\'t exist', $this->getPidFilename()));
535
        } catch (LineNotFoundException $lnfe) {
536
            $this->getSystemLogger()->notice(sprintf('PID %s is can not be found in PID file %s', $this->pid, $this->getPidFilename()));
537
        } catch (\Exception $e) {
538
            throw new \Exception(sprintf('Can\'t remove PID %s from PID file %s', $this->pid, $this->getPidFilename()), null, $e);
539
        }
540
    }
541
542
    /**
543
     * Remove's the passed line from the file with the passed name.
544
     *
545
     * @param string   $line The line to be removed
546
     * @param resource $fh   The file handle of the file the line has to be removed
547
     *
548
     * @return void
549
     * @throws \Exception Is thrown, if the file doesn't exists, the line is not found or can not be removed
550
     */
551
    public function removeLineFromFile($line, $fh)
552
    {
553
554
        // initialize the array for the PIDs found in the PID file
555
        $lines = array();
556
557
        // initialize the flag if the line has been found
558
        $found = false;
559
560
        // rewind the file pointer
561
        rewind($fh);
562
563
        // read the lines with the PIDs from the PID file
564
        while (($buffer = fgets($fh, 4096)) !== false) {
565
            // remove the new line
566
            $buffer = trim($buffer);
567
            // if the line is the one to be removed, ignore the line
568
            if ($line === $buffer) {
569
                $found = true;
570
                continue;
571
            }
572
573
            // add the found PID to the array
574
            $lines[] = $buffer;
575
        }
576
577
        // query whether or not, we found the line
578
        if (!$found) {
579
            throw new LineNotFoundException(sprintf('Line %s can not be found', $line));
580
        }
581
582
        // empty the file and rewind the file pointer
583
        ftruncate($fh, 0);
584
        rewind($fh);
585
586
        // append the existing lines to the file
587
        foreach ($lines as $ln) {
588
            if (fwrite($fh, $ln . PHP_EOL) === false) {
589
                throw new \Exception(sprintf('Can\'t write %s to file', $ln));
590
            }
591
        }
592
    }
593
594
    /**
595
     * Process the given operation.
596
     *
597
     * @param string $serial The unique serial of the actual import process
598
     *
599
     * @return null|int null or 0 if everything went fine, or an error code
600
     * @throws \Exception Is thrown if the operation can't be finished successfully
601
     */
602
    public function process($serial)
603
    {
604
605
        try {
606
            // track the start time
607
            $startTime = microtime(true);
608
609
            // set the serial for this import process
610
            $this->serial = $serial;
611
612
            // invoke the event that has to be fired before the application start's the transaction
613
            // (if single transaction mode has been activated)
614
            $this->getEmitter()->emit(EventNames::APP_PROCESS_TRANSACTION_START, $this);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
615
616
            // start the transaction, if single transaction mode has been configured
617
            if ($this->getConfiguration()->isSingleTransaction()) {
618
                $this->getImportProcessor()->getConnection()->beginTransaction();
619
            }
620
621
            // prepare the global data for the import process
622
            $this->setUp();
623
624
            // process the modules
625
            foreach ($this->getModules() as $module) {
626
                $module->process();
627
            }
628
629
            // tear down the  instance
630
            $this->tearDown();
631
632
            // commit the transaction, if single transation mode has been configured
633
            if ($this->getConfiguration()->isSingleTransaction()) {
634
                $this->getImportProcessor()->getConnection()->commit();
635
            }
636
637
            // track the time needed for the import in seconds
638
            $endTime = microtime(true) - $startTime;
639
640
            // log a message that import has been finished
641
            $this->log(
642
                sprintf(
643
                    'Successfully finished import with serial %s in %f s',
644
                    $this->getSerial(),
645
                    $endTime
646
                ),
647
                LogLevel::INFO
648
            );
649
650
            // invoke the event that has to be fired before the application has the transaction
651
            // committed successfully (if single transaction mode has been activated)
652
            $this->getEmitter()->emit(EventNames::APP_PROCESS_TRANSACTION_SUCCESS, $this);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
653
        } catch (ApplicationStoppedException $ase) {
0 ignored issues
show
Bug introduced by
The class TechDivision\Import\Exce...icationStoppedException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
654
            // tear down
655
            $this->tearDown();
656
657
            // rollback the transaction, if single transaction mode has been configured
658
            if ($this->getConfiguration()->isSingleTransaction()) {
659
                $this->getImportProcessor()->getConnection()->rollBack();
660
            }
661
662
            // invoke the event that has to be fired after the application rollbacked the
663
            // transaction (if single transaction mode has been activated)
664
            $this->getEmitter()->emit(EventNames::APP_PROCESS_TRANSACTION_FAILURE, $this, $ase);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
665
666
            // finally, if a PID has been set (because CSV files has been found),
667
            // remove it from the PID file to unlock the importer
668
            $this->unlock();
669
670
            // track the time needed for the import in seconds
671
            $endTime = microtime(true) - $startTime;
672
673
            // log a message that the file import failed
674
            foreach ($this->systemLoggers as $systemLogger) {
675
                $systemLogger->error($ase->__toString());
676
            }
677
678
            // log a message that import has been finished
679
            $this->getSystemLogger()->info(
680
                sprintf(
681
                    'Can\'t finish import with serial %s in %f s',
682
                    $this->getSerial(),
683
                    $endTime
684
                )
685
            );
686
687
            // log the exception message as warning
688
            $this->log($ase->getMessage(), LogLevel::WARNING);
689
690
            // return 1 to signal an error
691
            return 1;
692
        } catch (ImportAlreadyRunningException $iare) {
693
            // tear down
694
            $this->tearDown();
695
696
            // rollback the transaction, if single transaction mode has been configured
697
            if ($this->getConfiguration()->isSingleTransaction()) {
698
                $this->getImportProcessor()->getConnection()->rollBack();
699
            }
700
701
            // invoke the event that has to be fired after the application rollbacked the
702
            // transaction (if single transaction mode has been activated)
703
            $this->getEmitter()->emit(EventNames::APP_PROCESS_TRANSACTION_FAILURE, $this, $iare);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
704
705
            // finally, if a PID has been set (because CSV files has been found),
706
            // remove it from the PID file to unlock the importer
707
            $this->unlock();
708
709
            // track the time needed for the import in seconds
710
            $endTime = microtime(true) - $startTime;
711
712
            // log a warning, because another import process is already running
713
            $this->getSystemLogger()->warning($iare->__toString());
714
715
            // log a message that import has been finished
716
            $this->getSystemLogger()->info(
717
                sprintf(
718
                    'Can\'t finish import with serial because another import process is running %s in %f s',
719
                    $this->getSerial(),
720
                    $endTime
721
                )
722
            );
723
724
            // log the exception message as warning
725
            $this->log($iare->getMessage(), LogLevel::WARNING);
726
727
            // return 1 to signal an error
728
            return 1;
729
        } catch (\Exception $e) {
730
            // tear down
731
            $this->tearDown();
732
733
            // rollback the transaction, if single transaction mode has been configured
734
            if ($this->getConfiguration()->isSingleTransaction()) {
735
                $this->getImportProcessor()->getConnection()->rollBack();
736
            }
737
738
            // invoke the event that has to be fired after the application rollbacked the
739
            // transaction (if single transaction mode has been activated)
740
            $this->getEmitter()->emit(EventNames::APP_PROCESS_TRANSACTION_FAILURE, $this, $e);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
741
742
            // finally, if a PID has been set (because CSV files has been found),
743
            // remove it from the PID file to unlock the importer
744
            $this->unlock();
745
746
            // track the time needed for the import in seconds
747
            $endTime = microtime(true) - $startTime;
748
749
            // log a message that the file import failed
750
            foreach ($this->systemLoggers as $systemLogger) {
751
                $systemLogger->error($e->__toString());
752
            }
753
754
            // log a message that import has been finished
755
            $this->getSystemLogger()->info(
756
                sprintf(
757
                    'Can\'t finish import with serial %s in %f s',
758
                    $this->getSerial(),
759
                    $endTime
760
                )
761
            );
762
763
            // log the exception message as warning
764
            $this->log($e->getMessage(), LogLevel::ERROR);
765
766
            // return 1 to signal an error
767
            return 1;
768
        }
769
    }
770
771
    /**
772
     * Stop processing the operation.
773
     *
774
     * @param string $reason The reason why the operation has been stopped
775
     *
776
     * @return void
777
     * @throws \TechDivision\Import\Exceptions\ApplicationStoppedException Is thrown if the application has been stopped
778
     */
779
    public function stop($reason)
780
    {
781
782
        // stop processing the plugins by setting the flag to TRUE
783
        $this->stopped = true;
784
785
        // throw the exeception
786
        throw new ApplicationStoppedException($reason);
787
    }
788
789
    /**
790
     * Return's TRUE if the operation has been stopped, else FALSE.
791
     *
792
     * @return boolean TRUE if the process has been stopped, else FALSE
793
     */
794
    public function isStopped()
795
    {
796
        return $this->stopped;
797
    }
798
799
    /**
800
     * Gets a service.
801
     *
802
     * @param string $id The service identifier
803
     *
804
     * @return object The associated service
805
     *
806
     * @throws \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException When a circular reference is detected
807
     * @throws \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException          When the service is not defined
808
     */
809
    public function get($id)
810
    {
811
        return $this->getContainer()->get($id);
812
    }
813
814
    /**
815
     * Returns true if the container can return an entry for the given identifier.
816
     * Returns false otherwise.
817
     *
818
     * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
819
     * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
820
     *
821
     * @param string $id Identifier of the entry to look for.
822
     *
823
     * @return bool
824
     */
825
    public function has($id)
826
    {
827
        return $this->getContainer()->has($id);
828
    }
829
830
    /**
831
     * Lifecycle callback that will be inovked before the
832
     * import process has been started.
833
     *
834
     * @return void
835
     */
836
    protected function setUp()
837
    {
838
        $this->getEmitter()->emit(EventNames::APP_SET_UP, $this);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
839
    }
840
841
    /**
842
     * Lifecycle callback that will be inovked after the
843
     * import process has been finished.
844
     *
845
     * @return void
846
     */
847
    protected function tearDown()
848
    {
849
        $this->getEmitter()->emit(EventNames::APP_TEAR_DOWN, $this);
0 ignored issues
show
Unused Code introduced by
The call to EmitterInterface::emit() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
850
    }
851
852
    /**
853
     * Simple method that writes the passed method the the console and the
854
     * system logger, if configured and a log level has been passed.
855
     *
856
     * @param string $msg      The message to log
857
     * @param string $logLevel The log level to use
858
     *
859
     * @return void
860
     */
861
    public function log($msg, $logLevel = null)
862
    {
863
864
        // initialize the formatter helper
865
        $helper = new FormatterHelper();
866
867
        // map the log level to the console style
868
        $style = $this->mapLogLevelToStyle($logLevel);
869
870
        // format the message, according to the passed log level and write it to the console
871
        $this->getOutput()->writeln($logLevel ? $helper->formatBlock($msg, $style) : $msg);
872
873
        // log the message if a log level has been passed
874
        if ($logLevel && $systemLogger = $this->getSystemLogger()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $logLevel of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
875
            $systemLogger->log($logLevel, $msg);
876
        }
877
    }
878
879
    /**
880
     * Map's the passed log level to a valid symfony console style.
881
     *
882
     * @param string $logLevel The log level to map
883
     *
884
     * @return string The apropriate symfony console style
885
     */
886
    protected function mapLogLevelToStyle($logLevel)
887
    {
888
889
        // query whether or not the log level is mapped
890
        if (isset($this->logLevelStyleMapping[$logLevel])) {
891
            return $this->logLevelStyleMapping[$logLevel];
892
        }
893
894
        // return the default style => info
895
        return Simple::DEFAULT_STYLE;
896
    }
897
898
    /**
899
     * Return's the PID filename to use.
900
     *
901
     * @return string The PID filename
902
     */
903
    protected function getPidFilename()
904
    {
905
        return $this->getConfiguration()->getPidFilename();
906
    }
907
}
908