Completed
Push — master ( 3124fc...48a6ec )
by ignace nyamagana
06:33 queued 04:24
created

StreamIterator::fgets()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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