Completed
Pull Request — master (#210)
by ignace nyamagana
06:59
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 Iterator;
18
use LogicException;
19
use SplFileObject;
20
21
/**
22
 *  an object oriented interface for a stream resource.
23
 *
24
 * @package  League.csv
25
 * @since    8.2.0
26
 * @internal used internally to iterate over a stream resource
27
 *
28
 */
29
class StreamIterator implements Iterator
30
{
31
    /**
32
     * Attached filters
33
     *
34
     * @var resource[]
35
     */
36
    protected $filters;
37
38
    /**
39
     * Stream pointer
40
     *
41
     * @var resource
42
     */
43
    protected $stream;
44
45
    /**
46
     * Current iterator value
47
     *
48
     * @var mixed
49
     */
50
    protected $current_line;
51
52
    /**
53
     * Current iterator key
54
     *
55
     * @var int
56
     */
57
    protected $current_line_number;
58
59
    /**
60
     * Flags for the StreamIterator
61
     *
62
     * @var int
63
     */
64
    protected $flags = 0;
65
66
    /**
67
     * the field delimiter (one character only)
68
     *
69
     * @var string
70
     */
71
    protected $delimiter = ',';
72
73
    /**
74
     * the field enclosure character (one character only)
75
     *
76
     * @var string
77
     */
78
    protected $enclosure = '"';
79
80
    /**
81
     * the field escape character (one character only)
82
     *
83
     * @var string
84
     */
85
    protected $escape = '\\';
86
87
    /**
88
     * New instance
89
     *
90
     * @param resource $stream stream type resource
91
     */
92 58
    public function __construct($stream)
93
    {
94 58
        if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) {
95 4
            throw new Exception(sprintf(
96 4
                'Expected resource to be a stream, received %s instead',
97 4
                is_object($stream) ? get_class($stream) : gettype($stream)
98
            ));
99
        }
100
101 58
        $data = stream_get_meta_data($stream);
102 58
        if (!$data['seekable']) {
103 2
            throw new Exception('The stream must be seekable');
104
        }
105
106 58
        $this->stream = $stream;
107 58
    }
108
109
    /**
110
     * close the file pointer
111
     */
112 58
    public function __destruct()
113
    {
114 58
        $this->stream = null;
115 58
    }
116
117
    /**
118
     * Set CSV control
119
     *
120
     * @see http://php.net/manual/en/splfileobject.setcsvcontrol.php
121
     *
122
     * @param string $delimiter
123
     * @param string $enclosure
124
     * @param string $escape
125
     */
126 20
    public function setCsvControl(string $delimiter = ',', string $enclosure = '"', string $escape = '\\')
127
    {
128 20
        $this->delimiter = $this->filterControl($delimiter, 'delimiter');
129 18
        $this->enclosure = $this->filterControl($enclosure, 'enclosure');
130 18
        $this->escape = $this->filterControl($escape, 'escape');
131 18
    }
132
133
    /**
134
     * Filter Csv control character
135
     *
136
     * @param string $char Csv control character
137
     * @param string $type Csv control character type
138
     *
139
     * @throws InvalidArgumentException If the Csv control character is not one character only.
140
     *
141
     * @return string
142
     */
143 34
    protected function filterControl(string $char, string $type)
144
    {
145 34
        if (1 == strlen($char)) {
146 32
            return $char;
147
        }
148
149 2
        throw new Exception(sprintf('The %s character must be a single character', $type));
150
    }
151
152
    /**
153
     * Set Flags
154
     *
155
     * @see http://php.net/manual/en/splfileobject.setflags.php
156
     *
157
     * @param int $flags
158
     */
159 40
    public function setFlags(int $flags)
160
    {
161 40
        $this->flags = $flags;
162 40
    }
163
164
    /**
165
     * Write a field array as a CSV line
166
     *
167
     * @see http://php.net/manual/en/splfileobject.fputcsv.php
168
     *
169
     * @param array  $fields
170
     * @param string $delimiter
171
     * @param string $enclosure
172
     * @param string $escape
173
     *
174
     * @return int|false
175
     */
176 14
    public function fputcsv(array $fields, string $delimiter = ',', string $enclosure = '"', string $escape = '\\')
177
    {
178 14
        return fputcsv(
179 14
            $this->stream,
180
            $fields,
181 14
            $this->filterControl($delimiter, 'delimiter'),
182 14
            $this->filterControl($enclosure, 'enclosure'),
183 14
            $this->filterControl($escape, 'escape')
184
        );
185
    }
186
187
    /**
188
     * Retrieves the current line of the file.
189
     *
190
     * @return mixed
191
     */
192 24
    public function current()
193
    {
194 24
        if (false !== $this->current_line) {
195 10
            return $this->current_line;
196
        }
197
198 24
        if (($this->flags & SplFileObject::READ_CSV) == SplFileObject::READ_CSV) {
199 22
            $this->current_line = $this->getCurrentRecord();
200
201 22
            return $this->current_line;
202
        }
203
204 2
        $this->current_line = $this->getCurrentLine();
205
206 2
        return $this->current_line;
207
    }
208
209
    /**
210
     * Retrieves the current line as a CSV Record
211
     *
212
     * @return array
213
     */
214 22
    protected function getCurrentRecord()
215
    {
216
        do {
217 22
            $ret = fgetcsv($this->stream, 0, $this->delimiter, $this->enclosure, $this->escape);
218 22
        } while ($this->flags & SplFileObject::SKIP_EMPTY && $ret !== false && $ret[0] === null);
219
220 22
        return $ret;
221
    }
222
223
    /**
224
     * Retrieves the current line as a string
225
     *
226
     * @return string
227
     */
228 34
    protected function getCurrentLine()
229
    {
230
        do {
231 34
            $line = fgets($this->stream);
232 34
        } while ($this->flags & SplFileObject::SKIP_EMPTY && $line !== false && rtrim($line, "\r\n") !== '');
233
234 34
        return $line;
235
    }
236
237
    /**
238
     * Get line number
239
     *
240
     * @return int
241
     */
242 20
    public function key()
243
    {
244 20
        return $this->current_line_number;
245
    }
246
247
    /**
248
     * Read next line
249
     */
250 20
    public function next()
251
    {
252 20
        $this->current_line = false;
253 20
        $this->current_line_number++;
254 20
    }
255
256
    /**
257
     * Rewind the file to the first line
258
     */
259 40
    public function rewind()
260
    {
261 40
        rewind($this->stream);
262 40
        $this->current_line_number = 0;
263 40
        $this->current_line = false;
264 40
        if ($this->flags & SplFileObject::READ_AHEAD) {
265 10
            $this->current();
266
        }
267 40
    }
268
269
    /**
270
     * Not at EOF
271
     *
272
     * @return bool
273
     */
274 22
    public function valid()
275
    {
276 22
        if ($this->flags & SplFileObject::READ_AHEAD) {
277 8
            return $this->current() !== false;
278
        }
279
280 14
        return !feof($this->stream);
281
    }
282
283
    /**
284
     * Gets line from file
285
     *
286
     * @see http://php.net/manual/en/splfileobject.fgets.php
287
     *
288
     * @return string
289
     */
290 34
    public function fgets()
291
    {
292 34
        if (false !== $this->current_line) {
293 2
            $this->next();
294
        }
295 34
        return $this->current_line = $this->getCurrentLine();
296
    }
297
298
    /**
299
     * Output all remaining data on a file pointer
300
     *
301
     * @see http://php.net/manual/en/splfileobject.fpatssthru.php
302
     *
303
     * @return int
304
     */
305 16
    public function fpassthru()
306
    {
307 16
        return fpassthru($this->stream);
308
    }
309
310
    /**
311
     * Seek to a position
312
     *
313
     * @see http://php.net/manual/en/splfileobject.fseek.php
314
     *
315
     * @param int $offset
316
     * @param int $whence
317
     *
318
     * @return int
319
     */
320 4
    public function fseek($offset, $whence = SEEK_SET)
321
    {
322 4
        return fseek($this->stream, $offset, $whence);
323
    }
324
325
    /**
326
     * Seek a specified line
327
     *
328
     * @param int $line_pos
329
     *
330
     * @throws LogicException if the line positon is negative
331
     */
332 6
    public function seek(int $line_pos)
333
    {
334 6
        if (0 > $line_pos) {
335 2
            throw new LogicException(sprintf('Can\'t seek stream to negative line %d', $line_pos));
336
        }
337
338 4
        foreach ($this as $key => $value) {
339 4
            if ($key == $line_pos || feof($this->stream)) {
340 4
                $this->current_line_number--;
341 4
                break;
342
            }
343
        }
344
345 4
        $this->current();
346 4
    }
347
348
    /**
349
     * Write to stream
350
     *
351
     * @see http://php.net/manual/en/splfileobject.fwrite.php
352
     *
353
     * @param string $str
354
     * @param int    $length
355
     *
356
     * @return int
357
     */
358 2
    public function fwrite($str, $length = 0)
359
    {
360 2
        return fwrite($this->stream, $str, $length);
361
    }
362
363
    /**
364
     * append a filter
365
     *
366
     * @param string $filter_name
367
     *
368
     * @return resource
369
     */
370 10
    public function appendFilter(string $filter_name, int $read_write)
371
    {
372 10
        return stream_filter_append($this->stream, $filter_name, $read_write);
373
    }
374
375
    /**
376
     * prepend a filter
377
     *
378
     * @param string $filter_name
379
     *
380
     * @return resource
381
     */
382 2
    public function prependFilter(string $filter_name, int $read_write)
383
    {
384 2
        return stream_filter_prepend($this->stream, $filter_name, $read_write);
385
    }
386
387
    /**
388
     * remove all attached filters
389
     */
390 10
    public function removeFilter($resource)
391
    {
392 10
        return stream_filter_remove($resource);
393
    }
394
395
    /**
396
     * Flushes the output to a file
397
     *
398
     * @return bool
399
     */
400 2
    public function fflush()
401
    {
402 2
        return fflush($this->stream);
403
    }
404
405
    /**
406
     * @inheritdoc
407
     */
408 2
    public function __clone()
409
    {
410 2
        throw new LogicException('An object of class '.StreamIterator::class.' cannot be cloned');
411
    }
412
}
413