Passed
Push — master ( 25c805...999286 )
by Andy
02:56 queued 11s
created

Reader   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 231
Duplicated Lines 0 %

Test Coverage

Coverage 77.65%

Importance

Changes 17
Bugs 0 Features 0
Metric Value
wmc 39
eloc 69
dl 0
loc 231
ccs 66
cts 85
cp 0.7765
rs 9.28
c 17
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A valid() 0 5 1
A getHeaders() 0 7 3
A toArray() 0 8 2
A getHeader() 0 3 1
A getOffset() 0 3 1
A current() 0 3 1
A setHeaderNormalizer() 0 5 1
A getCurrentRow() 0 17 6
A getNormalizer() 0 13 4
A rewind() 0 20 4
A setOffset() 0 5 1
A setDefaultNormalizer() 0 5 1
A next() 0 3 1
A getHeaderNormalizer() 0 7 2
A setStripBom() 0 5 1
A getHeaderOffset() 0 3 1
A getDefaultNormalizer() 0 3 1
A addNormalizer() 0 5 1
A read() 0 3 1
A setHeaderOffset() 0 5 1
A getOpenMode() 0 3 1
A key() 0 3 1
A addNormalizers() 0 7 2
1
<?php
2
3
namespace Palmtree\Csv;
4
5
use Palmtree\Csv\Normalizer\NormalizerInterface;
6
use Palmtree\Csv\Normalizer\NullNormalizer;
7
use Palmtree\Csv\Row\Row;
8
use Palmtree\Csv\Util\StringUtil;
9
10
/**
11
 * Reads a CSV file by loading each line into memory one at a time.
12
 */
