Completed
Push — 17.x ( 60eb45 )
by Tim
07:22
created

Simple::unlock()   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 Psr\Container\ContainerInterface;
26
use Doctrine\Common\Collections\Collection;
27
use Symfony\Component\Console\Output\OutputInterface;
28
use Symfony\Component\Console\Helper\FormatterHelper;
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\ImportAlreadyRunningException;
35
use TechDivision\Import\Services\ImportProcessorInterface;
36
use TechDivision\Import\Services\RegistryProcessorInterface;
37
use TechDivision\Import\Exceptions\ApplicationStoppedException;
38
use TechDivision\Import\Handlers\PidFileHandlerInterface;
39
use TechDivision\Import\Handlers\GenericFileHandlerInterface;
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 \Psr\Container\ContainerInterface
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 generic file handler instance.
172
     *
173
     * @var \TechDivision\Import\Handlers\GenericFileHandlerInterface
174
     */
175
    protected $genericFileHanlder;
176
177
    /**
178
     * The PID file handler instance.
179
     *
180
     * @var \TechDivision\Import\Handlers\PidFileHandlerInterface
181
     */
182
    protected $pidFileHanlder;
183
184
    /**
185
     * The constructor to initialize the instance.
186
     *
187
     * @param \Psr\Container\ContainerInterface                         $container          The DI container instance
188
     * @param \TechDivision\Import\Services\RegistryProcessorInterface  $registryProcessor  The registry processor instance
189
     * @param \TechDivision\Import\Services\ImportProcessorInterface    $importProcessor    The import processor instance
190
     * @param \TechDivision\Import\ConfigurationInterface               $configuration      The system configuration
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
     * @param \TechDivision\Import\Handlers\GenericFileHandlerInterface $genericFileHandler The generic file handler instance
195
     * @param \TechDivision\Import\Handlers\PidFileHandlerInterface     $pidFileHandler     The PID file handler instance
196
     * @param \Traversable                                              $modules            The modules that provides the business logic
197
     */
198
    public function __construct(
199
        ContainerInterface $container,
200
        RegistryProcessorInterface $registryProcessor,
201
        ImportProcessorInterface $importProcessor,
202
        ConfigurationInterface $configuration,
203
        OutputInterface $output,
204
        Collection $systemLoggers,
205
        EmitterInterface $emitter,
206
        GenericFileHandlerInterface $genericFileHandler,
207
        PidFileHandlerInterface $pidFileHandler,
208
        \Traversable $modules
209
    ) {
210
211
        // register the shutdown function
212
        register_shutdown_function(array($this, 'shutdown'));
213
214
        // initialize the instance with the passed values
215
        $this->setOutput($output);
216
        $this->setEmitter($emitter);
217
        $this->setModules($modules);
218
        $this->setContainer($container);
219
        $this->setConfiguration($configuration);
220
        $this->setSystemLoggers($systemLoggers);
221
        $this->setPidFileHandler($pidFileHandler);
222
        $this->setImportProcessor($importProcessor);
223
        $this->setRegistryProcessor($registryProcessor);
224
        $this->setGenericFileHandler($genericFileHandler);
225
    }
226
227
    /**
228
     * Set's the event emitter instance.
229
     *
230
     * @param \League\Event\EmitterInterface $emitter The event emitter instance
231
     *
232
     * @return void
233
     */
234
    public function setEmitter(EmitterInterface $emitter)
235
    {
236
        $this->emitter = $emitter;
237
    }
238
239
    /**
240
     * Return's the event emitter instance.
241
     *
242
     * @return \League\Event\EmitterInterface The event emitter instance
243
     */
244
    public function getEmitter()
245
    {
246
        return $this->emitter;
247
    }
248
249
    /**
250
     * Set's the container instance.
251
     *
252
     * @param \Psr\Container\ContainerInterface $container The container instance
253
     *
254
     * @return void
255
     */
256
    public function setContainer(ContainerInterface $container)
257
    {
258
        $this->container = $container;
259
    }
260
261
    /**
262
     * Return's the container instance.
263
     *
264
     * @return \Psr\Container\ContainerInterface The container instance
265
     */
266
    public function getContainer()
267
    {
268
        return $this->container;
269
    }
270
271
    /**
272
     * Set's the output stream to write console information to.
273
     *
274
     * @param \Symfony\Component\Console\Output\OutputInterface $output The output stream
275
     *
276
     * @return void
277
     */
278
    public function setOutput(OutputInterface $output)
279
    {
280
        $this->output = $output;
281
    }
