Completed
Push — master ( 493789...a41208 )
by ignace nyamagana
06:59 queued 04:55
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\InvalidArgumentException;
18
use League\Csv\Exception\LogicException;
19
use League\Csv\Exception\RuntimeException;
20
use SeekableIterator;
21
use SplFileObject;
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 is only accessible
52
     * by the current object
53
     *
54
     * @var bool
55
     */
56
    protected $is_inner_stream = false;
57
58
    /**
59
     * Current iterator value
60
     *
61
     * @var mixed
62
     */
63
    protected $value;
64
65
    /**
66
     * Current iterator key
67
     *
68
     * @var int
69
     */
70
    protected $offset;
71
72
    /**
73
     * Flags for the StreamIterator
74
     *
75
     * @var int
76
     */
77
    protected $flags = 0;
78
79
    /**
80
     * the field delimiter (one character only)
81
     *
82
     * @var string
83
     */
84
    protected $delimiter = ',';
85
86
    /**
87
     * the field enclosure character (one character only)
88
     *
89
     * @var string
90
     */
91
    protected $enclosure = '"';
92
93
    /**
94
     * the field escape character (one character only)
95
     *
96
     * @var string
97
     */
98
    protected $escape = '\\';
99
100
    /**
101
     * New instance
102
     *
103
     *
104
     * @param  resource                 $stream stream type resource
105
     * @throws InvalidArgumentException if the argument passed is not a seeakable stream resource
106
     */
107 76
    public function __construct($stream)
108
    {
109 76
        if (!is_resource($stream)) {
110 4
            throw new InvalidArgumentException(sprintf('Argument passed must be a resource, %s given', is_object($stream) ? get_class($stream) : gettype($stream)));
111
        }
112
113 72
        if ('stream' !== ($type = get_resource_type($stream))) {
114 2
            throw new InvalidArgumentException(sprintf('Argument passed must be a stream resource, %s resource given', $type));
115
        }
116
117 70
        if (!stream_get_meta_data($stream)['seekable']) {
118 2
            throw new InvalidArgumentException(sprintf('Argument passed must be a seekable stream resource'));
119
        }
120
121 68
        $this->stream = $stream;
122 68
    }
123
124
    /**
125
     * close the file pointer
126
     */
127 68
    public function __destruct()
128
    {
129 68
        if ($this->is_inner_stream) {
130 48
            fclose($this->stream);
131
        }
132
133 68
        $this->stream = null;
134 68
    }
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
     *
142
     * @throws RuntimeException if the stream resource can not be created
143
     *
144
     * @return static
145
     */
146 26
    public static function createFromPath(string $path, string $open_mode = 'r'): self
147
    {
148 26
        if (!$stream = @fopen($path, $open_mode)) {
149 2
            throw new RuntimeException(error_get_last()['message']);
150
        }
151
152 24
        $instance = new static($stream);
153 24
        $instance->is_inner_stream = true;
154
155 24
        return $instance;
156
    }
157
158
    /**
159
     * Return a new instance from a string
160
     *
161
     * @param string $content the CSV document as a string
162
     *
163
     * @return static
164
     */
165 24
    public static function createFromString(string $content): self
166
    {
167 24
        $stream = fopen('php://temp', 'r+');
168 24
        fwrite($stream, $content);
169
170 24
        $instance = new static($stream);
171 24
        $instance->is_inner_stream = true;
172
173 24
        return $instance;
174
    }
175
176
    /**
177
     * Set CSV control
178
     *
179
     * @see http://php.net/manual/en/splfileobject.setcsvcontrol.php
180
     *
181
     * @param string $delimiter
182
     * @param string $enclosure
183
     * @param string $escape
184
     */
185 30
    public function setCsvControl(string $delimiter = ',', string $enclosure = '"', string $escape = '\\')
