Completed
Push — master ( 8ca68d...fb07a4 )
by ignace nyamagana
05:14
created

StreamIterator::__construct()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

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