PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.19
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.19
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
fluent-cart / vendor / openspout / openspout / src / Reader / CSV / RowIterator.php

RowIterator.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.3.19, at vendor/openspout/openspout/src/Reader/CSV/RowIterator.php

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