PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.11.10
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.11.10
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / openspout / openspout / src / Reader / CSV / RowIterator.php

RowIterator.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.11.10, at vendor/openspout/openspout/src/Reader/CSV/RowIterator.php

250 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace OpenSpout\Reader\CSV;
4
5 use OpenSpout\Common\Entity\Row;
6 use OpenSpout\Common\Helper\EncodingHelper;
7 use OpenSpout\Common\Helper\GlobalFunctionsHelper;
8 use OpenSpout\Common\Manager\OptionsManagerInterface;
9 use OpenSpout\Reader\Common\Entity\Options;
10 use OpenSpout\Reader\CSV\Creator\InternalEntityFactory;
11 use OpenSpout\Reader\RowIteratorInterface;
12
13 /**
14 * Iterate over CSV rows.
15 */
16 class RowIterator implements RowIteratorInterface
17 {
18 /**
19 * Value passed to fgetcsv. 0 means "unlimited" (slightly slower but accomodates for very long lines).
20 */
21 public const MAX_READ_BYTES_PER_LINE = 0;
22
23 /** @var null|resource Pointer to the CSV file to read */
24 protected $filePointer;
25
26 /** @var int Number of read rows */
27 protected $numReadRows = 0;
28
29 /** @var null|Row Buffer used to store the current row, while checking if there are more rows to read */
30 protected $rowBuffer;
31
32 /** @var bool Indicates whether all rows have been read */
33 protected $hasReachedEndOfFile = false;
34
35 /** @var string Defines the character used to delimit fields (one character only) */
36 protected $fieldDelimiter;
37
38 /** @var string Defines the character used to enclose fields (one character only) */
39 protected $fieldEnclosure;
40
41 /** @var string Encoding of the CSV file to be read */
42 protected $encoding;
43
44 /** @var bool Whether empty rows should be returned or skipped */
45 protected $shouldPreserveEmptyRows;
46
47 /** @var \OpenSpout\Common\Helper\EncodingHelper Helper to work with different encodings */
48 protected $encodingHelper;
49
50 /** @var \OpenSpout\Reader\CSV\Creator\InternalEntityFactory Factory to create entities */
51 protected $entityFactory;
52
53 /** @var \OpenSpout\Common\Helper\GlobalFunctionsHelper Helper to work with global functions */
54 protected $globalFunctionsHelper;
55
56 /**
57 * @param resource $filePointer Pointer to the CSV file to read
58 */
59 public function __construct(
60 $filePointer,
61 OptionsManagerInterface $optionsManager,
62 EncodingHelper $encodingHelper,
63 InternalEntityFactory $entityFactory,
64 GlobalFunctionsHelper $globalFunctionsHelper
65 ) {
66 $this->filePointer = $filePointer;
67 $this->fieldDelimiter = $optionsManager->getOption(Options::FIELD_DELIMITER);
68 $this->fieldEnclosure = $optionsManager->getOption(Options::FIELD_ENCLOSURE);
69 $this->encoding = $optionsManager->getOption(Options::ENCODING);
70 $this->shouldPreserveEmptyRows = $optionsManager->getOption(Options::SHOULD_PRESERVE_EMPTY_ROWS);
71 $this->encodingHelper = $encodingHelper;
72 $this->entityFactory = $entityFactory;
73 $this->globalFunctionsHelper = $globalFunctionsHelper;
74 }
75
76 /**
77 * Rewind the Iterator to the first element.
78 *
79 * @see http://php.net/manual/en/iterator.rewind.php
80 */
81 #[\ReturnTypeWillChange]
82 public function rewind(): void
83 {
84 $this->rewindAndSkipBom();
85
86 $this->numReadRows = 0;
87 $this->rowBuffer = null;
88
89 $this->next();
90 }
91
92 /**
93 * Checks if current position is valid.
94 *
95 * @see http://php.net/manual/en/iterator.valid.php
96 */
97 #[\ReturnTypeWillChange]
98 public function valid(): bool
99 {
100 return $this->filePointer && !$this->hasReachedEndOfFile;
101 }
102
103 /**
104 * Move forward to next element. Reads data for the next unprocessed row.
105 *
106 * @see http://php.net/manual/en/iterator.next.php
107 *
108 * @throws \OpenSpout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8
109 */
110 #[\ReturnTypeWillChange]
111 public function next(): void
112 {
113 $this->hasReachedEndOfFile = $this->globalFunctionsHelper->feof($this->filePointer);
114
115 if (!$this->hasReachedEndOfFile) {
116 $this->readDataForNextRow();
117 }
118 }
119
120 /**
121 * Return the current element from the buffer.
122 *
123 * @see http://php.net/manual/en/iterator.current.php
124 */
125 #[\ReturnTypeWillChange]
126 public function current(): ?Row
127 {
128 return $this->rowBuffer;
129 }
130
131 /**
132 * Return the key of the current element.
133 *
134 * @see http://php.net/manual/en/iterator.key.php
135 */
136 #[\ReturnTypeWillChange]
137 public function key(): int
138 {
139 return $this->numReadRows;
140 }
141
142 /**
143 * Cleans up what was created to iterate over the object.
144 */
145 #[\ReturnTypeWillChange]
146 public function end(): void
147 {
148 // do nothing
149 }
150
151 /**
152 * This rewinds and skips the BOM if inserted at the beginning of the file
153 * by moving the file pointer after it, so that it is not read.
154 */
155 protected function rewindAndSkipBom()
156 {
157 $byteOffsetToSkipBom = $this->encodingHelper->getBytesOffsetToSkipBOM($this->filePointer, $this->encoding);
158
159 // sets the cursor after the BOM (0 means no BOM, so rewind it)
160 $this->globalFunctionsHelper->fseek($this->filePointer, $byteOffsetToSkipBom);
161 }
162
163 /**
164 * @throws \OpenSpout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8
165 */
166 protected function readDataForNextRow()
167 {
168 do {
169 $rowData = $this->getNextUTF8EncodedRow();
170 } while ($this->shouldReadNextRow($rowData));
171
172 if (false !== $rowData) {
173 // array_map will replace NULL values by empty strings
174 $rowDataBufferAsArray = array_map(function ($value) { return (string) $value; }, $rowData);
175 $this->rowBuffer = $this->entityFactory->createRowFromArray($rowDataBufferAsArray);
176 ++$this->numReadRows;
177 } else {
178 // If we reach this point, it means end of file was reached.
179 // This happens when the last lines are empty lines.
180 $this->hasReachedEndOfFile = true;
181 }
182 }
183
184 /**
185 * @param array|bool $currentRowData
186 *
187 * @return bool Whether the data for the current row can be returned or if we need to keep reading
188 */
189 protected function shouldReadNextRow($currentRowData)
190 {
191 $hasSuccessfullyFetchedRowData = (false !== $currentRowData);
192 $hasNowReachedEndOfFile = $this->globalFunctionsHelper->feof($this->filePointer);
193 $isEmptyLine = $this->isEmptyLine($currentRowData);
194
195 return
196 (!$hasSuccessfullyFetchedRowData && !$hasNowReachedEndOfFile)
197 || (!$this->shouldPreserveEmptyRows && $isEmptyLine)
198 ;
199 }
200
201 /**
202 * Returns the next row, converted if necessary to UTF-8.
203 * As fgetcsv() does not manage correctly encoding for non UTF-8 data,
204 * we remove manually whitespace with ltrim or rtrim (depending on the order of the bytes).
205 *
206 * @throws \OpenSpout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8
207 *
208 * @return array|false The row for the current file pointer, encoded in UTF-8 or FALSE if nothing to read
209 */
210 protected function getNextUTF8EncodedRow()
211 {
212 $encodedRowData = $this->globalFunctionsHelper->fgetcsv($this->filePointer, self::MAX_READ_BYTES_PER_LINE, $this->fieldDelimiter, $this->fieldEnclosure);
213 if (false === $encodedRowData) {
214 return false;
215 }
216
217 foreach ($encodedRowData as $cellIndex => $cellValue) {
218 switch ($this->encoding) {
219 case EncodingHelper::ENCODING_UTF16_LE:
220 case EncodingHelper::ENCODING_UTF32_LE:
221 // remove whitespace from the beginning of a string as fgetcsv() add extra whitespace when it try to explode non UTF-8 data
222 $cellValue = ltrim($cellValue);
223
224 break;
225
226 case EncodingHelper::ENCODING_UTF16_BE:
227 case EncodingHelper::ENCODING_UTF32_BE:
228 // remove whitespace from the end of a string as fgetcsv() add extra whitespace when it try to explode non UTF-8 data
229 $cellValue = rtrim($cellValue);
230
231 break;
232 }
233
234 $encodedRowData[$cellIndex] = $this->encodingHelper->attemptConversionToUTF8($cellValue, $this->encoding);
235 }
236
237 return $encodedRowData;
238 }
239
240 /**
241 * @param array|bool $lineData Array containing the cells value for the line
242 *
243 * @return bool Whether the given line is empty
244 */
245 protected function isEmptyLine($lineData)
246 {
247 return \is_array($lineData) && 1 === \count($lineData) && null === $lineData[0];
248 }
249 }
250