PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.4
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.4
6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 All 196 releases
fluentform / app / Services / Spout / Reader / CSV / RowIterator.php

RowIterator.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 6.2.4, at app/Services/Spout/Reader/CSV/RowIterator.php

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