Completed
Push — master ( a41208...59b426 )
by ignace nyamagana
04:38
created

StreamIterator::fwrite()   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 2
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 78
    public function __construct($stream)
108
    {
109 78
        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 74
        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 72
        if (!stream_get_meta_data($stream)['seekable']) {
118 2
            throw new InvalidArgumentException(sprintf('Argument passed must be a seekable stream resource'));
119
        }
120
121 70
        $this->stream = $stream;
122 70
    }
123
124
    /**
125
     * close the file pointer
126
     */
127 70
    public function __destruct()
128
    {
129 70
        if ($this->is_inner_stream) {
130 48
            fclose($this->stream);
131
        }
132
133 70
        $this->stream = null;
134 70
    }
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 56
    public function setFlags(int $flags)
200
    {
201 56
        $this->flags = $flags;
202 56
    }
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 12
    public function fputcsv(array $fields, string $delimiter = ',', string $enclosure = '"', string $escape = '\\')
217
    {
218 12
        return fputcsv(
219 12
            $this->stream,
220
            $fields,
221 12
            $this->filterControl($delimiter, 'delimiter'),
222 12
            $this->filterControl($enclosure, 'enclosure'),
223 12
            $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 56
    public function rewind()
258
    {
259 56
        rewind($this->stream);
260 56
        $this->offset = 0;
261 56
        $this->value = false;
262 56
        if ($this->flags & SplFileObject::READ_AHEAD) {
263 32
            $this->current();
264
        }
265 56
    }
266
267
    /**
268
     * Not at EOF
269
     *
270
     * @see http://php.net/manual/en/splfileobject.valid.php
271
     *
272
     * @return bool
273
     */
274 52
    public function valid()
275
    {
276 52
        if ($this->flags & SplFileObject::READ_AHEAD) {
277 30
            return $this->current() !== false;
278
        }
279
280 22
        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 52
    protected function getCurrentLine()
327
    {
328
        do {
329 52
            $line = fgets($this->stream);
330 52
        } while ($this->flags & SplFileObject::SKIP_EMPTY && $line !== false && rtrim($line, "\r\n") !== '');
331
332 52
        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 52
    public function fgets()
363
    {
364 52
        if (false !== $this->value) {
365 2
            $this->next();
366
        }
367
368 52
        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 20
    public function fread($length)
393
    {
394 20
        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 8
    public function fseek(int $offset, int $whence = SEEK_SET)
408
    {
409 8
        return fseek($this->stream, $offset, $whence);
410
    }
411
412
    /**
413
     * Write to stream
414
     *
415
     * @see http://php.net/manual/en/splfileobject.fwrite.php
416
     *
417
     * @param string $str
418
     * @param int    $length
419
     *
420
     * @return int|bool
421
     */
422 2
    public function fwrite(string $str, int $length = 0)
423
    {
424 2
        return fwrite($this->stream, $str, $length);
425
    }
426
427
    /**
428
     * append a filter
429
     *
430
     * @see http://php.net/manual/en/function.stream-filter-append.php
431
     *
432
     * @param string $filter_name
433
     * @param int    $read_write
434
     * @param mixed  $params
435
     *
436
     * @throws InvalidArgumentException if the filter can not be appended
437
     *
438
     * @return resource
439
     */
440 14
    public function appendFilter(string $filter_name, int $read_write, $params = null)
441
    {
442 14
        $res = @stream_filter_append($this->stream, $filter_name, $read_write, $params);
443 14
        if (is_resource($res)) {
444 14
            return $res;
445
        }
446
447 2
        throw new InvalidArgumentException(error_get_last()['message']);
448
    }
449
450
    /**
451
     * Removes a registered filter
452
     *
453
     * @see http://php.net/manual/en/function.stream-filter-remove.php
454
     *
455
     * @param resource $resource
456
     */
457 14
    public function removeFilter($resource)
458
    {
459 14
        return stream_filter_remove($resource);
460
    }
461
462
    /**
463
     * Flushes the output to a file
464
     *
465
     * @see http://php.net/manual/en/splfileobject.fwrite.php
466
     *
467
     * @return bool
468
     */
469 20
    public function fflush()
470
    {
471 20
        return fflush($this->stream);
472
    }
473
474
    /**
475
     * @inheritdoc
476
     */
477 2
    public function __clone()
478
    {
479 2
        throw new LogicException('An object of class '.StreamIterator::class.' cannot be cloned');
480
    }
481
}
482