CliProcessor   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 131
Duplicated Lines 0 %

Test Coverage

Coverage 72.41%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 26
eloc 62
c 3
b 0
f 0
dl 0
loc 131
ccs 42
cts 58
cp 0.7241
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
B item() 0 32 10
A formatDataToDebug() 0 20 5
A end() 0 17 4
A batch() 0 3 1
A getBatchSize() 0 3 1
A __construct() 0 21 3
A begin() 0 7 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the DataImporter package.
7
 *
8
 * (c) Loïc Sapone <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace IQ2i\DataImporter\Bundle\Processor;
15
16
use IQ2i\DataImporter\Bundle\Exception\ItemHandlingException;
17
use IQ2i\DataImporter\Exchange\Message;
18
use IQ2i\DataImporter\Processor\BatchProcessorInterface;
19
use Symfony\Component\Console\Helper\ProgressBar;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Output\OutputInterface;
22
use Symfony\Component\Console\Style\SymfonyStyle;
23
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
24
use Symfony\Component\Serializer\Normalizer\NormalizableInterface;
25
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
26
use Symfony\Component\Serializer\Serializer;
27
use Symfony\Component\Serializer\SerializerInterface;
28
29
class CliProcessor implements BatchProcessorInterface
30
{
31
    private readonly SymfonyStyle $io;
32
33
    private readonly ProgressBar $progressBar;
34
35
    private readonly bool $stepByStep;
36
37
    private readonly bool $pauseOnError;
38
39
    private readonly int $batchSize;
40
41
    private array $errors = [];
42
43 2
    public function __construct(
44
        private readonly InputInterface $input,
45
        private readonly OutputInterface $output,
46
        private readonly \Closure $handleBegin,
47
        private readonly \Closure $handleItem,
48
        private readonly \Closure $handleBatch,
49
        private readonly \Closure $handleEnd,
50
        private ?SerializerInterface $serializer = null,
51
    ) {
52 2
        $this->serializer = $serializer ?? new Serializer([new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter())]);
53
54 2
        $this->io = new SymfonyStyle($this->input, $this->output);
0 ignored issues
show
Bug introduced by
The property io is declared read-only in IQ2i\DataImporter\Bundle\Processor\CliProcessor.
Loading history...
55 2
        $this->progressBar = new ProgressBar($this->output);
0 ignored issues
show
Bug introduced by
The property progressBar is declared read-only in IQ2i\DataImporter\Bundle\Processor\CliProcessor.
Loading history...
56
57 2
        $this->stepByStep = (bool) $this->input->getOption('step');
0 ignored issues
show
Bug introduced by
The property stepByStep is declared read-only in IQ2i\DataImporter\Bundle\Processor\CliProcessor.
Loading history...
58 2
        if ($this->stepByStep && $this->output->getVerbosity() >= OutputInterface::VERBOSITY_NORMAL) {
59
            $this->output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
60
        }
61
62 2
        $this->pauseOnError = (bool) $this->input->getOption('pause-on-error');
0 ignored issues
show
Bug introduced by
The property pauseOnError is declared read-only in IQ2i\DataImporter\Bundle\Processor\CliProcessor.
Loading history...
63 2
        $this->batchSize = (int) $this->input->getOption('batch-size');
0 ignored issues
show
Bug introduced by
The property batchSize is declared read-only in IQ2i\DataImporter\Bundle\Processor\CliProcessor.
Loading history...
64
    }
65
66 2
    public function begin(Message $message)
67
    {
68 2
        if (OutputInterface::VERBOSITY_NORMAL === $this->output->getVerbosity()) {
69 1
            $this->progressBar->start();
70
        }
71
72 2
        ($this->handleBegin)($message);
73
    }
74
75 2
    public function item(Message $message)
76
    {
77
        try {
78 2
            ($this->handleItem)($message);
79
        } catch (ItemHandlingException $itemHandlingException) {
80
            if ($this->pauseOnError) {
81
                throw $itemHandlingException;
82
            }
83
84
            $this->errors[$message->getCurrentIteration()] = $itemHandlingException->getMessage();
85
        }
86
87 2
        switch ($this->output->getVerbosity()) {
88
            case OutputInterface::VERBOSITY_NORMAL:
89 1
                $this->progressBar->setMaxSteps($message->getTotalIteration());
90 1
                $this->progressBar->advance();
91 1
                break;
92
93
            case OutputInterface::VERBOSITY_VERBOSE:
94
            case OutputInterface::VERBOSITY_VERY_VERBOSE:
95
            case OutputInterface::VERBOSITY_DEBUG:
96 1
                $this->io->comment(\sprintf('Row %d/%d',
97 1
                    $message->getCurrentIteration(),
98 1
                    $message->getTotalIteration()
99 1
                ));
100 1
                $this->io->definitionList(...$this->formatDataToDebug($message->getData()));
101 1
                break;
102
        }
103
104 2
        if ($this->stepByStep && $message->getCurrentIteration() < $message->getTotalIteration() && !$this->io->confirm('Continue?')) {
105
            $this->io->error('Import cancelled');
106
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
107
        }
108
    }
109
110 2
    public function end(Message $message)
111
    {
112 2
        if (OutputInterface::VERBOSITY_NORMAL === $this->output->getVerbosity()) {
113 1
            $this->progressBar->finish();
114 1
            $this->io->newLine(2);
115
        }
116
117 2
        ($this->handleEnd)($message, $this->errors);
118
119 2
        if ([] !== $this->errors) {
120
            $elements = [];
121
            foreach ($this->errors as $key => $value) {
122
                $elements[] = \sprintf('Line #%d: %s', $key, $value);
123
            }
124
125
            $this->io->error('Errors occured during import:');
126
            $this->io->listing($elements);
127
        }
128
    }
129
130 2
    public function batch(Message $message)
131
    {
132 2
        ($this->handleBatch)($message);
133
    }
134
135 2
    public function getBatchSize()
136
    {
137 2
        return $this->batchSize;
138
    }
139
140 1
    private function formatDataToDebug(mixed $data): array
141
    {
142 1
        if (null === $data) {
143
            return [];
144
        }
145
146 1
        if (\is_object($data)) {
147
            $this->serializer ??= new Serializer([new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter())]);
148
149
            if ($this->serializer instanceof NormalizableInterface) {
150
                $data = $this->serializer->normalize($data);
0 ignored issues
show
Bug introduced by
The method normalize() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

150
                /** @scrutinizer ignore-call */ 
151
                $data = $this->serializer->normalize($data);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
151
            }
152
        }
153
154 1
        $result = [];
155 1
        foreach ($data as $key => $value) {
156 1
            $result[] = [$key => $value];
157
        }
158
159 1
        return $result;
160
    }
161
}
162