StreamIterator   B
last analyzed

↳ Parent: Project

Coupling/Cohesion

Components 1
Dependencies 3

Complexity

Total Complexity 42

Size/Duplication

Total Lines 398
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 398
ccs 91
cts 91
cp 1
rs 8.295
c 0
b 0
f 0
wmc 42
lcom 1
cbo 3

22 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 16 5
A __destruct() 0 4 1
A setCsvControl() 0 6 1
A setFlags() 0 4 1
A fputcsv() 0 10 1
A current() 0 16 3
A getCurrentRecord() 0 8 4
A getCurrentLine() 0 8 4
A key() 0 4 1
A next() 0 5 1
A rewind() 0 9 2
A valid() 0 8 2
A fgets() 0 7 2
A fpassthru() 0 4 1
A fread() 0 4 1
A fseek() 0 4 1
B seek() 0 15 5
A fwrite() 0 4 1
A appendFilter() 0 9 2
A removeFilter() 0 4 1
A fflush() 0 4 1
A __clone() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like StreamIterator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use StreamIterator, and based on these observations, apply Extract Interface, too.

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