PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.15.0
Independent Analytics – WordPress Analytics Plugin v2.15.0
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
AbstractCsv.php 4 days ago ByteSequence.php 4 days ago CannotInsertRecord.php 4 days ago CharsetConverter.php 4 days ago ColumnConsistency.php 4 days ago EncloseField.php 4 days ago EscapeFormula.php 4 days ago Exception.php 9 months ago HTMLConverter.php 4 days ago Info.php 4 days ago InvalidArgument.php 9 months ago MapIterator.php 4 days ago RFC4180Field.php 4 days ago Reader.php 4 days ago ResultSet.php 4 days ago Statement.php 4 days ago Stream.php 4 days ago SyntaxError.php 4 days ago TabularDataReader.php 4 days ago UnableToProcessCsv.php 9 months ago UnavailableFeature.php 9 months ago UnavailableStream.php 9 months ago Writer.php 4 days ago XMLConverter.php 4 days ago functions.php 9 months ago functions_include.php 9 months ago
Reader.php
301 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 SplFileObject;
18 use function array_combine;
19 use function array_filter;
20 use function array_pad;
21 use function array_slice;
22 use function array_unique;
23 use function count;
24 use function is_array;
25 use function iterator_count;
26 use function iterator_to_array;
27 use function mb_strlen;
28 use function mb_substr;
29 use function strlen;
30 use function substr;
31 use const STREAM_FILTER_READ;
32 /**
33 * A class to parse and read records from a CSV document.
34 * @internal
35 */
36 class Reader extends AbstractCsv implements TabularDataReader, JsonSerializable
37 {
38 protected const STREAM_FILTER_MODE = STREAM_FILTER_READ;
39 protected ?int $header_offset = null;
40 protected int $nb_records = -1;
41 protected bool $is_empty_records_included = \false;
42 /** @var array<string> header record. */
43 protected array $header = [];
44 public static function createFromPath(string $path, string $open_mode = 'r', $context = null)
45 {
46 return parent::createFromPath($path, $open_mode, $context);
47 }
48 protected function resetProperties() : void
49 {
50 $this->nb_records = -1;
51 $this->header = [];
52 }
53 /** Returns the header offset. */
54 public function getHeaderOffset() : ?int
55 {
56 return $this->header_offset;
57 }
58 public function getHeader() : array
59 {
60 if (null === $this->header_offset) {
61 return $this->header;
62 }
63 if ([] !== $this->header) {
64 return $this->header;
65 }
66 $this->header = $this->setHeader($this->header_offset);
67 return $this->header;
68 }
69 /**
70 * Determine the CSV record header.
71 *
72 * @throws Exception If the header offset is set and no record is found or is the empty array
73 *
74 * @return array<string>
75 */
76 protected function setHeader(int $offset) : array
77 {
78 $header = $this->seekRow($offset);
79 if (\in_array($header, [[], [null]], \true)) {
80 throw SyntaxError::dueToHeaderNotFound($offset);
81 }
82 if (0 !== $offset) {
83 return $header;
84 }
85 $header = $this->removeBOM($header, mb_strlen($this->getInputBOM()), $this->enclosure);
86 if ([''] === $header) {
87 throw SyntaxError::dueToHeaderNotFound($offset);
88 }
89 return $header;
90 }
91 /** Returns the row at a given offset. */
92 protected function seekRow(int $offset) : array
93 {
94 foreach ($this->getDocument() as $index => $record) {
95 if ($offset === $index) {
96 return $record;
97 }
98 }
99 return [];
100 }
101 /**
102 * Returns the document as an Iterator.
103 */
104 protected function getDocument() : Iterator
105 {
106 $this->document->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD);
107 $this->document->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
108 $this->document->rewind();
109 return $this->document;
110 }
111 /**
112 * Strip the BOM sequence from a record.
113 *
114 * @param string[] $record
115 *
116 * @return array<string>
117 */
118 protected function removeBOM(array $record, int $bom_length, string $enclosure) : array
119 {
120 if (0 === $bom_length) {
121 return $record;
122 }
123 $record[0] = mb_substr($record[0], $bom_length);
124 if ($enclosure . $enclosure != substr($record[0] . $record[0], strlen($record[0]) - 1, 2)) {
125 return $record;
126 }
127 $record[0] = substr($record[0], 1, -1);
128 return $record;
129 }
130 public function fetchColumnByName(string $name) : Iterator
131 {
132 return ResultSet::createFromTabularDataReader($this)->fetchColumnByName($name);
133 }
134 public function fetchColumnByOffset(int $offset = 0) : Iterator
135 {
136 return ResultSet::createFromTabularDataReader($this)->fetchColumnByOffset($offset);
137 }
138 public function fetchColumn($index = 0) : Iterator
139 {
140 return ResultSet::createFromTabularDataReader($this)->fetchColumn($index);
141 }
142 public function fetchOne(int $nth_record = 0) : array
143 {
144 return ResultSet::createFromTabularDataReader($this)->fetchOne($nth_record);
145 }
146 public function fetchPairs($offset_index = 0, $value_index = 1) : Iterator
147 {
148 return ResultSet::createFromTabularDataReader($this)->fetchPairs($offset_index, $value_index);
149 }
150 public function count() : int
151 {
152 if (-1 === $this->nb_records) {
153 $this->nb_records = iterator_count($this->getRecords());
154 }
155 return $this->nb_records;
156 }
157 public function getIterator() : Iterator
158 {
159 return $this->getRecords();
160 }
161 public function jsonSerialize() : array
162 {
163 return iterator_to_array($this->getRecords(), \false);
164 }
165 public function getRecords(array $header = []) : Iterator
166 {
167 $header = $this->computeHeader($header);
168 $normalized = fn($record): bool => is_array($record) && ($this->is_empty_records_included || $record != [null]);
169 $bom = '';
170 if (!$this->is_input_bom_included) {
171 $bom = $this->getInputBOM();
172 }
173 $document = $this->getDocument();
174 $records = $this->stripBOM(new CallbackFilterIterator($document, $normalized), $bom);
175 if (null !== $this->header_offset) {
176 $records = new CallbackFilterIterator($records, fn(array $record, int $offset): bool => $offset !== $this->header_offset);
177 }
178 if ($this->is_empty_records_included) {
179 return $this->combineHeader(new MapIterator($records, fn(array $record): array => [null] === $record ? [] : $record), $header);
180 }
181 return $this->combineHeader($records, $header);
182 }
183 /**
184 * Returns the header to be used for iteration.
185 *
186 * @param string[] $header
187 *
188 * @throws Exception If the header contains non unique column name
189 *
190 * @return array<string>
191 */
192 protected function computeHeader(array $header)
193 {
194 if ([] === $header) {
195 $header = $this->getHeader();
196 }
197 if ($header !== ($filtered_header = array_filter($header, 'is_string'))) {
198 throw SyntaxError::dueToInvalidHeaderColumnNames();
199 }
200 if ($header !== array_unique($filtered_header)) {
201 throw SyntaxError::dueToDuplicateHeaderColumnNames($header);
202 }
203 return $header;
204 }
205 /**
206 * Combine the CSV header to each record if present.
207 *
208 * @param string[] $header
209 */
210 protected function combineHeader(Iterator $iterator, array $header) : Iterator
211 {
212 if ([] === $header) {
213 return $iterator;
214 }
215 $field_count = count($header);
216 $mapper = static function (array $record) use($header, $field_count) : array {
217 if (count($record) != $field_count) {
218 $record = array_slice(array_pad($record, $field_count, null), 0, $field_count);
219 }
220 /** @var array<string|null> $assocRecord */
221 $assocRecord = array_combine($header, $record);
222 return $assocRecord;
223 };
224 return new MapIterator($iterator, $mapper);
225 }
226 /**
227 * Strip the BOM sequence from the returned records if necessary.
228 */
229 protected function stripBOM(Iterator $iterator, string $bom) : Iterator
230 {
231 if ('' === $bom) {
232 return $iterator;
233 }
234 $bom_length = mb_strlen($bom);
235 $mapper = function (array $record, int $index) use($bom_length) : array {
236 if (0 !== $index) {
237 return $record;
238 }
239 $record = $this->removeBOM($record, $bom_length, $this->enclosure);
240 if ([''] === $record) {
241 return [null];
242 }
243 return $record;
244 };
245 return new CallbackFilterIterator(new MapIterator($iterator, $mapper), fn(array $record): bool => $this->is_empty_records_included || $record != [null]);
246 }
247 /**
248 * Selects the record to be used as the CSV header.
249 *
250 * Because the header is represented as an array, to be valid
251 * a header MUST contain only unique string value.
252 *
253 * @param int|null $offset the header record offset
254 *
255 * @throws Exception if the offset is a negative integer
256 *
257 * @return static
258 */
259 public function setHeaderOffset(?int $offset) : self
260 {
261 if ($offset === $this->header_offset) {
262 return $this;
263 }
264 if (null !== $offset && 0 > $offset) {
265 throw InvalidArgument::dueToInvalidHeaderOffset($offset, __METHOD__);
266 }
267 $this->header_offset = $offset;
268 $this->resetProperties();
269 return $this;
270 }
271 /**
272 * Enable skipping empty records.
273 */
274 public function skipEmptyRecords() : self
275 {
276 if ($this->is_empty_records_included) {
277 $this->is_empty_records_included = \false;
278 $this->nb_records = -1;
279 }
280 return $this;
281 }
282 /**
283 * Disable skipping empty records.
284 */
285 public function includeEmptyRecords() : self
286 {
287 if (!$this->is_empty_records_included) {
288 $this->is_empty_records_included = \true;
289 $this->nb_records = -1;
290 }
291 return $this;
292 }
293 /**
294 * Tells whether empty records are skipped by the instance.
295 */
296 public function isEmptyRecordsIncluded() : bool
297 {
298 return $this->is_empty_records_included;
299 }
300 }
301