1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Palmtree\Csv; |
4
|
|
|
|
5
|
|
|
use Palmtree\Csv\Formatter\FormatterInterface; |
6
|
|
|
use Palmtree\Csv\Formatter\StringFormatter; |
7
|
|
|
use Palmtree\Csv\Row\Row; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Reads a CSV file by loading each line into memory |
11
|
|
|
* one at a time. |
12
|
|
|
*/ |
13
|
|
|
class Reader extends AbstractCsv implements \Iterator, \Countable |
14
|
|
|
{ |
15
|
|
|
protected $fopenMode = 'r'; |
16
|
|
|
/** @var FormatterInterface[] */ |
17
|
|
|
protected $formatters = []; |
18
|
|
|
/** @var int */ |
19
|
|
|
protected $index = 0; |
20
|
|
|
/** @var array */ |
21
|
|
|
protected $headers; |
22
|
|
|
/** @var Row */ |
23
|
|
|
protected $row; |
24
|
|
|
/** @var string */ |
25
|
|
|
protected $escapeCharacter = "\0"; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @param $file |
29
|
|
|
* |
30
|
|
|
* @return array |
31
|
|
|
*/ |
32
|
|
|
public static function read($file) |
33
|
|
|
{ |
34
|
|
|
$csv = new static($file); |
35
|
|
|
|
36
|
|
|
return iterator_to_array($csv); |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @return array |
41
|
|
|
*/ |
42
|
|
|
public function getHeaders() |
43
|
|
|
{ |
44
|
|
|
return $this->headers; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @param mixed $key |
49
|
|
|
* @param FormatterInterface $formatter |
50
|
|
|
* |
51
|
|
|
* @return $this |
52
|
|
|
*/ |
53
|
|
|
public function addFormatter($key, FormatterInterface $formatter) |
54
|
|
|
{ |
55
|
|
|
$this->formatters[$key] = $formatter; |
56
|
|
|
|
57
|
|
|
return $this; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @param array|\Traversable $formatters |
62
|
|
|
* |
63
|
|
|
* @return $this |
64
|
|
|
*/ |
65
|
|
|
public function addFormatters($formatters) |
66
|
|
|
{ |
67
|
|
|
foreach ($formatters as $key => $formatter) { |
68
|
|
|
$this->addFormatter($key, $formatter); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
return $this; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @param mixed $key |
76
|
|
|
* |
77
|
|
|
* @return null|FormatterInterface |
78
|
|
|
*/ |
79
|
|
|
public function getFormatter($key) |
80
|
|
|
{ |
81
|
|
|
if (!isset($this->formatters[$key])) { |
82
|
|
|
$this->formatters[$key] = new StringFormatter(); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
return $this->formatters[$key]; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* @return string |
90
|
|
|
*/ |
91
|
|
|
public function getEscapeCharacter() |
92
|
|
|
{ |
93
|
|
|
return $this->escapeCharacter; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* @param string $escapeCharacter |
98
|
|
|
* |
99
|
|
|
* @return AbstractCsv |
100
|
|
|
*/ |
101
|
|
|
public function setEscapeCharacter($escapeCharacter) |
102
|
|
|
{ |
103
|
|
|
$this->escapeCharacter = $escapeCharacter; |
104
|
|
|
|
105
|
|
|
return $this; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Returns an array of cells for the next row. |
110
|
|
|
* |
111
|
|
|
* @return Row |
112
|
|
|
*/ |
113
|
|
|
protected function getNextRow() |
114
|
|
|
{ |
115
|
|
|
if (!$this->getFileHandle()) { |
116
|
|
|
$this->createFileHandle(); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
$row = fgetcsv( |
120
|
|
|
$this->getFileHandle(), |
121
|
|
|
null, |
122
|
|
|
$this->getDelimiter(), |
123
|
|
|
$this->getEnclosure(), |
124
|
|
|
$this->getEscapeCharacter() |
125
|
|
|
); |
126
|
|
|
|
127
|
|
|
if (!$row) { |
|
|
|
|
128
|
|
|
return null; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
$row = new Row($row, $this); |
132
|
|
|
|
133
|
|
|
return $row; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @inheritDoc |
138
|
|
|
*/ |
139
|
|
|
public function current() |
140
|
|
|
{ |
141
|
|
|
return $this->row; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* @inheritDoc |
146
|
|
|
*/ |
147
|
|
|
public function next() |
148
|
|
|
{ |
149
|
|
|
++$this->index; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* @inheritDoc |
154
|
|
|
*/ |
155
|
|
|
public function key() |
156
|
|
|
{ |
157
|
|
|
return $this->index; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* @inheritDoc |
162
|
|
|
*/ |
163
|
|
|
public function valid() |
164
|
|
|
{ |
165
|
|
|
$this->row = $this->getNextRow(); |
166
|
|
|
|
167
|
|
|
return $this->row instanceof Row; |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* @inheritDoc |
172
|
|
|
*/ |
173
|
|
|
public function rewind() |
174
|
|
|
{ |
175
|
|
|
if ($this->getFileHandle()) { |
176
|
|
|
rewind($this->getFileHandle()); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
$this->index = 0; |
180
|
|
|
|
181
|
|
|
if ($this->hasHeaders()) { |
182
|
|
|
$this->headers = $this->getNextRow(); |
|
|
|
|
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* @inheritDoc |
188
|
|
|
*/ |
189
|
|
|
public function count() |
190
|
|
|
{ |
191
|
|
|
return count(iterator_to_array($this)); |
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.