282
283
    /**
284
     * Return's the output stream to write console information to.
285
     *
286
     * @return \Symfony\Component\Console\Output\OutputInterface The output stream
287
     */
288
    public function getOutput()
289
    {
290
        return $this->output;
291
    }
292
293
    /**
294
     * Set's the system configuration.
295
     *
296
     * @param \TechDivision\Import\ConfigurationInterface $configuration The system configuration
297
     *
298
     * @return void
299
     */
300
    public function setConfiguration(ConfigurationInterface $configuration)
301
    {
302
        $this->configuration = $configuration;
303
    }
304
305
    /**
306
     * Return's the system configuration.
307
     *
308
     * @return \TechDivision\Import\ConfigurationInterface The system configuration
309
     */
310
    public function getConfiguration()
311
    {
312
        return $this->configuration;
313
    }
314
315
    /**
316
     * Set's the RegistryProcessor instance to handle the running threads.
317
     *
318
     * @param \TechDivision\Import\Services\RegistryProcessor $registryProcessor The registry processor instance
319
     *
320
     * @return void
321
     */
322
    public function setRegistryProcessor(RegistryProcessorInterface $registryProcessor)
323
    {
324
        $this->registryProcessor = $registryProcessor;
325
    }
326
327
    /**
328
     * Return's the RegistryProcessor instance to handle the running threads.
329
     *
330
     * @return \TechDivision\Import\Services\RegistryProcessor The registry processor instance
331
     */
332
    public function getRegistryProcessor()
333
    {
334
        return $this->registryProcessor;
335
    }
336
337
    /**
338
     * Set's the import processor instance.
339
     *
340
     * @param \TechDivision\Import\Services\ImportProcessorInterface $importProcessor The import processor instance
341
     *
342
     * @return void
343
     */
344
    public function setImportProcessor(ImportProcessorInterface $importProcessor)
345
    {
346
        $this->importProcessor = $importProcessor;
347
    }
348
349
    /**
350
     * Return's the import processor instance.
351
     *
352
     * @return \TechDivision\Import\Services\ImportProcessorInterface The import processor instance
353
     */
354
    public function getImportProcessor()
355
    {
356
        return $this->importProcessor;
357
    }
358
359
    /**
360
     * The array with the system loggers.
361
     *
362
     * @param \Doctrine\Common\Collections\Collection $systemLoggers The system logger instances
363
     *
364
     * @return void
365
     */
366
    public function setSystemLoggers(Collection $systemLoggers)
367
    {
368
        $this->systemLoggers = $systemLoggers;
369
    }
370
371
    /**
372
     * Set's the module instances.
373
     *
374
     * @param \Traversable $modules The modules instances
375
     *
376
     * @return void
377
     */
378
    public function setModules(\Traversable $modules)
379
    {
380
        $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...
381
    }
382
383
    /**
384
     * Return's the module instances.
385
     *
386
     * @return \Traversable The module instances
387
     */
388
    public function getModules()
389
    {
390
        return $this->modules;
391
    }
392
393
    /**
394
     * Set's the PID file handler instance.
395
     *
396
     * @param \TechDivision\Import\Handlers\PidFileHandlerInterface $pidFileHandler The PID file handler instance
397
     *
398
     * @return void
399
     */
400
    public function setPidFileHandler(PidFileHandlerInterface $pidFileHandler) : void
401
    {
402
        $this->pidFileHanlder = $pidFileHandler;
403
    }
404
405
    /**
406
     * Return's the PID file handler instance.
407
     *
408
     * @return \TechDivision\Import\Handlers\PidFileHandlerInterface The PID file handler instance
409
     */
410
    public function getPidFileHandler() : PidFileHandlerInterface
411
    {
412
        return $this->pidFileHanlder;
413
    }
414
415
    /**
416
     * Set's the generic file handler instance.
417
     *
418
     * @param \TechDivision\Import\Handlers\GenericFileHandlerInterface $genericFileHandler The generic file handler instance
419
     *
420
     * @return void
421
     */
422
    public function setGenericFileHandler(GenericFileHandlerInterface $genericFileHandler) : void
423
    {
424
        $this->genericFileHandler = $genericFileHandler;
0 ignored issues
show
Bug introduced by
The property genericFileHandler does not seem to exist. Did you mean genericFileHanlder?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
425
    }
426
427
    /**
428
     * Return's the generic file handler instance.
429
     *
430
     * @return \TechDivision\Import\Handlers\GenericFileHandlerInterface The generic file handler instance
431
     */
432
    public function getGenericFileHandler() : GenericFileHandlerInterface
