StreamIterator   B
last analyzed

↳ Parent: Project

Coupling/Cohesion

Components 1
Dependencies 3

Complexity

Total Complexity 41

Size/Duplication

Total Lines 369
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 41
lcom 1
cbo 3
dl 0
loc 369
c 0
b 0
f 0
ccs 88
cts 88
cp 1
rs 8.2769

22 Methods

Rating   Name   Duplication   Size   Complexity  
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 fseek() 0 4 1
B seek() 0 15 5
A fwrite() 0 4 1
A appendFilter() 0 4 1
A prependFilter() 0 4 1
A removeFilter() 0 4 1
A fflush() 0 4 1
A __clone() 0 4 1
B __construct() 0 16 5

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