186
    {
187 30
        $this->delimiter = $this->filterControl($delimiter, 'delimiter');
188 28
        $this->enclosure = $this->filterControl($enclosure, 'enclosure');
189 28
        $this->escape = $this->filterControl($escape, 'escape');
190 28
    }
191
192
    /**
193
     * Set StreamIterator Flags
194
     *
195
     * @see http://php.net/manual/en/splfileobject.setflags.php
196
     *
197
     * @param int $flags
198
     */
199 54
    public function setFlags(int $flags)
200
    {
201 54
        $this->flags = $flags;
202 54
    }
203
204
    /**
205
     * Write a field array as a CSV line
206
     *
207
     * @see http://php.net/manual/en/splfileobject.fputcsv.php
208
     *
209
     * @param array  $fields
210
     * @param string $delimiter
211
     * @param string $enclosure
212
     * @param string $escape
213
     *
214
     * @return int|bool
215
     */
216 10
    public function fputcsv(array $fields, string $delimiter = ',', string $enclosure = '"', string $escape = '\\')
217
    {
218 10
        return fputcsv(
219 10
            $this->stream,
220
            $fields,
221 10
            $this->filterControl($delimiter, 'delimiter'),
222 10
            $this->filterControl($enclosure, 'enclosure'),
223 10
            $this->filterControl($escape, 'escape')
224
        );
225
    }
226
227
    /**
228
     * Get line number
229
     *
230
     * @see http://php.net/manual/en/splfileobject.key.php
231
     *
232
     * @return int
233
     */
234 32
    public function key()
235
    {
236 32
        return $this->offset;
237
    }
238
239
    /**
240
     * Read next line
241
     *
242
     * @see http://php.net/manual/en/splfileobject.next.php
243
     *
244
     */
245 30
    public function next()
246
    {
247 30
        $this->value = false;
248 30
        $this->offset++;
249 30
    }
250
251
    /**
252
     * Rewind the file to the first line
253
     *
254
     * @see http://php.net/manual/en/splfileobject.rewind.php
255
     *
256
     */
257 54
    public function rewind()
258
    {
259 54
        rewind($this->stream);
260 54
        $this->offset = 0;
261 54
        $this->value = false;
262 54
        if ($this->flags & SplFileObject::READ_AHEAD) {
263 32
            $this->current();
264
        }
265 54
    }
266
267
    /**
268
     * Not at EOF
269
     *
270
     * @see http://php.net/manual/en/splfileobject.valid.php
271
     *
272
     * @return bool
273
     */
274 50
    public function valid()
275
    {
276 50
        if ($this->flags & SplFileObject::READ_AHEAD) {
277 30
            return $this->current() !== false;
278
        }
279
280 20
        return !feof($this->stream);
281
    }
282
283
    /**
284
     * Retrieves the current line of the file.
285
     *
286
     * @see http://php.net/manual/en/splfileobject.current.php
287
     *
288
     * @return mixed
289
     */
290 34
    public function current()
291
    {
292 34
        if (false !== $this->value) {
293 32
            return $this->value;
294
        }
295
296 34
        if (($this->flags & SplFileObject::READ_CSV) == SplFileObject::READ_CSV) {
297 32
            $this->value = $this->getCurrentRecord();
298
299 32
            return $this->value;
300
        }
301
302 2
        $this->value = $this->getCurrentLine();
303
304 2
        return $this->value;
305
    }
306
307
    /**
308
     * Retrieves the current line as a CSV Record
309
     *
310
     * @return array|bool
311
     */
312 32
    protected function getCurrentRecord()
313
    {
314
        do {
315 32
            $ret = fgetcsv($this->stream, 0, $this->delimiter, $this->enclosure, $this->escape);
316 32
        } while ($this->flags & SplFileObject::SKIP_EMPTY && $ret !== false && $ret[0] === null);
317
318 32
        return $ret;
319
    }
320
321
    /**
322
     * Retrieves the current line as a string
323
     *
324
     * @return string|bool
325
     */
