PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.14.1
Independent Analytics – WordPress Analytics Plugin v2.14.1
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / vendor / league / csv / src / Reader.php
independent-analytics / vendor / league / csv / src Last commit date
Polyfill 10 months ago AbstractCsv.php 10 months ago ByteSequence.php 10 months ago CannotInsertRecord.php 10 months ago CharsetConverter.php 10 months ago ColumnConsistency.php 10 months ago EncloseField.php 10 months ago EscapeFormula.php 10 months ago Exception.php 10 months ago HTMLConverter.php 10 months ago Info.php 10 months ago InvalidArgument.php 10 months ago MapIterator.php 10 months ago RFC4180Field.php 10 months ago Reader.php 10 months ago ResultSet.php 10 months ago Statement.php 10 months ago Stream.php 10 months ago SyntaxError.php 10 months ago TabularDataReader.php 10 months ago UnableToProcessCsv.php 10 months ago UnavailableFeature.php 10 months ago UnavailableStream.php 10 months ago Writer.php 10 months ago XMLConverter.php 10 months ago functions.php 10 months ago functions_include.php 10 months ago
Reader.php
366 lines
1 <?php
2
3 /**
4 * League.Csv (https://csv.thephpleague.com)
5 *
6 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 declare (strict_types=1);
12 namespace IAWPSCOPED\League\Csv;
13
14 use CallbackFilterIterator;
15 use Iterator;
16 use JsonSerializable;
17 use IAWPSCOPED\League\Csv\Polyfill\EmptyEscapeParser;
18 use SplFileObject;
19 use function array_combine;
20 use function array_filter;
21 use function array_pad;
22 use function array_slice;
23 use function array_unique;
24 use function count;
25 use function is_array;
26 use function iterator_count;
27 use function iterator_to_array;
28 use function mb_strlen;
29 use function mb_substr;
30 use function strlen;
31 use function substr;
32 use const PHP_VERSION_ID;
33 use const STREAM_FILTER_READ;
34 /**
35 * A class to parse and read records from a CSV document.
36 * @internal
37 */
38 class Reader extends AbstractCsv implements TabularDataReader, JsonSerializable
39 {
40 protected const STREAM_FILTER_MODE = STREAM_FILTER_READ;
41 /**
42 * header offset.
43 *
44 * @var int|null
45 */
46 protected $header_offset;
47 /**
48 * header record.
49 *
50 * @var array<string>
51 */
52 protected $header = [];
53 /**
54 * records count.
55 *
56 * @var int
57 */
58 protected $nb_records = -1;
59 /**
60 * @var bool
61 */
62 protected $is_empty_records_included = \false;
63 /**
64 * {@inheritdoc}
65 */
66 public static function createFromPath(string $path, string $open_mode = 'r', $context = null)
67 {
68 return parent::createFromPath($path, $open_mode, $context);
69 }
70 /**
71 * {@inheritdoc}
72 */
73 protected function resetProperties() : void
74 {
75 $this->nb_records = -1;
76 $this->header = [];
77 }
78 /**
79 * Returns the header offset.
80 *
81 * If no CSV header offset is set this method MUST return null
82 *
83 */
84 public function getHeaderOffset() : ?int
85 {
86 return $this->header_offset;
87 }
88 /**
89 * {@inheritDoc}
90 */
91 public function getHeader() : array
92 {
93 if (null === $this->header_offset) {
94 return $this->header;
95 }
96 if ([] !== $this->header) {
97 return $this->header;
98 }
99 $this->header = $this->setHeader($this->header_offset);
100 return $this->header;
101 }
102 /**
103 * Determine the CSV record header.
104 *
105 * @throws Exception If the header offset is set and no record is found or is the empty array
106 *
107 * @return string[]
108 */
109 protected function setHeader(int $offset) : array
110 {
111 $header = $this->seekRow($offset);
112 if (\in_array($header, [[], [null]], \true)) {
113 throw SyntaxError::dueToHeaderNotFound($offset);
114 }
115 if (0 !== $offset) {
116 return $header;
117 }
118 $header = $this->removeBOM($header, mb_strlen($this->getInputBOM()), $this->enclosure);
119 if ([''] === $header) {
120 throw SyntaxError::dueToHeaderNotFound($offset);
121 }
122 return $header;
123 }
124 /**
125 * Returns the row at a given offset.
126 */
127 protected function seekRow(int $offset) : array
128 {
129 foreach ($this->getDocument() as $index => $record) {
130 if ($offset === $index) {
131 return $record;
132 }
133 }
134 return [];
135 }
136 /**
137 * Returns the document as an Iterator.
138 */
139 protected function getDocument() : Iterator
140 {
141 if (70400 > PHP_VERSION_ID && '' === $this->escape) {
142 $this->document->setCsvControl($this->delimiter, $this->enclosure);
143 return EmptyEscapeParser::parse($this->document);
144 }
145 $this->document->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD);
146 $this->document->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
147 $this->document->rewind();
148 return $this->document;
149 }
150 /**
151 * Strip the BOM sequence from a record.
152 *
153 * @param string[] $record
154 *
155 * @return string[]
156 */
157 protected function removeBOM(array $record, int $bom_length, string $enclosure) : array
158 {
159 if (0 === $bom_length) {
160 return $record;
161 }
162 $record[0] = mb_substr($record[0], $bom_length);
163 if ($enclosure . $enclosure != substr($record[0] . $record[0], strlen($record[0]) - 1, 2)) {
164 return $record;
165 }
166 $record[0] = substr($record[0], 1, -1);
167 return $record;
168 }
169 /**
170 * {@inheritdoc}
171 */
172 public function fetchColumn($index = 0) : Iterator
173 {
174 return ResultSet::createFromTabularDataReader($this)->fetchColumn($index);
175 }
176 /**
177 * {@inheritdoc}
178 */
179 public function fetchOne(int $nth_record = 0) : array
180 {
181 return ResultSet::createFromTabularDataReader($this)->fetchOne($nth_record);
182 }
183 /**
184 * {@inheritdoc}
185 */
186 public function fetchPairs($offset_index = 0, $value_index = 1) : Iterator
187 {
188 return ResultSet::createFromTabularDataReader($this)->fetchPairs($offset_index, $value_index);
189 }
190 /**
191 * {@inheritdoc}
192 */
193 public function count() : int
194 {
195 if (-1 === $this->nb_records) {
196 $this->nb_records = iterator_count($this->getRecords());
197 }
198 return $this->nb_records;
199 }
200 /**
201 * {@inheritdoc}
202 */
203 public function getIterator() : Iterator
204 {
205 return $this->getRecords();
206 }
207 /**
208 * {@inheritdoc}
209 */
210 public function jsonSerialize() : array
211 {
212 return iterator_to_array($this->getRecords(), \false);
213 }
214 /**
215 * {@inheritDoc}
216 */
217 public function getRecords(array $header = []) : Iterator
218 {
219 $header = $this->computeHeader($header);
220 $normalized = function ($record) : bool {
221 return is_array($record) && ($this->is_empty_records_included || $record != [null]);
222 };
223 $bom = '';
224 if (!$this->is_input_bom_included) {
225 $bom = $this->getInputBOM();
226 }
227 $document = $this->getDocument();
228 $records = $this->stripBOM(new CallbackFilterIterator($document, $normalized), $bom);
229 if (null !== $this->header_offset) {
230 $records = new CallbackFilterIterator($records, function (array $record, int $offset) : bool {
231 return $offset !== $this->header_offset;
232 });
233 }
234 if ($this->is_empty_records_included) {
235 $normalized_empty_records = static function (array $record) : array {
236 if ([null] === $record) {
237 return [];
238 }
239 return $record;
240 };
241 return $this->combineHeader(new MapIterator($records, $normalized_empty_records), $header);
242 }
243 return $this->combineHeader($records, $header);
244 }
245 /**
246 * Returns the header to be used for iteration.
247 *
248 * @param string[] $header
249 *
250 * @throws Exception If the header contains non unique column name
251 *
252 * @return string[]
253 */
254 protected function computeHeader(array $header)
255 {
256 if ([] === $header) {
257 $header = $this->getHeader();
258 }
259 if ($header !== ($filtered_header = array_filter($header, 'is_string'))) {
260 throw SyntaxError::dueToInvalidHeaderColumnNames();
261 }
262 if ($header !== array_unique($filtered_header)) {
263 throw SyntaxError::dueToDuplicateHeaderColumnNames($header);
264 }
265 return $header;
266 }
267 /**
268 * Combine the CSV header to each record if present.
269 *
270 * @param string[] $header
271 */
272 protected function combineHeader(Iterator $iterator, array $header) : Iterator
273 {
274 if ([] === $header) {
275 return $iterator;
276 }
277 $field_count = count($header);
278 $mapper = static function (array $record) use($header, $field_count) : array {
279 if (count($record) != $field_count) {
280 $record = array_slice(array_pad($record, $field_count, null), 0, $field_count);
281 }
282 /** @var array<string|null> $assocRecord */
283 $assocRecord = array_combine($header, $record);
284 return $assocRecord;
285 };
286 return new MapIterator($iterator, $mapper);
287 }
288 /**
289 * Strip the BOM sequence from the returned records if necessary.
290 */
291 protected function stripBOM(Iterator $iterator, string $bom) : Iterator
292 {
293 if ('' === $bom) {
294 return $iterator;
295 }
296 $bom_length = mb_strlen($bom);
297 $mapper = function (array $record, int $index) use($bom_length) : array {
298 if (0 !== $index) {
299 return $record;
300 }
301 $record = $this->removeBOM($record, $bom_length, $this->enclosure);
302 if ([''] === $record) {
303 return [null];
304 }
305 return $record;
306 };
307 $filter = function (array $record) : bool {
308 return $this->is_empty_records_included || $record != [null];
309 };
310 return new CallbackFilterIterator(new MapIterator($iterator, $mapper), $filter);
311 }
312 /**
313 * Selects the record to be used as the CSV header.
314 *
315 * Because the header is represented as an array, to be valid
316 * a header MUST contain only unique string value.
317 *
318 * @param int|null $offset the header record offset
319 *
320 * @throws Exception if the offset is a negative integer
321 *
322 * @return static
323 */
324 public function setHeaderOffset(?int $offset) : self
325 {
326 if ($offset === $this->header_offset) {
327 return $this;
328 }
329 if (null !== $offset && 0 > $offset) {
330 throw InvalidArgument::dueToInvalidHeaderOffset($offset, __METHOD__);
331 }
332 $this->header_offset = $offset;
333 $this->resetProperties();
334 return $this;
335 }
336 /**
337 * Enable skipping empty records.
338 */
339 public function skipEmptyRecords() : self
340 {
341 if ($this->is_empty_records_included) {
342 $this->is_empty_records_included = \false;
343 $this->nb_records = -1;
344 }
345 return $this;
346 }
347 /**
348 * Disable skipping empty records.
349 */
350 public function includeEmptyRecords() : self
351 {
352 if (!$this->is_empty_records_included) {
353 $this->is_empty_records_included = \true;
354 $this->nb_records = -1;
355 }
356 return $this;
357 }
358 /**
359 * Tells whether empty records are skipped by the instance.
360 */
361 public function isEmptyRecordsIncluded() : bool
362 {
363 return $this->is_empty_records_included;
364 }
365 }
366