Passed
Branch feature/php7 (5f0634)
by Andy
02:37
created

Reader   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Test Coverage

Coverage 77.27%

Importance

Changes 14
Bugs 0 Features 0
Metric Value
wmc 39
eloc 71
c 14
b 0
f 0
dl 0
loc 228
ccs 68
cts 88
cp 0.7727
rs 9.28

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