Completed
Push — 9.x ( 3f3f75 )
by Tim
01:55
created

Simple::getVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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\Plugins\PluginFactoryInterface;
35
use TechDivision\Import\Exceptions\LineNotFoundException;
36
use TechDivision\Import\Exceptions\FileNotFoundException;
37
use TechDivision\Import\Exceptions\ImportAlreadyRunningException;
38
use TechDivision\Import\Services\ImportProcessorInterface;
39
use TechDivision\Import\Services\RegistryProcessorInterface;
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 TechDivision company name as ANSI art.
65
     *
66
     * @var string
67
     */
68
    protected $ansiArt = ' _______        _     _____  _       _     _
69
|__   __|      | |   |  __ \(_)     (_)   (_)
70
   | | ___  ___| |__ | |  | |___   ___ ___ _  ___  _ __
71
   | |/ _ \/ __| \'_ \| |  | | \ \ / / / __| |/ _ \| \'_ \
72
   | |  __/ (__| | | | |__| | |\ V /| \__ \ | (_) | | | |
73
   |_|\___|\___|_| |_|_____/|_| \_/ |_|___/_|\___/|_| |_|
74
';
75
76
    /**
77
     * The log level => console style mapping.
78
     *
79
     * @var array
80
     */
81
    protected $logLevelStyleMapping = array(
82
        LogLevel::INFO      => 'info',
83
        LogLevel::DEBUG     => 'comment',
84
        LogLevel::ERROR     => 'error',
85
        LogLevel::ALERT     => 'error',
86
        LogLevel::CRITICAL  => 'error',
87
        LogLevel::EMERGENCY => 'error',
88
        LogLevel::WARNING   => 'error',
89
        LogLevel::NOTICE    => 'info'
90
    );
91
92
    /**
93
     * The PID for the running processes.
94
     *
95
     * @var array
96
     */
97
    protected $pid;
98
99
    /**
100
     * The actions unique serial.
101
     *
102
     * @var string
103
     */
104
    protected $serial;
105
106
    /**
107
     * The array with the system logger instances.
108
     *
109
     * @var \Doctrine\Common\Collections\Collection
110
     */
111
    protected $systemLoggers;
112
113
    /**
114
     * The RegistryProcessor instance to handle running threads.
115
     *
116
     * @var \TechDivision\Import\Services\RegistryProcessorInterface
117
     */
118
    protected $registryProcessor;
119
120
    /**
121
     * The processor to read/write the necessary import data.
122
     *
123
     * @var \TechDivision\Import\Services\ImportProcessorInterface
124
     */
125
    protected $importProcessor;
126
127
    /**
128
     * The DI container builder instance.
129
     *
130
     * @var \Symfony\Component\DependencyInjection\TaggedContainerInterface
131
     */
132
    protected $container;
133
134
    /**
135
     * The system configuration.
136
     *
137
     * @var \TechDivision\Import\ConfigurationInterface
138
     */
139
    protected $configuration;
140
141
    /**
142
     * The output stream to write console information to.
143
     *
144
     * @var \Symfony\Component\Console\Output\OutputInterface
145
     */
146
    protected $output;
147
148
    /**
149
     * The plugins to be processed.
150
     *
151
     * @var array
152
     */
153
    protected $plugins = array();
154
155
    /**
156
     * The flag that stop's processing the operation.
157
     *
158
     * @var boolean
159
     */
160
    protected $stopped = false;
161
162
    /**
163
     * The filehandle for the PID file.
164
     *
165
     * @var resource
166
     */
167
    protected $fh;
168
169
    /**
170
     * The plugin factory instance.
171
     *
172
     * @var \TechDivision\Import\Plugins\PluginFactoryInterface
173
     */
174
    protected $pluginFactory;
175
176
    /**
177
     * The event emitter instance.
178
     *
179
     * @var \League\Event\EmitterInterface
180
     */
181
    protected $emitter;
182
183
    /**
184
     * The constructor to initialize the instance.
185
     *
186
     * @param \Symfony\Component\DependencyInjection\TaggedContainerInterface $container         The DI container instance
187
     * @param \TechDivision\Import\Services\RegistryProcessorInterface        $registryProcessor The registry processor instance
188
     * @param \TechDivision\Import\Services\ImportProcessorInterface          $importProcessor   The import processor instance
189
     * @param \TechDivision\Import\ConfigurationInterface                     $configuration     The system configuration
190
     * @param \TechDivision\Import\Plugins\PluginFactoryInterface             $pluginFactory     The plugin factory instance
191
     * @param \Symfony\Component\Console\Output\OutputInterface               $output            The output instance
192
     * @param \Doctrine\Common\Collections\Collection                         $systemLoggers     The array with the system logger instances
193
     * @param \League\Event\EmitterInterface                                  $emitter           The event emitter instance
194
     */
195
    public function __construct(
196
        TaggedContainerInterface $container,
197
        RegistryProcessorInterface $registryProcessor,
198
        ImportProcessorInterface $importProcessor,
199
        ConfigurationInterface $configuration,
200
        PluginFactoryInterface $pluginFactory,
201
        OutputInterface $output,
202
        Collection $systemLoggers,
203
        EmitterInterface $emitter
204
    ) {
205
206
        // register the shutdown function
207
        register_shutdown_function(array($this, 'shutdown'));
208
209
        // initialize the instance with the passed values
210
        $this->setOutput($output);
211
        $this->setEmitter($emitter);
212
        $this->setContainer($container);
213
        $this->setConfiguration($configuration);
214
        $this->setSystemLoggers($systemLoggers);
215
        $this->setPluginFactory($pluginFactory);
216
        $this->setImportProcessor($importProcessor);
217
        $this->setRegistryProcessor($registryProcessor);
218
    }
219
220
    /**
221
     * Set's the event emitter instance.
222
     *
223
     * @param \League\Event\EmitterInterface $emitter The event emitter instance
224
     *
225
     * @return void
226
     */
227
    public function setEmitter(EmitterInterface $emitter)
228
    {
229
        $this->emitter = $emitter;
230
    }
231
232
    /**
233
     * Return's the event emitter instance.
234
     *
235
     * @return \League\Event\EmitterInterface The event emitter instance
236
     */
237
    public function getEmitter()
238
    {
239
        return $this->emitter;
240
    }
241
242
    /**
243
     * Set's the container instance.
244
     *
245
     * @param \Symfony\Component\DependencyInjection\TaggedContainerInterface $container The container instance
246
     *
247
     * @return void
248
     */
249
    public function setContainer(TaggedContainerInterface $container)
250
    {
251
        $this->container = $container;
252
    }
253
254
    /**
255
     * Return's the container instance.
256
     *
257
     * @return \Symfony\Component\DependencyInjection\TaggedContainerInterface The container instance
258
     */
259
    public function getContainer()
260
    {
261
        return $this->container;
262
    }
263
264
    /**
265
     * Set's the output stream to write console information to.
266
     *
267
     * @param \Symfony\Component\Console\Output\OutputInterface $output The output stream
268
     *
269
     * @return void
270
     */
271
    public function setOutput(OutputInterface $output)
272
    {
273
        $this->output = $output;
274
    }
275
276
    /**
277
     * Return's the output stream to write console information to.
278
     *
279
     * @return \Symfony\Component\Console\Output\OutputInterface The output stream
280
     */
281
    public function getOutput()
282
    {
283
        return $this->output;
284
    }
285
286
    /**
287
     * Set's the system configuration.
288
     *
289
     * @param \TechDivision\Import\ConfigurationInterface $configuration The system configuration
290
     *
291
     * @return void
292
     */
293
    public function setConfiguration(ConfigurationInterface $configuration)
294
    {
295
        $this->configuration = $configuration;
296
    }
297
298
    /**
299
     * Return's the system configuration.
300
     *
301
     * @return \TechDivision\Import\ConfigurationInterface The system configuration
302
     */
303
    public function getConfiguration()
304
    {
305
        return $this->configuration;
306
    }
307
308
    /**
309
     * Set's the RegistryProcessor instance to handle the running threads.
310
     *
311
     * @param \TechDivision\Import\Services\RegistryProcessor $registryProcessor The registry processor instance
312
     *
313
     * @return void
314
     */
315
    public function setRegistryProcessor(RegistryProcessorInterface $registryProcessor)
316
    {
317
        $this->registryProcessor = $registryProcessor;
318
    }
319
320
    /**
321
     * Return's the RegistryProcessor instance to handle the running threads.
322
     *
323
     * @return \TechDivision\Import\Services\RegistryProcessor The registry processor instance
324
     */
325
    public function getRegistryProcessor()
326
    {
327
        return $this->registryProcessor;
328
    }
329
330
    /**
331
     * Set's the import processor instance.
332
     *
333
     * @param \TechDivision\Import\Services\ImportProcessorInterface $importProcessor The import processor instance
334
     *
335
     * @return void
336
     */
337
    public function setImportProcessor(ImportProcessorInterface $importProcessor)
338
    {
339
        $this->importProcessor = $importProcessor;
340
    }
341
342
    /**
343
     * Return's the import processor instance.
344
     *
345
     * @return \TechDivision\Import\Services\ImportProcessorInterface The import processor instance
346
     */
347
    public function getImportProcessor()
348
    {
349
        return $this->importProcessor;
350
    }
351
352
    /**
353
     * The array with the system loggers.
354
     *
355
     * @param \Doctrine\Common\Collections\Collection $systemLoggers The system logger instances
356
     *
357
     * @return void
358
     */
359
    public function setSystemLoggers(Collection $systemLoggers)
360
    {
361
        $this->systemLoggers = $systemLoggers;
362
    }
363
364
    /**
365
     * Set's the plugin factory instance.
366
     *
367
     * @param \TechDivision\Import\Plugins\PluginFactoryInterface $pluginFactory The plugin factory instance
368
     *
369
     * @return void
370
     */
371
    public function setPluginFactory(PluginFactoryInterface $pluginFactory)
372
    {
373
        $this->pluginFactory = $pluginFactory;
374
    }
375
376
    /**
377
     * Return's the plugin factory instance.
378
     *
379
     * @return \TechDivision\Import\Plugins\PluginFactoryInterface The plugin factory instance
380
     */
381
    public function getPluginFactory()
382
    {
383
        return $this->pluginFactory;
384
    }
385
386
    /**
387
     * Return's the logger with the passed name, by default the system logger.
388
     *
389
     * @param string $name The name of the requested system logger
390
     *
391
     * @return \Psr\Log\LoggerInterface The logger instance
392
     * @throws \Exception Is thrown, if the requested logger is NOT available
393
     */
394
    public function getSystemLogger($name = LoggerKeys::SYSTEM)
395
    {
396
397
        // query whether or not, the requested logger is available
398
        if (isset($this->systemLoggers[$name])) {
399
            return $this->systemLoggers[$name];
400
        }
401
402
        // throw an exception if the requested logger is NOT available
403
        throw new \Exception(
404
            sprintf(
405
                'The requested logger \'%s\' is not available',
406
                $name
407
            )
408
        );
409
    }
410
411
    /**
412
     * Returns the actual application version.
413
     *
414
     * @return string The application's version
415
     */
416
    public function getVersion()
417
    {
418
        return $this->getContainer()->get(DependencyInjectionKeys::APPLICATION)->getVersion();
419
    }
420
421
    /**
422
     * Query whether or not the system logger with the passed name is available.
423
     *
424
     * @param string $name The name of the requested system logger
425
     *
426
     * @return boolean TRUE if the logger with the passed name exists, else FALSE
427
     */
428
    public function hasSystemLogger($name = LoggerKeys::SYSTEM)
429
    {
430
        return isset($this->systemLoggers[$name]);
431
    }
432
433
    /**
434
     * Return's the array with the system logger instances.
435
     *
436
     * @return \Doctrine\Common\Collections\Collection The logger instance
437
     */
438
    public function getSystemLoggers()
439
    {
440
        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...
441
    }
442
443
    /**
444
     * Return's the unique serial for this import process.
445
     *
446
     * @return string The unique serial
447
     */
448
    public function getSerial()
449
    {
450
        return $this->serial;
451
    }
452
453
    /**
454
     * The shutdown handler to catch fatal errors.
455
     *
456
     * This method is need to make sure, that an existing PID file will be removed
457
     * if a fatal error has been triggered.
458
     *
459
     * @return void
460
     */
461
    public function shutdown()
462
    {
463
464
        // check if there was a fatal error caused shutdown
465
        if ($lastError = error_get_last()) {
466
            // initialize error type and message
467
            $type = 0;
468
            $message = '';
469
            // extract the last error values
470
            extract($lastError);
471
            // query whether we've a fatal/user error
472
            if ($type === E_ERROR || $type === E_USER_ERROR) {
473
                // clean-up the PID file
474
                $this->unlock();
475
                // log the fatal error message
476
                $this->log($message, LogLevel::ERROR);
477
            }
478
        }
479
    }
480
481
    /**
482
     * Persist the UUID of the actual import process to the PID file.
483
     *
484
     * @return void
485
     * @throws \Exception Is thrown, if the PID can not be locked or the PID can not be added
486
     * @throws \TechDivision\Import\Exceptions\ImportAlreadyRunningException Is thrown, if a import process is already running
487
     */
488
    public function lock()
489
    {
490
491
        // query whether or not, the PID has already been set
492
        if ($this->pid === $this->getSerial()) {
493
            return;
494
        }
495
496
        // if not, initialize the PID
497
        $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...
498
499
        // open the PID file
500
        $this->fh = fopen($filename = $this->getPidFilename(), 'a+');
501
502
        // try to lock the PID file exclusive
503
        if (!flock($this->fh, LOCK_EX|LOCK_NB)) {
504
            throw new ImportAlreadyRunningException(sprintf('PID file %s is already in use', $filename));
505
        }
506
507
        // append the PID to the PID file
508
        if (fwrite($this->fh, $this->pid . PHP_EOL) === false) {
509
            throw new \Exception(sprintf('Can\'t write PID %s to PID file %s', $this->pid, $filename));
510
        }
511
    }
512
513
    /**
514
     * Remove's the UUID of the actual import process from the PID file.
515
     *
516
     * @return void
517
     * @throws \Exception Is thrown, if the PID can not be removed
518
     */
519
    public function unlock()
520
    {
521
        try {
522
            // remove the PID from the PID file if set
523
            if ($this->pid === $this->getSerial() && is_resource($this->fh)) {
524
                // remove the PID from the file
525
                $this->removeLineFromFile($this->pid, $this->fh);
526
527
                // finally unlock/close the PID file
528
                flock($this->fh, LOCK_UN);
529
                fclose($this->fh);
530
531
                // if the PID file is empty, delete the file
532
                if (filesize($filename = $this->getPidFilename()) === 0) {
533
                    unlink($filename);
534
                }
535
            }
536
        } catch (FileNotFoundException $fnfe) {
537
            $this->getSystemLogger()->notice(sprintf('PID file %s doesn\'t exist', $this->getPidFilename()));
538
        } catch (LineNotFoundException $lnfe) {
539
            $this->getSystemLogger()->notice(sprintf('PID %s is can not be found in PID file %s', $this->pid, $this->getPidFilename()));
540
        } catch (\Exception $e) {
541
            throw new \Exception(sprintf('Can\'t remove PID %s from PID file %s', $this->pid, $this->getPidFilename()), null, $e);
542
        }
543
    }
544
545
    /**
546
     * Remove's the passed line from the file with the passed name.
547
     *
548
     * @param string   $line The line to be removed
549
     * @param resource $fh   The file handle of the file the line has to be removed
550
     *
551
     * @return void
552
     * @throws \Exception Is thrown, if the file doesn't exists, the line is not found or can not be removed
553
     */
554
    public function removeLineFromFile($line, $fh)
555
    {
556
557
        // initialize the array for the PIDs found in the PID file
558
        $lines = array();
559
560
        // initialize the flag if the line has been found
561
        $found = false;
562
563
        // rewind the file pointer
564
        rewind($fh);
565
566
        // read the lines with the PIDs from the PID file
567
        while (($buffer = fgets($fh, 4096)) !== false) {
568
            // remove the new line
569
            $buffer = trim($buffer);
570
            // if the line is the one to be removed, ignore the line
571
            if ($line === $buffer) {
572
                $found = true;
573
                continue;
574
            }
575
576
            // add the found PID to the array
577
            $lines[] = $buffer;
578
        }
579
580
        // query whether or not, we found the line
581
        if (!$found) {
582
            throw new LineNotFoundException(sprintf('Line %s can not be found', $line));
583
        }
584
585
        // empty the file and rewind the file pointer
586
        ftruncate($fh, 0);
587
        rewind($fh);
588
589
        // append the existing lines to the file
590
        foreach ($lines as $ln) {
591
            if (fwrite($fh, $ln . PHP_EOL) === false) {
592
                throw new \Exception(sprintf('Can\'t write %s to file', $ln));
593
            }
594
        }
595
    }
596
597
    /**
598
     * Process the given operation.
599
     *
600
     * @param string $serial The unique serial of the actual import process
601
     *
602
     * @return void
603
     * @throws \Exception Is thrown if the operation can't be finished successfully
604
     */
605
    public function process($serial)
606
    {
607
608
        try {
609
            // track the start time
610
            $startTime = microtime(true);
611
612
            // set the serial for this import process
613
            $this->serial = $serial;
614
615
            // invoke the event that has to be fired before the application start's the transaction
616
            // (if single transaction mode has been activated)
617
            $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...
618
619
            // start the transaction, if single transaction mode has been configured
620
            if ($this->getConfiguration()->isSingleTransaction()) {
621
                $this->getImportProcessor()->getConnection()->beginTransaction();
622
            }
623
624
            // prepare the global data for the import process
625
            $this->setUp();
626
627
            // process the plugins defined in the configuration
628
            /** @var \TechDivision\Import\Configuration\PluginConfigurationInterface $pluginConfiguration */
629
            foreach ($this->getConfiguration()->getPlugins() as $pluginConfiguration) {
630
                // query whether or not the operation has been stopped
631
                if ($this->isStopped()) {
632
                    break;
633
                }
634
                // process the plugin if not
635
                $this->pluginFactory->createPlugin($pluginConfiguration)->process();
636
            }
637
638
            // tear down the  instance
639
            $this->tearDown();
640
641
            // commit the transaction, if single transation mode has been configured
642
            if ($this->getConfiguration()->isSingleTransaction()) {
643
                $this->getImportProcessor()->getConnection()->commit();
644
            }
645
646
            // track the time needed for the import in seconds
647
            $endTime = microtime(true) - $startTime;
648
649
            // log a message that import has been finished
650
            $this->log(
651
                sprintf(
652
                    'Successfully finished import with serial %s in %f s',
653
                    $this->getSerial(),
654
                    $endTime
655
                ),
656
                LogLevel::INFO
657
            );
658
659
            // invoke the event that has to be fired before the application has the transaction
660
            // committed successfully (if single transaction mode has been activated)
661
            $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...
662
        } catch (ImportAlreadyRunningException $iare) {
663
            // tear down
664
            $this->tearDown();
665
666
            // rollback the transaction, if single transaction mode has been configured
667
            if ($this->getConfiguration()->isSingleTransaction()) {
668
                $this->getImportProcessor()->getConnection()->rollBack();
669
            }
670
671
            // invoke the event that has to be fired after the application rollbacked the
672
            // transaction (if single transaction mode has been activated)
673
            $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...
674
675
            // finally, if a PID has been set (because CSV files has been found),
676
            // remove it from the PID file to unlock the importer
677
            $this->unlock();
678
679
            // track the time needed for the import in seconds
680
            $endTime = microtime(true) - $startTime;
681
682
            // log a warning, because another import process is already running
683
            $this->getSystemLogger()->warning($iare->__toString());
684
685
            // log a message that import has been finished
686
            $this->getSystemLogger()->info(
687
                sprintf(
688
                    'Can\'t finish import with serial because another import process is running %s in %f s',
689
                    $this->getSerial(),
690
                    $endTime
691
                )
692
            );
693
694
            // re-throw the exception
695
            throw $iare;
696
        } catch (\Exception $e) {
697
            // tear down
698
            $this->tearDown();
699
700
            // rollback the transaction, if single transaction mode has been configured
701
            if ($this->getConfiguration()->isSingleTransaction()) {
702
                $this->getImportProcessor()->getConnection()->rollBack();
703
            }
704
705
            // invoke the event that has to be fired after the application rollbacked the
706
            // transaction (if single transaction mode has been activated)
707
            $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...
708
709
            // finally, if a PID has been set (because CSV files has been found),
710
            // remove it from the PID file to unlock the importer
711
            $this->unlock();
712
713
            // track the time needed for the import in seconds
714
            $endTime = microtime(true) - $startTime;
715
716
            // log a message that the file import failed
717
            foreach ($this->systemLoggers as $systemLogger) {
718
                $systemLogger->error($e->__toString());
719
            }
720
721
            // log a message that import has been finished
722
            $this->getSystemLogger()->info(
723
                sprintf(
724
                    'Can\'t finish import with serial %s in %f s',
725
                    $this->getSerial(),
726
                    $endTime
727
                )
728
            );
729
730
            // re-throw the exception
731
            throw $e;
732
        }
733
    }
734
735
    /**
736
     * Stop processing the operation.
737
     *
738
     * @param string $reason The reason why the operation has been stopped
739
     *
740
     * @return void
741
     */
742
    public function stop($reason)
743
    {
744
745
        // log a message that the operation has been stopped
746
        $this->log($reason, LogLevel::INFO);
747
748
        // stop processing the plugins by setting the flag to TRUE
749
        $this->stopped = true;
750
    }
751
752
    /**
753
     * Return's TRUE if the operation has been stopped, else FALSE.
754
     *
755
     * @return boolean TRUE if the process has been stopped, else FALSE
756
     */
757
    public function isStopped()
758
    {
759
        return $this->stopped;
760
    }
761
762
    /**
763
     * Gets a service.
764
     *
765
     * @param string $id The service identifier
766
     *
767
     * @return object The associated service
768
     *
769
     * @throws \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException When a circular reference is detected
770
     * @throws \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException          When the service is not defined
771
     */
772
    public function get($id)
773
    {
774
        return $this->getContainer()->get($id);
775
    }
776
777
    /**
778
     * Returns true if the container can return an entry for the given identifier.
779
     * Returns false otherwise.
780
     *
781
     * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
782
     * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
783
     *
784
     * @param string $id Identifier of the entry to look for.
785
     *
786
     * @return bool
787
     */
788
    public function has($id)
789
    {
790
        return $this->getContainer()->has($id);
791
    }
792
793
    /**
794
     * Lifecycle callback that will be inovked before the
795
     * import process has been started.
796
     *
797
     * @return void
798
     */
799
    protected function setUp()
800
    {
801
        $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...
802
    }
803
804
    /**
805
     * Lifecycle callback that will be inovked after the
806
     * import process has been finished.
807
     *
808
     * @return void
809
     */
810
    protected function tearDown()
811
    {
812
        $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...
813
    }
814
815
    /**
816
     * Simple method that writes the passed method the the console and the
817
     * system logger, if configured and a log level has been passed.
818
     *
819
     * @param string $msg      The message to log
820
     * @param string $logLevel The log level to use
821
     *
822
     * @return void
823
     */
824
    public function log($msg, $logLevel = null)
825
    {
826
827
        // initialize the formatter helper
828
        $helper = new FormatterHelper();
829
830
        // map the log level to the console style
831
        $style = $this->mapLogLevelToStyle($logLevel);
832
833
        // format the message, according to the passed log level and write it to the console
834
        $this->getOutput()->writeln($logLevel ? $helper->formatBlock($msg, $style) : $msg);
835
836
        // log the message if a log level has been passed
837
        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...
838
            $systemLogger->log($logLevel, $msg);
839
        }
840
    }
841
842
    /**
843
     * Map's the passed log level to a valid symfony console style.
844
     *
845
     * @param string $logLevel The log level to map
846
     *
847
     * @return string The apropriate symfony console style
848
     */
849
    protected function mapLogLevelToStyle($logLevel)
850
    {
851
852
        // query whether or not the log level is mapped
853
        if (isset($this->logLevelStyleMapping[$logLevel])) {
854
            return $this->logLevelStyleMapping[$logLevel];
855
        }
856
857
        // return the default style => info
858
        return Simple::DEFAULT_STYLE;
859
    }
860
861
    /**
862
     * Return's the PID filename to use.
863
     *
864
     * @return string The PID filename
865
     */
866
    protected function getPidFilename()
867
    {
868
        return $this->getConfiguration()->getPidFilename();
869
    }
870
}
871