433
    {
434
        return $this->genericFileHandler;
0 ignored issues
show
Bug introduced by
The property genericFileHandler does not seem to exist. Did you mean genericFileHanlder?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
435
    }
436
437
    /**
438
     * Return's the logger with the passed name, by default the system logger.
439
     *
440
     * @param string $name The name of the requested system logger
441
     *
442
     * @return \Psr\Log\LoggerInterface The logger instance
443
     * @throws \Exception Is thrown, if the requested logger is NOT available
444
     */
445
    public function getSystemLogger($name = LoggerKeys::SYSTEM)
446
    {
447
448
        // query whether or not, the requested logger is available
449
        if (isset($this->systemLoggers[$name])) {
450
            return $this->systemLoggers[$name];
451
        }
452
453
        // throw an exception if the requested logger is NOT available
454
        throw new \Exception(
455
            sprintf(
456
                'The requested logger \'%s\' is not available',
457
                $name
458
            )
459
        );
460
    }
461
462
    /**
463
     * Returns the actual application version.
464
     *
465
     * @return string The application's version
466
     */
467
    public function getVersion()
468
    {
469
        return $this->getContainer()->get(DependencyInjectionKeys::APPLICATION)->getVersion();
470
    }
471
472
    /**
473
     * Returns the actual application name.
474
     *
475
     * @return string The application's name
476
     */
477
    public function getName()
478
    {
479
        return $this->getContainer()->get(DependencyInjectionKeys::APPLICATION)->getName();
480
    }
481
482
    /**
483
     * Query whether or not the system logger with the passed name is available.
484
     *
485
     * @param string $name The name of the requested system logger
486
     *
487
     * @return boolean TRUE if the logger with the passed name exists, else FALSE
488
     */
489
    public function hasSystemLogger($name = LoggerKeys::SYSTEM)
490
    {
491
        return isset($this->systemLoggers[$name]);
492
    }
493
494
    /**
495
     * Return's the array with the system logger instances.
496
     *
497
     * @return \Doctrine\Common\Collections\Collection The logger instance
498
     */
499
    public function getSystemLoggers()
500
    {
501
        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...
502
    }
503
504
    /**
505
     * Return's the unique serial for this import process.
506
     *
507
     * @return string The unique serial
508
     */
509
    public function getSerial()
510
    {
511
        return $this->serial;
512
    }
513
514
    /**
515
     * The shutdown handler to catch fatal errors.
516
     *
517
     * This method is need to make sure, that an existing PID file will be removed
518
     * if a fatal error has been triggered.
519
     *
520
     * @return void
521
     */
522
    public function shutdown()
523
    {
524
525
        // check if there was a fatal error caused shutdown
526
        if ($lastError = error_get_last()) {
527
            // initialize error type and message
528
            $type = 0;
529
            $message = '';
530
            // extract the last error values
531
            extract($lastError);
532
            // query whether we've a fatal/user error
533
            if ($type === E_ERROR || $type === E_USER_ERROR) {
534
                // clean-up the PID file
535
                $this->unlock();
536
                // log the fatal error message
537
                $this->log($message, LogLevel::ERROR);
538
            }
539
        }
540
    }
541
542
    /**
543
     * Persist the UUID of the actual import process to the PID file.
544
     *
545
     * @return void
546
     * @throws \Exception Is thrown, if the PID can not be locked or the PID can not be added
547
     * @throws \TechDivision\Import\Exceptions\ImportAlreadyRunningException Is thrown, if a import process is already running
548
     */
549
    public function lock()
550
    {
551
        $this->getPidFileHandler()->lock();
552
    }
553
554
    /**
555
     * Remove's the UUID of the actual import process from the PID file.
556
     *
557
     * @return void
558
     * @throws \Exception Is thrown, if the PID can not be removed
559
     */
560
    public function unlock()
561
    {
562
        $this->getPidFileHandler()->unlock();
563
    }
564
565
    /**
566
     * Remove's the passed line from the file with the passed name.
567
     *
568
     * @param string   $line The line to be removed
569
     * @param resource $fh   The file handle of the file the line has to be removed
570
     *
571
     * @return void
572
     * @throws \Exception Is thrown, if the file doesn't exists, the line is not found or can not be removed
573
     * @deprecated Since version 17.0.0
574
     * @see \TechDivision\Import\Handlers\GenericFileHandler::removeLineFromFile()
575
     */
576
    public function removeLineFromFile($line, $fh)
