Completed
Push — master ( 91a727...b61a92 )
by ignace nyamagana
09:12 queued 04:30
created

StreamIterator::fflush()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
* This file is part of the League.csv library
4
*
5
* @license http://opensource.org/licenses/MIT
6
* @link https://github.com/thephpleague/csv/
7
* @version 9.0.0
8
* @package League.csv
9
*
10
* For the full copyright and license information, please view the LICENSE
11
* file that was distributed with this source code.
12
*/
13
declare(strict_types=1);
14
15
namespace League\Csv;
16
17
use League\Csv\Exception\LogicException;
18
use League\Csv\Exception\RuntimeException;
19
use SeekableIterator;
20
use SplFileObject;
21
use TypeError;
22
23
/**
24
 * An object oriented API for a seekable stream resource.
25
 *
26
 * @package  League.csv
27
 * @since    8.2.0
28
 * @author   Ignace Nyamagana Butera <[email protected]>
29
 * @internal used internally to iterate over a stream resource
30
 *
31
 */
32
class StreamIterator implements SeekableIterator
33
{
34
    use ValidatorTrait;
35
36
    /**
37
     * Attached filters
38
     *
39
     * @var resource[]
40
     */
41
    protected $filters;
42
43
    /**
44
     * Stream pointer
45
     *
46
     * @var resource
47
     */
48
    protected $stream;
49
50
    /**
51
     * Tell whether the stream should be closed on object destruction
52
     *
53
     * @var bool
54
     */
55
    protected $should_close_stream = false;
56
57
    /**
58
     * Current iterator value
59
     *
60
     * @var mixed
61
     */
62
    protected $value;
63
64
    /**
65
     * Current iterator key
66
     *
67
     * @var int
68
     */
69
    protected $offset;
70
71
    /**
72
     * Flags for the StreamIterator
73
     *
74
     * @var int
75
     */
76
    protected $flags = 0;
77
78
    /**
79
     * the field delimiter (one character only)
80
     *
81
     * @var string
82
     */
83
    protected $delimiter = ',';
84
85
    /**
86
     * the field enclosure character (one character only)
87
     *
88
     * @var string
89
     */
90
    protected $enclosure = '"';
91
92
    /**
93
     * the field escape character (one character only)
94
     *
95
     * @var string
96
     */
97
    protected $escape = '\\';
98
99
    /**
100
     * New instance
101
     *
102
     *
103
     * @param resource $stream stream type resource
104
     *
105
     * @throws RuntimeException if the argument passed is not a seeakable stream resource
106
     */
107 30
    public function __construct($stream)
108
    {
109 30
        if (!is_resource($stream)) {
110 2
            throw new TypeError(sprintf('Argument passed must be a seekable stream resource, %s given', gettype($stream)));
111
        }
112
113 28
        if ('stream' !== ($type = get_resource_type($stream))) {
114 2
            throw new TypeError(sprintf('Argument passed must be a seekable stream resource, %s resource given', $type));
115
        }
116
117 26
        if (!stream_get_meta_data($stream)['seekable']) {
118 2
            throw new RuntimeException('Argument passed must be a seekable stream resource');
119
        }
120
121 24
        $this->stream = $stream;
122 24
    }
123
124
    /**
125
     * close the file pointer
126
     */
127 24
    public function __destruct()
128
    {
129 24
        if ($this->should_close_stream) {
130 14
            fclose($this->stream);
131
        }
132
133 24
        $this->stream = null;
134 24
    }
135
136
    /**
137
     * Return a new instance from a file path
138
     *
139
     * @param string        $path      file path
140
     * @param string        $open_mode the file open mode flag
141
     * @param resource|null $context   the resource context
142
     *
143
     * @throws RuntimeException if the stream resource can not be created
144
     *
145
     * @return static
146
     */
147 12
    public static function createFromPath(string $path, string $open_mode = 'r', $context = null): self
148
    {
149 12
        $args = [$path, $open_mode, false];
150 12
        if (null !== $context) {
151 2
            $args[] = $context;
152
        }
153
154 12
        if (!$stream = @fopen(...$args)) {
155 2
            throw new RuntimeException(error_get_last()['message']);
156
        }
157
158 10
        $instance = new static($stream);
159 10
        $instance->should_close_stream = true;
160
161 10
        return $instance;
162
    }
163
164
    /**
165
     * Return a new instance from a string
166
     *
167
     * @param string $content the CSV document as a string
168
     *
169
     * @return static
170
     */
171 6
    public static function createFromString(string $content): self
172
    {
173 6
        $stream = fopen('php://temp', 'r+');
174 6
        fwrite($stream, $content);
175
176 6
        $instance = new static($stream);
177 6
        $instance->should_close_stream = true;
178
179 6
        return $instance;
180
    }
181
182
    /**
183
     * Set CSV control
184
     *
185
     * @see http://php.net/manual/en/splfileobject.setcsvcontrol.php
186
     *
187
     * @param string $delimiter
188
     * @param string $enclosure
189
     * @param string $escape
190
     */
191 12
    public function setCsvControl(string $delimiter = ',', string $enclosure = '"', string $escape = '\\')
192
    {
193 12
        $this->delimiter = $this->filterControl($delimiter, 'delimiter', __METHOD__);
194 12
        $this->enclosure = $this->filterControl($enclosure, 'enclosure', __METHOD__);
195 12
        $this->escape = $this->filterControl($escape, 'escape', __METHOD__);
196 12
    }
197
198
    /**
199
     * Set StreamIterator Flags
200
     *
201
     * @see http://php.net/manual/en/splfileobject.setflags.php
202
     *
203
     * @param int $flags
204
     */
205 22
    public function setFlags(int $flags)
206
    {
207 22
        $this->flags = $flags;
208 22
    }
209
210
    /**
211
     * Write a field array as a CSV line
212
     *
213
     * @see http://php.net/manual/en/splfileobject.fputcsv.php
214
     *
215
     * @param array  $fields
216
     * @param string $delimiter
217
     * @param string $enclosure
218
     * @param string $escape
219
     *
220
     * @return int|bool
221
     */
222 14
    public function fputcsv(array $fields, string $delimiter = ',', string $enclosure = '"', string $escape = '\\')
223
    {
224 14
        return fputcsv(
225 14
            $this->stream,
226
            $fields,
227 14
            $this->filterControl($delimiter, 'delimiter', __METHOD__),
228 12
            $this->filterControl($enclosure, 'enclosure', __METHOD__),
229 10
            $this->filterControl($escape, 'escape', __METHOD__)
230
        );
231
    }
232
233
    /**
234
     * Get line number
235
     *
236
     * @see http://php.net/manual/en/splfileobject.key.php
237
     *
238
     * @return int
239
     */
240 12
    public function key()
241
    {
242 12
        return $this->offset;
243
    }
244
245
    /**
246
     * Read next line
247
     *
248
     * @see http://php.net/manual/en/splfileobject.next.php
249
     *
250
     */
251 12
    public function next()
252
    {
253 12
        $this->value = false;
254 12
        $this->offset++;
255 12
    }
256
257
    /**
258
     * Rewind the file to the first line
259
     *
260
     * @see http://php.net/manual/en/splfileobject.rewind.php
261
     *
262
     */
263 22
    public function rewind()
264
    {
265 22
        rewind($this->stream);
266 22
        $this->offset = 0;
267 22
        $this->value = false;
268 22
        if ($this->flags & SplFileObject::READ_AHEAD) {
269 12
            $this->current();
270
        }
271 22
    }
272
273
    /**
274
     * Not at EOF
275
     *
276
     * @see http://php.net/manual/en/splfileobject.valid.php
277
     *
278
     * @return bool
279
     */
280 20
    public function valid()
281
    {
282 20
        if ($this->flags & SplFileObject::READ_AHEAD) {
283 12
            return $this->current() !== false;
284
        }
285
286 8
        return !feof($this->stream);
287
    }
288
289
    /**
290
     * Retrieves the current line of the file.
291
     *
292
     * @see http://php.net/manual/en/splfileobject.current.php
293
     *
294
     * @return mixed
295
     */
296 16
    public function current()
297
    {
298 16
        if (false !== $this->value) {
299 16
            return $this->value;
300
        }
301
302 16
        if (($this->flags & SplFileObject::READ_CSV) == SplFileObject::READ_CSV) {
303 12
            $this->value = $this->getCurrentRecord();
304
305 12
            return $this->value;
306
        }
307
308 4
        $this->value = $this->getCurrentLine();
309
310 4
        return $this->value;
311
    }
312
313
    /**
314
     * Retrieves the current line as a CSV Record
315
     *
316
     * @return array|bool
317
     */
318 12
    protected function getCurrentRecord()
319
    {
320
        do {
321 12
            $ret = fgetcsv($this->stream, 0, $this->delimiter, $this->enclosure, $this->escape);
322 12
        } while ($this->flags & SplFileObject::SKIP_EMPTY && $ret !== false && $ret[0] === null);
323
324 12
        return $ret;
325
    }
326
327
    /**
328
     * Retrieves the current line as a string
329
     *
330
     * @return string|bool
331
     */
332 22
    protected function getCurrentLine()
333
    {
334
        do {
335 22
            $line = fgets($this->stream);
336 22
        } while ($this->flags & SplFileObject::SKIP_EMPTY && $line !== false && rtrim($line, "\r\n") !== '');
337
338 22
        return $line;
339
    }
340
341
    /**
342
     * Seek to specified line
343
     *
344
     * @see http://php.net/manual/en/splfileobject.seek.php
345
     *
346
     * @param int $position
347
     */
348 4
    public function seek($position)
349
    {
350 4
        $pos = $this->filterMinRange((int) $position, 0, __METHOD__.'() Can\'t seek stream to negative line %d');
351 4
        foreach ($this as $key => $value) {
352 4
            if ($key === $pos || feof($this->stream)) {
353 4
                $this->offset--;
354 4
                break;
355
            }
356
        }
357
358 4
        $this->current();
359 4
    }
360
361
    /**
362
     * Gets line from file
363
     *
364
     * @see http://php.net/manual/en/splfileobject.fgets.php
365
     *
366
     * @return string|bool
367
     */
368 26
    public function fgets()
369
    {
370 26
        if (false !== $this->value) {
371 4
            $this->next();
372
        }
373
374 26
        return $this->value = $this->getCurrentLine();
375
    }
376
377
    /**
378
     * Output all remaining data on a file pointer
379
     *
380
     * @see http://php.net/manual/en/splfileobject.fpatssthru.php
381
     *
382
     * @return int
383
     */
384 2
    public function fpassthru()
385
    {
386 2
        return fpassthru($this->stream);
387
    }
388
389
    /**
390
     * Read from file
391
     *
392
     * @see http://php.net/manual/en/splfileobject.fread.php
393
     *
394
     * @param int $length The number of bytes to read
395
     *
396
     * @return string|false
397
     */
398 8
    public function fread($length)
399
    {
400 8
        return fread($this->stream, $length);
401
    }
402
403
    /**
404
     * Seek to a position
405
     *
406
     * @see http://php.net/manual/en/splfileobject.fseek.php
407
     *
408
     * @param int $offset
409
     * @param int $whence
410
     *
411
     * @return int
412
     */
413 4
    public function fseek(int $offset, int $whence = SEEK_SET)
414
    {
415 4
        return fseek($this->stream, $offset, $whence);
416
    }
417
418
    /**
419
     * Write to stream
420
     *
421
     * @see http://php.net/manual/en/splfileobject.fwrite.php
422
     *
423
     * @param string $str
424
     * @param int    $length
425
     *
426
     * @return int|bool
427
     */
428 2
    public function fwrite(string $str, int $length = 0)
429
    {
430 2
        return fwrite($this->stream, $str, $length);
431
    }
432
433
    /**
434
     * append a filter
435
     *
436
     * @see http://php.net/manual/en/function.stream-filter-append.php
437
     *
438
     * @param string $filter_name
439
     * @param int    $read_write
440
     * @param mixed  $params
441
     *
442
     * @throws RuntimeException if the filter can not be appended
443
     *
444
     * @return resource
445
     */
446 10
    public function appendFilter(string $filter_name, int $read_write, $params = null)
447
    {
448 10
        $res = @stream_filter_append($this->stream, $filter_name, $read_write, $params);
449 10
        if (is_resource($res)) {
450 8
            return $res;
451
        }
452
453 2
        throw new RuntimeException(error_get_last()['message']);
454
    }
455
456
    /**
457
     * Removes a registered filter
458
     *
459
     * @see http://php.net/manual/en/function.stream-filter-remove.php
460
     *
461
     * @param resource $resource
462
     */
463 8
    public function removeFilter($resource)
464
    {
465 8
        return stream_filter_remove($resource);
466
    }
467
468
    /**
469
     * Flushes the output to a file
470
     *
471
     * @see http://php.net/manual/en/splfileobject.fwrite.php
472
     *
473
     * @return bool
474
     */
475 8
    public function fflush()
476
    {
477 8
        return fflush($this->stream);
478
    }
479
480
    /**
481
     * @inheritdoc
482
     */
483 2
    public function __clone()
484
    {
485 2
        throw new LogicException('An object of class '.StreamIterator::class.' cannot be cloned');
486
    }
487
}
488