CsvReader   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 149
Duplicated Lines 0 %

Test Coverage

Coverage 98.18%

Importance

Changes 0
Metric Value
eloc 58
dl 0
loc 149
ccs 54
cts 55
cp 0.9818
rs 10
c 0
b 0
f 0
wmc 21

11 Methods

Rating   Name   Duplication   Size   Complexity  
A current() 0 15 6
A __construct() 0 37 4
A isDenormalizable() 0 3 2
A getDto() 0 3 1
A rewind() 0 9 2
A count() 0 3 1
A index() 0 3 1
A valid() 0 3 1
A key() 0 3 1
A getFile() 0 3 1
A next() 0 4 1
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\Reader;
15
16
class CsvReader implements ReaderInterface
17
{
18
    /**
19
     * @var string
20
     */
21
    final public const CONTEXT_DELIMITER = 'csv_delimiter';
22
23
    /**
24
     * @var string
25
     */
26
    final public const CONTEXT_ENCLOSURE = 'csv_enclosure';
27
28
    /**
29
     * @var string
30
     */
31
    final public const CONTEXT_ESCAPE_CHAR = 'csv_escape_char';
32
33
    /**
34
     * @var string
35
     */
36
    final public const CONTEXT_HEADERS = 'csv_headers';
37
38
    /**
39
     * @var string
40
     */
41
    final public const CONTEXT_NO_HEADERS = 'no_headers';
42
43
    private readonly \SplFileInfo $file;
44
45
    private readonly \SplFileObject $iterator;
46
47
    private int $count = 0;
48
49
    private int $index = 1;
50
51
    private array $defaultContext = [
52
        self::CONTEXT_DELIMITER => ',',
53
        self::CONTEXT_ENCLOSURE => '"',
54
        self::CONTEXT_ESCAPE_CHAR => '',
55
        self::CONTEXT_HEADERS => [],
56
        self::CONTEXT_NO_HEADERS => false,
57
    ];
58
59 13
    public function __construct(
60
        string $filePath,
61
        private readonly ?string $dto = null,
62
        array $defaultContext = [],
63
    ) {
64 13
        $this->file = new \SplFileInfo($filePath);
0 ignored issues
show
Bug introduced by
The property file is declared read-only in IQ2i\DataImporter\Reader\CsvReader.
Loading history...
65 13
        if (!$this->file->isReadable()) {
66 1
            throw new \InvalidArgumentException('The file '.$this->file->getFilename().' is not readable.');
67
        }
68
69 12
        $this->defaultContext = \array_merge($this->defaultContext, $defaultContext);
70
71 12
        $this->iterator = $this->file->openFile();
0 ignored issues
show
Bug introduced by
The property iterator is declared read-only in IQ2i\DataImporter\Reader\CsvReader.
Loading history...
72 12
        $this->iterator->setFlags(
73 12
            \SplFileObject::READ_CSV |
74 12
            \SplFileObject::SKIP_EMPTY |
75 12
            \SplFileObject::READ_AHEAD |
76 12
            \SplFileObject::DROP_NEW_LINE
77 12
        );
78 12
        $this->iterator->setCsvControl(
79 12
            $this->defaultContext[self::CONTEXT_DELIMITER],
80 12
            $this->defaultContext[self::CONTEXT_ENCLOSURE],
81 12
            $this->defaultContext[self::CONTEXT_ESCAPE_CHAR]
82 12
        );
83
84 12
        if (!$this->defaultContext[self::CONTEXT_NO_HEADERS]) {
85 11
            $this->rewind();
86 11
            $this->defaultContext[self::CONTEXT_HEADERS] = $this->iterator->current();
87
        }
88
89 12
        $this->rewind();
90 12
        while ($this->valid()) {
91 12
            ++$this->count;
92 12
            $this->next();
93
        }
94
95 12
        $this->rewind();
96
    }
97
98 2
    public function getDto(): ?string
99
    {
100 2
        return $this->dto;
101
    }
102
103 10
    public function isDenormalizable(): bool
104
    {
105 10
        return null !== $this->dto && !empty($this->defaultContext[self::CONTEXT_HEADERS]);
106
    }
107
108 12
    public function getFile(): \SplFileInfo
109
    {
110 12
        return $this->file;
111
    }
112
113 12
    public function index(): mixed
114
    {
115 12
        return $this->index;
116
    }
117
118 12
    public function current(): array
119
    {
120 12
        if (!$this->valid()) {
121 2
            return [];
122
        }
123
124 12
        if (empty($this->defaultContext[self::CONTEXT_HEADERS])) {
125 1
            return $this->iterator->current();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->iterator->current() could return the type string which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
126
        }
127
128 11
        if ((\is_countable($this->defaultContext[self::CONTEXT_HEADERS]) ? \count($this->defaultContext[self::CONTEXT_HEADERS]) : 0) === (\is_countable($this->iterator->current()) ? \count($this->iterator->current()) : 0)) {
0 ignored issues
show
Bug introduced by
It seems like $this->iterator->current() can also be of type string; however, parameter $value of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

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

128
        if ((\is_countable($this->defaultContext[self::CONTEXT_HEADERS]) ? \count($this->defaultContext[self::CONTEXT_HEADERS]) : 0) === (\is_countable($this->iterator->current()) ? \count(/** @scrutinizer ignore-type */ $this->iterator->current()) : 0)) {
Loading history...
129 11
            return \array_combine($this->defaultContext[self::CONTEXT_HEADERS], $this->iterator->current());
0 ignored issues
show
Bug introduced by
It seems like $this->iterator->current() can also be of type string; however, parameter $values of array_combine() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

129
            return \array_combine($this->defaultContext[self::CONTEXT_HEADERS], /** @scrutinizer ignore-type */ $this->iterator->current());
Loading history...
130
        }
131
132
        return [];
133
    }
134
135 12
    public function next(): void
136
    {
137 12
        $this->iterator->next();
138 12
        ++$this->index;
139
    }
140
141 2
    public function key(): mixed
142
    {
143 2
        return $this->iterator->key();
144
    }
145
146 12
    public function valid(): bool
147
    {
148 12
        return $this->iterator->valid();
149
    }
150
151 12
    public function rewind(): void
152
    {
153 12
        $this->iterator->rewind();
154
155 12
        if (!empty($this->defaultContext[self::CONTEXT_HEADERS])) {
156 11
            $this->next();
157
        }
158
159 12
        $this->index = 1;
160
    }
161
162 12
    public function count(): int
163
    {
164 12
        return $this->count;
165
    }
166
}
167