577
    {
578
579
        // delegate the invocation to the generic file handler's method
580
        $this->getGenericFileHandler()->removeLineFromFile($line, $fh);
581
582
        // log a message that this method has been deprecated now
583
        $this->log(
584
            sprintf('Method "%s" has been deprecated since version 17.0.0, use  \TechDivision\Import\Handlers\GenericFileHandler::removeLineFromFile() instead', __METHOD__),
585
            LogLevel::WARNING
586
        );
587
    }
588
589
    /**
590
     * Process the given operation.
591
     *
592
     * @param string $serial The unique serial of the actual import process
593
     *
594
     * @return null|int null or 0 if everything went fine, or an error code
595
     * @throws \Exception Is thrown if the operation can't be finished successfully
596
     */
597
    public function process($serial)
598
    {
599
600
        try {
601
            // track the start time
602
            $startTime = microtime(true);
603
604
            // set the serial for this import process
605
            $this->serial = $serial;
606
607
            // invoke the event that has to be fired before the application start's the transaction
608
            // (if single transaction mode has been activated)
609
            $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...
610
611
            // start the transaction, if single transaction mode has been configured
612
            if ($this->getConfiguration()->isSingleTransaction()) {
613
                $this->getImportProcessor()->getConnection()->beginTransaction();
614
            }
615
616
            // prepare the global data for the import process
617
            $this->setUp();
618
619
            // process the modules
620
            foreach ($this->getModules() as $module) {
621
                $module->process();
622
            }
623
624
            // commit the transaction, if single transation mode has been configured
625
            if ($this->getConfiguration()->isSingleTransaction()) {
626
                $this->getImportProcessor()->getConnection()->commit();
627
            }
628
629
            // track the time needed for the import in seconds
630
            $endTime = microtime(true) - $startTime;
631
632
            // log a debug message that import has been finished
633
            $this->getSystemLogger()->info(sprintf('Execution time for operation with serial %s in %f s', $this->getSerial(), $endTime));
634
635
            // invoke the event that has to be fired before the application has the transaction
636
            // committed successfully (if single transaction mode has been activated)
637
            $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...
638
        } catch (ApplicationStoppedException $ase) {
639
            // rollback the transaction, if single transaction mode has been configured
640
            if ($this->getConfiguration()->isSingleTransaction()) {
641
                $this->getImportProcessor()->getConnection()->rollBack();
642
            }
643
644
            // invoke the event that has to be fired after the application rollbacked the
645
            // transaction (if single transaction mode has been activated)
646
            $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...
647
648
            // finally, if a PID has been set (because CSV files has been found),
649
            // remove it from the PID file to unlock the importer
650
            $this->unlock();
651
652
            // track the time needed for the import in seconds
653
            $endTime = microtime(true) - $startTime;
654
655
            // log a message that the file import failed
656
            foreach ($this->systemLoggers as $systemLogger) {
657
                $systemLogger->error($ase->__toString());
658
            }
659
660
            // log a message that import has been finished
661
            $this->getSystemLogger()->warning(sprintf('Can\'t finish import with serial %s in %f s', $this->getSerial(), $endTime));
662
663
            // log the exception message as warning
664
            $this->log($ase->getMessage(), LogLevel::WARNING);
665
666
            // return 1 to signal an error
667
            return 1;
668
        } catch (ImportAlreadyRunningException $iare) {
669
            // rollback the transaction, if single transaction mode has been configured
670
            if ($this->getConfiguration()->isSingleTransaction()) {
671
                $this->getImportProcessor()->getConnection()->rollBack();
672
            }
673
674
            // invoke the event that has to be fired after the application rollbacked the
675
            // transaction (if single transaction mode has been activated)
676
            $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...
677
678
            // finally, if a PID has been set (because CSV files has been found),
679
            // remove it from the PID file to unlock the importer
680
            $this->unlock();
681
682
            // track the time needed for the import in seconds
683
            $endTime = microtime(true) - $startTime;
684
685
            // log a warning, because another import process is already running
686
            $this->getSystemLogger()->warning($iare->__toString());
687
688
            // log a message that import has been finished
689
            $this->getSystemLogger()->warning(sprintf('Can\'t finish import with serial because another import process is running %s in %f s', $this->getSerial(), $endTime));
690
691
            // log the exception message as warning
692
            $this->log($iare->getMessage(), LogLevel::WARNING);
693
694
            // return 1 to signal an error
695
            return 1;
696
        } catch (\Exception $e) {
697
            // rollback the transaction, if single transaction mode has been configured
698
            if ($this->getConfiguration()->isSingleTransaction()) {
699
                $this->getImportProcessor()->getConnection()->rollBack();
700
            }
701
702
            // invoke the event that has to be fired after the application rollbacked the
703
            // transaction (if single transaction mode has been activated)
704
            $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...
705
706
            // finally, if a PID has been set (because CSV files has been found),
707
            // remove it from the PID file to unlock the importer
708
            $this->unlock();
709
710
            // track the time needed for the import in seconds
711
            $endTime = microtime(true) - $startTime;
712
713
            // log a message that the file import failed
714
            foreach ($this->systemLoggers as $systemLogger) {
715
                $systemLogger->error($e->__toString());
716
            }
717
718
            // log a message that import has been finished
719
            $this->getSystemLogger()->error(sprintf('Can\'t finish import with serial %s in %f s', $this->getSerial(), $endTime));
720
721
            // log the exception message as warning
722
            $this->log($e->getMessage(), LogLevel::ERROR);
723
724
            // return 1 to signal an error
725
            return 1;
726
        } finally {
727
            // tear down
728
            $this->tearDown();
729
730
            // invoke the event that has to be fired after the application transaction has been finished
731
            $this->getEmitter()->emit(EventNames::APP_PROCESS_TRANSACTION_FINISHED, $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...
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
     * @throws \TechDivision\Import\Exceptions\ApplicationStoppedException Is thrown if the application has been stopped
742
     */
743
    public function stop($reason)
744
    {
745
746
        // stop processing the plugins by setting the flag to TRUE
747
        $this->stopped = true;
748
749
        // throw the exeception
750
        throw new ApplicationStoppedException($reason);
751
    }
752
753
    /**
754
     * Return's TRUE if the operation has been stopped, else FALSE.
755
     *
756
     * @return boolean TRUE if the process has been stopped, else FALSE
757
     */
758
    public function isStopped()
759
    {
760
        return $this->stopped;
761
    }
762
763
    /**
764
     * Gets a service.
765
     *
766
     * @param string $id The service identifier
767
     *
768
     * @return object The associated service
769
     *
770
     * @throws \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException When a circular reference is detected
771
     * @throws \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException          When the service is not defined
772
     */
773
    public function get($id)
774
    {
775
        return $this->getContainer()->get($id);
776
    }
777
778
    /**
779
     * Returns true if the container can return an entry for the given identifier.
780
     * Returns false otherwise.
781
     *
782
     * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
783
     * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
784
     *
785
     * @param string $id Identifier of the entry to look for.
786
     *
787
     * @return bool
788
     */
789
    public function has($id)
790
    {
791
        return $this->getContainer()->has($id);
792
    }
793
794
    /**
795
     * Lifecycle callback that will be inovked before the
796
     * import process has been started.
797
     *
798
     * @return void
799
     */
800
    protected function setUp()
801
    {
802
        $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...
803
    }
804
805
    /**
806
     * Lifecycle callback that will be inovked after the
807
     * import process has been finished.
808
     *
809
     * @return void
810
     */
811
    protected function tearDown()
812
    {
813
        $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...
814
    }
815
816
    /**
817
     * Simple method that writes the passed method the the console and the
818
     * system logger, if configured and a log level has been passed.
819
     *
820
     * @param string $msg      The message to log
821
     * @param string $logLevel The log level to use
822
     *
823
     * @return void
824
     */
825
    public function log($msg, $logLevel = null)
826
    {
827
828
        // initialize the formatter helper
829
        $helper = new FormatterHelper();
830
831
        // map the log level to the console style
832
        $style = $this->mapLogLevelToStyle($logLevel);
833
834
        // format the message, according to the passed log level and write it to the console
835
        $this->getOutput()->writeln($logLevel ? $helper->formatBlock($msg, $style) : $msg);
836
837
        // log the message if a log level has been passed
838
        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...
839
            $systemLogger->log($logLevel, $msg);
840
        }
841
    }
842
843
    /**
844
     * Map's the passed log level to a valid symfony console style.
845
     *
846
     * @param string $logLevel The log level to map
847
     *
848
     * @return string The apropriate symfony console style
849
     */
850
    protected function mapLogLevelToStyle($logLevel)
851
    {
852
853
        // query whether or not the log level is mapped
854
        if (isset($this->logLevelStyleMapping[$logLevel])) {
855
            return $this->logLevelStyleMapping[$logLevel];
856
        }
857
858
        // return the default style => info
859
        return Simple::DEFAULT_STYLE;
860
    }
861
862
    /**
863
     * Return's the PID filename to use.
864
     *
865
     * @return string The PID filename
866
     */
867
    protected function getPidFilename()
868
    {
869
        return $this->getConfiguration()->getPidFilename();
870
    }
871
}
872