PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.2.2
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.2.2
2.4.0 2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.2 2.2.1 2.2.0 2.1.2 2.1.1 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 All 67 releases
better-payment / vendor / league / csv / src / Stream.php

Stream.php in Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More 2.2.2, at vendor/league/csv/src/Stream.php

448 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * League.Csv (https://csv.thephpleague.com)
5 *
6 * (c) Ignace Nyamagana Butera <[email protected]>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 declare(strict_types=1);
13
14 namespace League\Csv;
15
16 use ReturnTypeWillChange;
17 use SeekableIterator;
18 use SplFileObject;
19 use TypeError;
20 use function array_keys;
21 use function array_walk_recursive;
22 use function fclose;
23 use function feof;
24 use function fflush;
25 use function fgetcsv;
26 use function fgets;
27 use function fopen;
28 use function fpassthru;
29 use function fputcsv;
30 use function fread;
31 use function fseek;
32 use function fwrite;
33 use function get_resource_type;
34 use function gettype;
35 use function is_array;
36 use function is_resource;
37 use function rewind;
38 use function stream_filter_append;
39 use function stream_filter_remove;
40 use function stream_get_meta_data;
41 use function strlen;
42 use const PHP_VERSION_ID;
43 use const SEEK_SET;
44
45 /**
46 * An object oriented API to handle a PHP stream resource.
47 *
48 * @internal used internally to iterate over a stream resource
49 */
50 final class Stream implements SeekableIterator
51 {
52 /** @var array<string, array<resource>> Attached filters. */
53 private array $filters = [];
54 /** @var resource */
55 private $stream;
56 private bool $should_close_stream = false;
57 /** @var mixed can be a null false or a scalar type value. Current iterator value. */
58 private $value;
59 /** Current iterator key. */
60 private int $offset;
61 /** Flags for the Document.*/
62 private int $flags = 0;
63 private string $delimiter = ',';
64 private string $enclosure = '"';
65 private string $escape = '\\';
66 private bool $is_seekable = false;
67
68 /**
69 * @param mixed $stream stream type resource
70 */
71 public function __construct($stream)
72 {
73 if (!is_resource($stream)) {
74 throw new TypeError('Argument passed must be a stream resource, '.gettype($stream).' given.');
75 }
76
77 if ('stream' !== ($type = get_resource_type($stream))) {
78 throw new TypeError('Argument passed must be a stream resource, '.$type.' resource given');
79 }
80
81 $this->is_seekable = stream_get_meta_data($stream)['seekable'];
82 $this->stream = $stream;
83 }
84
85 public function __destruct()
86 {
87 array_walk_recursive($this->filters, fn ($filter): bool => @stream_filter_remove($filter));
88
89 if ($this->should_close_stream && is_resource($this->stream)) {
90 fclose($this->stream);
91 }
92
93 unset($this->stream);
94 }
95
96 public function __clone()
97 {
98 throw UnavailableStream::dueToForbiddenCloning(self::class);
99 }
100
101 public function __debugInfo(): array
102 {
103 return stream_get_meta_data($this->stream) + [
104 'delimiter' => $this->delimiter,
105 'enclosure' => $this->enclosure,
106 'escape' => $this->escape,
107 'stream_filters' => array_keys($this->filters),
108 ];
109 }
110
111 /**
112 * Return a new instance from a file path.
113 *
114 * @param resource|null $context
115 *
116 * @throws Exception if the stream resource can not be created
117 */
118 public static function createFromPath(string $path, string $open_mode = 'r', $context = null): self
119 {
120 $args = [$path, $open_mode];
121 if (null !== $context) {
122 $args[] = false;
123 $args[] = $context;
124 }
125
126 $resource = @fopen(...$args);
127 if (!is_resource($resource)) {
128 throw UnavailableStream::dueToPathNotFound($path);
129 }
130
131 $instance = new self($resource);
132 $instance->should_close_stream = true;
133
134 return $instance;
135 }
136
137 /**
138 * Return a new instance from a string.
139 */
140 public static function createFromString(string $content = ''): self
141 {
142 /** @var resource $resource */
143 $resource = fopen('php://temp', 'r+');
144 fwrite($resource, $content);
145
146 $instance = new self($resource);
147 $instance->should_close_stream = true;
148
149 return $instance;
150 }
151
152 /**
153 * returns the URI of the underlying stream.
154 *
155 * @see https://www.php.net/manual/en/splfileinfo.getpathname.php
156 */
157 public function getPathname(): string
158 {
159 return stream_get_meta_data($this->stream)['uri'];
160 }
161
162 /**
163 * append a filter.
164 *
165 * @see http://php.net/manual/en/function.stream-filter-append.php
166 *
167 * @throws InvalidArgument if the filter can not be appended
168 */
169 public function appendFilter(string $filtername, int $read_write, array $params = null): void
170 {
171 $res = @stream_filter_append($this->stream, $filtername, $read_write, $params ?? []);
172 if (!is_resource($res)) {
173 throw InvalidArgument::dueToStreamFilterNotFound($filtername);
174 }
175
176 $this->filters[$filtername][] = $res;
177 }
178
179 /**
180 * Set CSV control.
181 *
182 * @see http://php.net/manual/en/SplFileObject.setcsvcontrol.php
183 */
184 public function setCsvControl(string $delimiter = ',', string $enclosure = '"', string $escape = '\\'): void
185 {
186 [$this->delimiter, $this->enclosure, $this->escape] = $this->filterControl($delimiter, $enclosure, $escape, __METHOD__);
187 }
188
189 /**
190 * Filter Csv control characters.
191 *
192 * @throws InvalidArgument If the Csv control character is not one character only.
193 */
194 private function filterControl(string $delimiter, string $enclosure, string $escape, string $caller): array
195 {
196 if (1 !== strlen($delimiter)) {
197 throw InvalidArgument::dueToInvalidDelimiterCharacter($delimiter, $caller);
198 }
199
200 if (1 !== strlen($enclosure)) {
201 throw InvalidArgument::dueToInvalidEnclosureCharacter($enclosure, $caller);
202 }
203
204 if (1 === strlen($escape) || ('' === $escape && 70400 <= PHP_VERSION_ID)) {
205 return [$delimiter, $enclosure, $escape];
206 }
207
208 throw InvalidArgument::dueToInvalidEscapeCharacter($escape, $caller);
209 }
210
211 /**
212 * Set CSV control.
213 *
214 * @see http://php.net/manual/en/SplFileObject.getcsvcontrol.php
215 *
216 * @return array<string>
217 */
218 public function getCsvControl(): array
219 {
220 return [$this->delimiter, $this->enclosure, $this->escape];
221 }
222
223 /**
224 * Set CSV stream flags.
225 *
226 * @see http://php.net/manual/en/SplFileObject.setflags.php
227 */
228 public function setFlags(int $flags): void
229 {
230 $this->flags = $flags;
231 }
232
233 /**
234 * Write a field array as a CSV line.
235 *
236 * @see http://php.net/manual/en/SplFileObject.fputcsv.php
237 *
238 * @return int|false
239 */
240 public function fputcsv(array $fields, string $delimiter = ',', string $enclosure = '"', string $escape = '\\', string $eol = "\n")
241 {
242 $controls = $this->filterControl($delimiter, $enclosure, $escape, __METHOD__);
243 if (80100 <= PHP_VERSION_ID) {
244 $controls[] = $eol;
245 }
246
247 return fputcsv($this->stream, $fields, ...$controls);
248 }
249
250 /**
251 * Get line number.
252 *
253 * @see http://php.net/manual/en/SplFileObject.key.php
254 */
255 public function key(): int
256 {
257 return $this->offset;
258 }
259
260 /**
261 * Read next line.
262 *
263 * @see http://php.net/manual/en/SplFileObject.next.php
264 */
265 public function next(): void
266 {
267 $this->value = false;
268 $this->offset++;
269 }
270
271 /**
272 * Rewind the file to the first line.
273 *
274 * @see http://php.net/manual/en/SplFileObject.rewind.php
275 *
276 * @throws Exception if the stream resource is not seekable
277 */
278 public function rewind(): void
279 {
280 if (!$this->is_seekable) {
281 throw UnavailableFeature::dueToMissingStreamSeekability();
282 }
283
284 rewind($this->stream);
285 $this->offset = 0;
286 $this->value = false;
287 if (0 !== ($this->flags & SplFileObject::READ_AHEAD)) {
288 $this->current();
289 }
290 }
291
292 /**
293 * Not at EOF.
294 *
295 * @see http://php.net/manual/en/SplFileObject.valid.php
296 */
297 public function valid(): bool
298 {
299 if (0 !== ($this->flags & SplFileObject::READ_AHEAD)) {
300 return $this->current() !== false;
301 }
302
303 return !feof($this->stream);
304 }
305
306 /**
307 * Retrieves the current line of the file.
308 *
309 * @see http://php.net/manual/en/SplFileObject.current.php
310 *
311 * @return mixed The value of the current element.
312 */
313 #[ReturnTypeWillChange]
314 public function current()
315 {
316 if (false !== $this->value) {
317 return $this->value;
318 }
319
320 $this->value = $this->getCurrentRecord();
321
322 return $this->value;
323 }
324
325 /**
326 * Retrieves the current line as a CSV Record.
327 *
328 * @return array|false
329 */
330 private function getCurrentRecord()
331 {
332 $flag = 0 !== ($this->flags & SplFileObject::SKIP_EMPTY);
333 do {
334 $ret = fgetcsv($this->stream, 0, $this->delimiter, $this->enclosure, $this->escape);
335 } while ($flag && is_array($ret) && null === $ret[0]);
336
337 return $ret;
338 }
339
340 /**
341 * Seek to specified line.
342 *
343 * @see http://php.net/manual/en/SplFileObject.seek.php
344 *
345 * @param int $position
346 * @throws Exception if the position is negative
347 */
348 public function seek($position): void
349 {
350 if ($position < 0) {
351 throw InvalidArgument::dueToInvalidSeekingPosition($position, __METHOD__);
352 }
353
354 $this->rewind();
355 while ($this->key() !== $position && $this->valid()) {
356 $this->current();
357 $this->next();
358 }
359
360 if (0 !== $position) {
361 $this->offset--;
362 }
363
364 $this->current();
365 }
366
367 /**
368 * Output all remaining data on a file pointer.
369 *
370 * @see http://php.net/manual/en/SplFileObject.fpatssthru.php
371 *
372 * @return int|false
373 */
374 public function fpassthru()
375 {
376 return fpassthru($this->stream);
377 }
378
379 /**
380 * Read from file.
381 *
382 * @see http://php.net/manual/en/SplFileObject.fread.php
383 *
384 * @param int<0, max> $length The number of bytes to read
385 *
386 * @return string|false
387 */
388 public function fread(int $length)
389 {
390 return fread($this->stream, $length);
391 }
392
393 /**
394 * Gets a line from file.
395 *
396 * @see http://php.net/manual/en/SplFileObject.fgets.php
397 *
398 * @return string|false
399 */
400 public function fgets()
401 {
402 return fgets($this->stream);
403 }
404
405 /**
406 * Seek to a position.
407 *
408 * @see http://php.net/manual/en/SplFileObject.fseek.php
409 *
410 * @throws Exception if the stream resource is not seekable
411 */
412 public function fseek(int $offset, int $whence = SEEK_SET): int
413 {
414 if (!$this->is_seekable) {
415 throw UnavailableFeature::dueToMissingStreamSeekability();
416 }
417
418 return fseek($this->stream, $offset, $whence);
419 }
420
421 /**
422 * Write to stream.
423 *
424 * @see http://php.net/manual/en/SplFileObject.fwrite.php
425 *
426 * @return int|false
427 */
428 public function fwrite(string $str, int $length = null)
429 {
430 $args = [$this->stream, $str];
431 if (null !== $length) {
432 $args[] = $length;
433 }
434
435 return fwrite(...$args);
436 }
437
438 /**
439 * Flushes the output to a file.
440 *
441 * @see http://php.net/manual/en/SplFileObject.fwrite.php
442 */
443 public function fflush(): bool
444 {
445 return fflush($this->stream);
446 }
447 }
448