Reader::getFileHandlerOpenMode()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
/**
3
 * @author: stev leibelt <[email protected]>
4
 * @since: 2015-04-17
5
 */
6
7
namespace Net\Bazzline\Component\Csv\Reader;
8
9
//@see https://github.com/ajgarlag/AjglCsv/blob/master/Reader/ReaderAbstract.php
10
//@see https://github.com/jwage/easy-csv/blob/master/lib/EasyCSV/Reader.php
11
//@todo implement save version to call enable/disable headline before setDelimiter etc.
12
use Net\Bazzline\Component\Csv\AbstractBase;
13
use Net\Bazzline\Component\Csv\InvalidArgumentException;
14
use Net\Bazzline\Component\Toolbox\HashMap\Combine;
15
use SplFileObject;
16
17
class Reader extends AbstractBase implements ReaderInterface
18
{
19
    /** @var bool */
20
    private $addHeadlineToOutput = true;
21
22
    /** @var Combine */
23
    private $combine;
24
25
    /** @var int */
26
    private $initialLineNumber = 0;
27
28
    /**
29
     * @param null $currentLineNumber
30
     * @return array|bool|string
31
     */
32
    public function __invoke($currentLineNumber = null)
33
    {
34
        return $this->readOne($currentLineNumber);
35
    }
36
37
    //begin of AbstractBase
38
    /**
39
     * @param string $delimiter
40
     * @throws InvalidArgumentException
41
     */
42
    public function setDelimiter($delimiter)
43
    {
44
        parent::setDelimiter($delimiter);
45
        if ($this->hasHeadline()) {
46
            $this->enableHasHeadline();
47
        }
48
    }
49
50
    /**
51
     * @param string $enclosure
52
     * @throws InvalidArgumentException
53
     */
54
    public function setEnclosure($enclosure)
55
    {
56
        parent::setEnclosure($enclosure);
57
        if ($this->hasHeadline()) {
58
            $this->enableHasHeadline();
59
        }
60
    }
61
62
    /**
63
     * @param string $escapeCharacter
64
     * @throws InvalidArgumentException
65
     */
66
    public function setEscapeCharacter($escapeCharacter)
67
    {
68
        parent::setEscapeCharacter($escapeCharacter);
69
        if ($this->hasHeadline()) {
70
            $this->enableHasHeadline();
71
        }
72
    }
73
74
    //end of AbstractBase
75
76
    //begin of Iterator
77
    /**
78
     * (PHP 5 &gt;= 5.0.0)<br/>
79
     * Return the current element
80
     * @link http://php.net/manual/en/iterator.current.php
81
     * @return mixed Can return any type.
82
     */
83
    public function current()
84
    {
85
        return $this->getFileHandler()->current();
86
    }
87
88
    /**
89
     * (PHP 5 &gt;= 5.0.0)<br/>
90
     * Move forward to next element
91
     * @link http://php.net/manual/en/iterator.next.php
92
     * @return void Any returned value is ignored.
93
     */
94
    public function next()
95
    {
96
        $this->getFileHandler()->next();
97
    }
98
99
    /**
100
     * (PHP 5 &gt;= 5.0.0)<br/>
101
     * Return the key of the current element
102
     * @link http://php.net/manual/en/iterator.key.php
103
     * @return mixed scalar on success, or null on failure.
104
     */
105
    public function key()
106
    {
107
        return $this->getFileHandler()->key();
108
    }
109
110
    /**
111
     * (PHP 5 &gt;= 5.0.0)<br/>
112
     * Checks if current position is valid
113
     * @link http://php.net/manual/en/iterator.valid.php
114
     * @return boolean The return value will be casted to boolean and then evaluated.
115
     * Returns true on success or false on failure.
116
     */
117
    public function valid()
118
    {
119
        return $this->getFileHandler()->valid();
120
    }
121
122
    /**
123
     * (PHP 5 &gt;= 5.0.0)<br/>
124
     * Rewind the Iterator to the first element
125
     * @link http://php.net/manual/en/iterator.rewind.php
126
     * @return void Any returned value is ignored.
127
     */
128
    public function rewind()
129
    {
130
        if ($this->hasHeadline()) {
131
            $this->updateHeadline();
132
            $lineNumber = 1;
133
        } else {
134
            $lineNumber = 0;
135
        }
136
        $this->initialLineNumber = $lineNumber;
137
        $this->seekFileToCurrentLineNumberIfNeeded(
138
            $this->getFileHandler(),
0 ignored issues
show
Bug introduced by
It seems like $this->getFileHandler() targeting Net\Bazzline\Component\C...tBase::getFileHandler() can also be of type resource; however, Net\Bazzline\Component\C...entLineNumberIfNeeded() does only seem to accept object<SplFileObject>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
139
            $lineNumber
140
        );
141
    }
142
    //end of Iterator
143
144
    //begin of headlines
145
    /**
146
     * @return $this
147
     */
148
    public function disableAddHeadlineToOutput()
149
    {
150
        $this->addHeadlineToOutput = false;
151
152
        return $this;
153
    }
154
155
    /**
156
     * @return $this
157
     */
158
    public function enableAddHeadlineToOutput()
159
    {
160
        $this->addHeadlineToOutput = true;
161
162
        return $this;
163
    }
164
165
    /**
166
     * @return $this
167
     */
168
    public function disableHasHeadline()
169
    {
170
        $this->resetHeadline();
171
        $this->rewind();
172
173
        return $this;
174
    }
175
176
    /**
177
     * @return $this
178
     */
179
    public function enableHasHeadline()
180
    {
181
        $this->updateHeadline();
182
        $this->rewind();
183
184
        return $this;
185
    }
186
187
    private function updateHeadline()
188
    {
189
        $this->initialLineNumber    = 0;
190
        $wasEnabled                 = $this->addHeadlineToOutput;
191
192
        if ($wasEnabled) {
193
            $this->disableAddHeadlineToOutput();
194
        }
195
        $this->setHeadline($this->readOne(0));
196
        if ($wasEnabled) {
197
            $this->enableAddHeadlineToOutput();
198
        }
199
    }
200
201
    /**
202
     * @return false|array
203
     */
204
    public function readHeadline()
205
    {
206
        return $this->getHeadline();
207
    }
208
    //end of headlines
209
210
    //begin of general
211
    /**
212
     * @param Combine $combine
213
     * @return $this
214
     */
215
    public function setCombine(Combine $combine)
216
    {
217
        $this->combine = $combine;
218
219
        return $this;
220
    }
221
222
    /**
223
     * @param null|int $lineNumber - if "null", current line number is used
224
     * @return array|bool|string
225
     */
226
    public function readOne($lineNumber = null)
227
    {
228
        $file           = $this->getFileHandler();
229
        $headline       = $this->getHeadline();
230
        $hasHeadline    = $this->hasHeadline();
231
        $this->seekFileToCurrentLineNumberIfNeeded($file, $lineNumber);
0 ignored issues
show
Bug introduced by
It seems like $file defined by $this->getFileHandler() on line 228 can also be of type resource; however, Net\Bazzline\Component\C...entLineNumberIfNeeded() does only seem to accept object<SplFileObject>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
232
233
        $addHeadline    = ($hasHeadline && $this->addHeadlineToOutput && ($this->current() !== false));
234
        $content        = ($addHeadline)
235
            ? $this->combine->combine($headline, $this->current())
0 ignored issues
show
Security Bug introduced by
It seems like $headline defined by $this->getHeadline() on line 229 can also be of type false; however, Net\Bazzline\Component\T...hMap\Combine::combine() does only seem to accept array, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
236
            : $this->current();
237
        $this->next();
238
239
        return $content;
240
    }
241
242
    /**
243
     * @param int $length
244
     * @param null|int $lineNumberToStartWith - if "null", current line number is used
245
     * @return array
246
     */
247
    public function readMany($length, $lineNumberToStartWith = null)
248
    {
249
        $this->rewind();
250
        $lastLine       = $lineNumberToStartWith + $length;
251
        $lines          = [];
252
        $currentLine    = $lineNumberToStartWith;
253
254
        //foreach not usable here since it is calling rewind before iterating
255
        while ($currentLine < $lastLine) {
256
            $line   = $this->readOne($currentLine);
257
            $lines  = $this->addToLinesIfLineIsValid($lines, $line);
258
            if (!$this->valid()) {
259
                $currentLine = $lastLine;
260
            }
261
            ++$currentLine;
262
        }
263
264
        return $lines;
265
    }
266
267
    /**
268
     * @return array
269
     */
270
    public function readAll()
271
    {
272
        $this->rewind();
273
        $lines = [];
274
275
        while ($line = $this()) {
276
            $lines = $this->addToLinesIfLineIsValid($lines, $line);
277
        }
278
279
        return $lines;
280
    }
281
    //end of general
282
283
    /**
284
     * @return string
285
     */
286
    protected function getFileHandlerOpenMode()
287
    {
288
        return 'r';
289
    }
290
291
    /**
292
     * @param array $lines
293
     * @param mixed $line
294
     * @return array
295
     */
296
    private function addToLinesIfLineIsValid(array &$lines, $line)
297
    {
298
        if (!is_null($line)) {
299
            $lines[] = $line;
300
        }
301
302
        return $lines;
303
    }
304
305
    /**
306
     * @param SplFileObject $file
307
     * @param null|int $newLineNumber
308
     * @return SplFileObject
309
     */
310
    private function seekFileToCurrentLineNumberIfNeeded(SplFileObject $file, $newLineNumber = null)
311
    {
312
        $seekIsNeeded = ((!is_null($newLineNumber))
313
            && ($newLineNumber >= $this->initialLineNumber)
314
            && ($newLineNumber !== $this->key()));
315
316
        if ($seekIsNeeded) {
317
            $file->seek($newLineNumber);
318
        }
319
320
        return $file;
321
    }
322
}
323