13
class Reader extends AbstractCsvDocument implements \Iterator
14
{
15
    /** @var string */
16
    private $defaultNormalizer = NullNormalizer::class;
17
    /** @var NormalizerInterface|null */
18
    private $headerNormalizer;
19
    /** @var NormalizerInterface[] */
20
    private $normalizers = [];
21
    /** @var Row|null */
22
    private $headers;
23
    /** @var Row */
24
    private $row;
25
    /** @var string|null */
26
    private $stripBom = StringUtil::BOM_UTF8;
27
    /** @var int */
28
    private $offset = 0;
29
    /** @var int */
30
    private $headerOffset = 0;
31
32
    public static function read(string $filePath, bool $hasHeaders = true): self
33
    {
34
        return new self($filePath, $hasHeaders);
35
    }
36
37 3
    public function getHeaders(): ?Row
38
    {
39 3
        if ($this->hasHeaders && $this->headers === null) {
40 3
            $this->rewind();
41
        }
42
43 3
        return $this->headers;
44
    }
45
46
    /**
47
     * @param string|int $key
48
     *
49
     * @return string|int
50
     */
51 10
    public function getHeader($key)
52
    {
53 10
        return $this->headers[$key] ?? $key;
54
    }
55
56
    public function setHeaderNormalizer(NormalizerInterface $headerNormalizer): self
57
    {
58
        $this->headerNormalizer = $headerNormalizer;
59
60
        return $this;
61
    }
62
63 8
    public function getHeaderNormalizer(): NormalizerInterface
64
    {
65 8
        if ($this->headerNormalizer === null) {
66 8
            $this->headerNormalizer = new NullNormalizer();
67
        }
68
69 8
        return $this->headerNormalizer;
70
    }
71
72
    public function addNormalizer(string $key, NormalizerInterface $normalizer): self
73
    {
74
        $this->normalizers[$key] = $normalizer;
75
76
        return $this;
77
    }
78
79
    public function addNormalizers(iterable $normalizers): self
80
    {
81
        foreach ($normalizers as $key => $normalizer) {
82
            $this->addNormalizer($key, $normalizer);
83
        }
84
85
        return $this;
86
    }
87
88
    /**
89
     * @param string|int $key
90
     */
91 10
    public function getNormalizer($key): NormalizerInterface
92
    {
93 10
        if ($this->hasHeaders && \is_int($key)) {
94 8
            $this->normalizers[$key] = $this->getHeaderNormalizer();
95
        }
96
97 10
        if (!isset($this->normalizers[$key])) {
98 5
            $class = $this->getDefaultNormalizer();
99
100 5
            $this->normalizers[$key] = new $class();
101
        }
102
103 10
        return $this->normalizers[$key];
104
    }
105
106
    /**
107
     * Reads the next line in the CSV file and returns a Row object from it.
108
     */
109 7
    private function getCurrentRow(): ?Row
110
    {
111 7
        $cells = $this->getDocument()->current();
112
113 7
        if (!\is_array($cells) || $cells == [null]) {
114 5
            return null;
115
        }
116
117 7
        if ($this->key() === 0 && $this->stripBom) {
118 5
            $stripped = StringUtil::stripBom($cells[0], $this->stripBom);
119
120 5
            if ($stripped !== $cells[0]) {
121 1
                $cells[0] = \trim($stripped, $this->enclosure);
122
            }
123
        }
124
125 7
        return new Row($cells, $this);
126
    }
127
128
    /**
129
     * @inheritDoc
130
     */
131 5
    public function current(): Row
132
    {
133 5
        return $this->row;
134
    }
135
136
    /**
137
     * @inheritDoc
138
     */
139 5
    public function next(): void
140
    {
141 5
        $this->getDocument()->next();
142 5
    }
143
144
    /**
145
     * @inheritDoc
146
     */
147 7
    public function key(): int
148
    {
149 7
        return $this->getDocument()->key();
150
    }
151
152
    /**
153
     * @inheritDoc
154
     */
155 5
    public function valid(): bool
156
    {
157 5
        $this->row = $this->getCurrentRow();
158
159 5
        return $this->row instanceof Row;
160
    }
161
162
    /**
163
     * @inheritDoc
164
     */
165 7
    public function rewind(): void
166
    {
167 7
        $this->getDocument()->rewind();
168
169 7
        $dataOffset = $this->offset + $this->headerOffset;
170 7
        if ($this->hasHeaders) {
171 5
            if ($this->headerOffset) {
172 1
                $this->getDocument()->seek($this->headerOffset);
173
            }
174
175
            // Set headers to null first so the header row is a zero-based array and can be used
176
            // to set the array keys of all other rows.
177 5
            $this->headers = null;
178 5
            $this->headers = $this->getCurrentRow();
179
180 5
            ++$dataOffset;
181
        }
182
183 7
        if ($dataOffset > 0) {
184 5
            $this->getDocument()->seek($dataOffset);
185
        }
186 7
    }
187
188
    public function setDefaultNormalizer(string $defaultNormalizer): self
189
    {
190
        $this->defaultNormalizer = $defaultNormalizer;
191
192
        return $this;
193
    }
194
195 5
    public function getDefaultNormalizer(): string
196
    {
197 5
        return $this->defaultNormalizer;
198
    }
199
200 1
    public function setStripBom(?string $stripBom): self
201
    {
202 1
        $this->stripBom = $stripBom;
203
204 1
        return $this;
205
    }
206
207 1
    public function setOffset(int $offset): self
208
    {
209 1
        $this->offset = $offset;
210
211 1
        return $this;
212
    }
213
214
    public function getOffset(): int
215
    {
216
        return $this->offset;
217
    }
218
219 1
    public function setHeaderOffset(int $headerOffset): self
220
    {
221 1
        $this->headerOffset = $headerOffset;
222
223 1
        return $this;
224
    }
225
226
    public function getHeaderOffset(): int
227
    {
228
        return $this->headerOffset;
229
    }
230
231 3
    public function toArray(): array
232
    {
233 3
        $result = [];
234 3
        foreach ($this as $row) {
235 3
            $result[] = $row->toArray();
236
        }
237
238 3
        return $result;
239
    }
240
241 7
    protected function getOpenMode(): string
242
    {
243 7
        return 'r';
244
    }
245
}
246