Passed
Push — update-phpstan ( 75ec58 )
by Jonathan
03:24
created

Reader::getCurrentRow()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.0312

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 0
dl 0
loc 15
ccs 7
cts 8
cp 0.875
crap 4.0312
rs 9.7666
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace EasyCSV;
6
7
use LogicException;
8
use function array_combine;
9
use function array_filter;
10
use function is_array;
11
use function is_string;
12
use function mb_strpos;
13
use function sprintf;
14
use function str_getcsv;
15
use function str_replace;
16
17
class Reader extends AbstractBase
18
{
19
    /** @var bool */
20
    private $headersInFirstRow = true;
21
22
    /** @var string[]|bool */
23
    private $headers = false;
24
25
    /** @var bool */
26
    private $init;
27
28
    /** @var bool|int */
29
    private $headerLine = false;
30
31
    /** @var bool|int */
32
    private $lastLine = false;
33
34
    /** @var bool */
35
    private $isNeedBOMRemove = true;
36
37 4
    public function __construct(string $path, string $mode = 'r+', bool $headersInFirstRow = true)
38
    {
39 4
        parent::__construct($path, $mode);
40
41 4
        $this->headersInFirstRow = $headersInFirstRow;
42 4
    }
43
44
    /**
45
     * @return string[]|bool
46
     */
47 6
    public function getHeaders()
48
    {
49 6
        $this->init();
50
51 6
        return $this->headers;
52
    }
53
54
    /**
55
     * @return string[]|bool
56
     */
57 26
    public function getRow()
58
    {
59 26
        $this->init();
60
61 26
        if ($this->isEof()) {
62 10
            return false;
63
        }
64
65 26
        $row     = $this->getCurrentRow();
66 26
        $isEmpty = $this->rowIsEmpty($row);
67
68 26
        if ($this->isEof() === false) {
0 ignored issues
show
introduced by
The condition $this->isEof() === false is always true.
Loading history...
69 26
            $this->getHandle()->next();
70
        }
71
72 26
        if ($isEmpty === false) {
73 26
            return ($this->headers !== false && is_array($this->headers)) ? array_combine($this->headers, $row) : $row;
74
        }
75
76 4
        return $this->getRow();
77
    }
78
79 30
    public function isEof() : bool
80
    {
81 30
        return $this->getHandle()->eof();
82
    }
83
84
    /**
85
     * @return mixed[]
86
     */
87 8
    public function getAll() : array
88
    {
89 8
        $data = [];
90 8
        while ($row = $this->getRow()) {
91 8
            $data[] = $row;
92
        }
93
94 8
        return $data;
95
    }
96
97 6
    public function getLineNumber() : int
98
    {
99 6
        return $this->getHandle()->key();
100
    }
101
102
    /**
103
     * @return int|bool
104
     */
105 4
    public function getLastLineNumber()
106
    {
107 4
        if ($this->lastLine !== false) {
108 2
            return $this->lastLine;
109
        }
110
111 4
        $this->getHandle()->seek($this->getHandle()->getSize());
112 4
        $lastLine = $this->getHandle()->key();
113
114 4
        $this->getHandle()->rewind();
115
116 4
        return $this->lastLine = $lastLine;
117
    }
118
119
    /**
120
     * @return string[]
121
     */
122 28
    public function getCurrentRow() : array
123
    {
124 28
        $current = $this->getHandle()->current();
125
126 28
        if (! is_string($current)) {
127
            return [];
128
        }
129
130 28
        if ($this->isNeedBOMRemove && mb_strpos($current, "\xEF\xBB\xBF", 0, 'utf-8') === 0) {
131 2
            $this->isNeedBOMRemove = false;
132
133 2
            $current = str_replace("\xEF\xBB\xBF", '', $current);
134
        }
135
136 28
        return str_getcsv($current, $this->delimiter, $this->enclosure);
137
    }
138
139 12
    public function advanceTo(int $lineNumber) : void
140
    {
141 12
        if ($this->headerLine > $lineNumber) {
142 2
            throw new LogicException(sprintf(
143 2
                'Line Number %s is before the header line that was set',
144 2
                $lineNumber
145
            ));
146 10
        } elseif ($this->headerLine === $lineNumber) {
147 2
            throw new LogicException(sprintf(
148 2
                'Line Number %s is equal to the header line that was set',
149 2
                $lineNumber
150
            ));
151
        }
152
153 8
        if ($lineNumber > 0) {
154 8
            $this->getHandle()->seek($lineNumber - 1);
155
        } // check the line before
156
157 8
        if ($this->isEof()) {
158 2
            throw new LogicException(sprintf(
159 2
                'Line Number %s is past the end of the file',
160 2
                $lineNumber
161
            ));
162
        }
163
164 6
        $this->getHandle()->seek($lineNumber);
165 6
    }
166
167 6
    public function setHeaderLine(int $lineNumber) : bool
168
    {
169 6
        if ($lineNumber === 0) {
170 2
            return false;
171
        }
172
173 4
        $this->headersInFirstRow = false;
174
175 4
        $this->headerLine = $lineNumber;
176
177 4
        $this->getHandle()->seek($lineNumber);
178
179
        // get headers
180 4
        $this->headers = $this->getRow();
181
182 4
        return true;
183
    }
184
185 26
    protected function init() : void
186
    {
187 26
        if ($this->init === true) {
188 24
            return;
189
        }
190
191 26
        $this->init = true;
192
193 26
        if ($this->headersInFirstRow !== true) {
194 6
            return;
195
        }
196
197 20
        $this->getHandle()->rewind();
198
199 20
        $this->headerLine = 0;
200
201 20
        $this->headers = $this->getRow();
202 20
    }
203
204
    /**
205
     * @param string[]|null[] $row
206
     */
207 26
    protected function rowIsEmpty(array $row) : bool
208
    {
209 26
        $emptyRow               = ($row === [null]);
210 26
        $emptyRowWithDelimiters = (array_filter($row) === []);
211 26
        $isEmpty                = false;
212
213 26
        if ($emptyRow) {
214 4
            $isEmpty = true;
215
216 4
            return $isEmpty;
217 26
        } elseif ($emptyRowWithDelimiters) {
218 2
            $isEmpty = true;
219
220 2
            return $isEmpty;
221
        }
222
223 26
        return $isEmpty;
224
    }
225
}
226