326 50
    protected function getCurrentLine()
327
    {
328
        do {
329 50
            $line = fgets($this->stream);
330 50
        } while ($this->flags & SplFileObject::SKIP_EMPTY && $line !== false && rtrim($line, "\r\n") !== '');
331
332 50
        return $line;
333
    }
334
335
    /**
336
     * Seek to specified line
337
     *
338
     * @see http://php.net/manual/en/splfileobject.seek.php
339
     *
340
     * @param int $position
341
     */
342 20
    public function seek($position)
343
    {
344 20
        $position = $this->filterMinRange((int) $position, 0, 'Can\'t seek stream to negative line %d');
345 18
        foreach ($this as $key => $value) {
346 18
            if ($key === $position || feof($this->stream)) {
347 18
                $this->offset--;
348 18
                break;
349
            }
350
        }
351
352 18
        $this->current();
353 18
    }
354
355
    /**
356
     * Gets line from file
357
     *
358
     * @see http://php.net/manual/en/splfileobject.fgets.php
359
     *
360
     * @return string|bool
361
     */
362 50
    public function fgets()
363
    {
364 50
        if (false !== $this->value) {
365 2
            $this->next();
366
        }
367
368 50
        return $this->value = $this->getCurrentLine();
369
    }
370
371
    /**
372
     * Output all remaining data on a file pointer
373
     *
374
     * @see http://php.net/manual/en/splfileobject.fpatssthru.php
375
     *
376
     * @return int
377
     */
378 2
    public function fpassthru()
379
    {
380 2
        return fpassthru($this->stream);
381
    }
382
383
    /**
384
     * Read from file
385
     *
386
     * @see http://php.net/manual/en/splfileobject.fread.php
387
     *
388
     * @param int $length The number of bytes to read
389
     *
390
     * @return string|false
391
     */
392 18
    public function fread($length)
393
    {
394 18
        return fread($this->stream, $length);
395
    }
396
397
    /**
398
     * Seek to a position
399
     *
400
     * @see http://php.net/manual/en/splfileobject.fseek.php
401
     *
402
     * @param int $offset
403
     * @param int $whence
404
     *
405
     * @return int
406
     */
407 6
    public function fseek(int $offset, int $whence = SEEK_SET)
408
    {
409 6
        return fseek($this->stream, $offset, $whence);
410
    }
411
412
    /**
413
     * append a filter
414
     *
415
     * @see http://php.net/manual/en/function.stream-filter-append.php
416
     *
417
     * @param string $filter_name
418
     * @param int    $read_write
419
     * @param mixed  $params
420
     *
421
     * @throws InvalidArgumentException if the filter can not be appended
422
     *
423
     * @return resource
424
     */
425 14
    public function appendFilter(string $filter_name, int $read_write, $params = null)
426
    {
427 14
        $res = @stream_filter_append($this->stream, $filter_name, $read_write, $params);
428 14
        if (is_resource($res)) {
429 14
            return $res;
430
        }
431
432 2
        throw new InvalidArgumentException(error_get_last()['message']);
433
    }
434
435
    /**
436
     * Removes a registered filter
437
     *
438
     * @see http://php.net/manual/en/function.stream-filter-remove.php
439
     *
440
     * @param resource $resource
441
     */
442 14
    public function removeFilter($resource)
443
    {
444 14
        return stream_filter_remove($resource);
445
    }
446
447
    /**
448
     * Flushes the output to a file
449
     *
450
     * @see http://php.net/manual/en/splfileobject.fwrite.php
451
     *
452
     * @return bool
453
     */
454 18
    public function fflush()
455
    {
456 18
        return fflush($this->stream);
457
    }
458
459
    /**
460
     * @inheritdoc
461
     */
462 2
    public function __clone()
463
    {
464 2
        throw new LogicException('An object of class '.StreamIterator::class.' cannot be cloned');
465
    }
466
}
467