Completed
Push — master ( 98092a...390b27 )
by Marc
02:28
created

CsvReader::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 11
rs 9.4285
cc 1
eloc 7
nc 1
nop 5
1
<?php
2
3
/*
4
 * This file is part of the kaloa/filesystem package.
5
 *
6
 * For full copyright and license information, please view the LICENSE file
7
 * that was distributed with this source code.
8
 */
9
10
namespace Kaloa\Filesystem;
11
12
use Exception;
13
use LogicException;
14
15
/**
16
 *
17
 */
18
final class CsvReader
19
{
20
    /**
21
     * @var resource
22
     */
23
    private $stream;
24
25
    /**
26
     * @var string
27
     */
28
    private $delimiter;
29
30
    /**
31
     * @var string
32
     */
33
    private $enclosure;
34
35
    /**
36
     * @var string
37
     */
38
    private $escape;
39
40
    /**
41
     * @var string
42
     */
43
    private $encoding;
44
45
    /**
46
     * @var int
47
     */
48
    private $fetchRowLineCounter = 0;
49
50
    /**
51
     * @var array
52
     */
53
    private $fetchRowKeys = array();
54
55
    /**
56
     * @param resource $stream
57
     * @param string $encoding
58
     * @param string $delimiter
59
     * @param string $enclosure
60
     * @param string $escape
61
     * @throws Exception
62
     */
63
    public function __construct($stream, $encoding = 'UTF-8', $delimiter = ',', $enclosure = '"', $escape = '\\')
64
    {
65
        $this->stream = $stream;
66
67
        $this->assertStream();
68
69
        $this->delimiter = $delimiter;
70
        $this->enclosure = $enclosure;
71
        $this->escape    = $escape;
72
        $this->encoding  = $encoding;
73
    }
74
75
    /**
76
     * @return array
77
     */
78
    public function fetchAll()
79
    {
80
        $data = array();
81
82
        while ($row = $this->getNextRow()) {
83
            $data[] = $this->negotiateEncoding($row);
0 ignored issues
show
Bug introduced by
It seems like $row defined by $this->getNextRow() on line 82 can also be of type boolean; however, Kaloa\Filesystem\CsvReader::negotiateEncoding() does only seem to accept array, 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...
84
        }
85
86
        return $data;
87
    }
88
89
    /**
90
     * @param array $keys If not empty, use as array keys. Otherwise read keys
91
     *                    from first line of input data
92
     * @return array
93
     * @throws Exception
94
     */
95
    public function fetchAllAssoc(array $keys = array())
96
    {
97
        $headerFlag = true;
98
        $keysCount = count($keys);
99
100
        if ($keysCount > 0) {
101
            $headerFlag = false;
102
        }
103
104
        $data = array();
105
106
        while ($row = $this->getNextRow()) {
107
            if ($headerFlag) {
108
                $headerFlag = false;
109
                $keys = $this->negotiateEncoding($row);
0 ignored issues
show
Bug introduced by
It seems like $row defined by $this->getNextRow() on line 106 can also be of type boolean; however, Kaloa\Filesystem\CsvReader::negotiateEncoding() does only seem to accept array, 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...
110
                $keysCount = count($keys);
111
                continue;
112
            }
113
114
            if (count($row) !== $keysCount) {
115
                throw new Exception('Malformed line in CSV input');
116
            }
117
118
            $data[] = array_combine($keys, $this->negotiateEncoding($row));
0 ignored issues
show
Bug introduced by
It seems like $row defined by $this->getNextRow() on line 106 can also be of type boolean; however, Kaloa\Filesystem\CsvReader::negotiateEncoding() does only seem to accept array, 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...
119
        }
120
121
        return $data;
122
    }
123
124
    /**
125
     * @param array $keys
126
     * @return array|bool
127
     */
128
    public function fetchAssoc(array $keys = array())
129
    {
130
        $headerFlag = empty($keys);
131
132
        $row = $this->getNextRow();
133
134
        if (!is_array($row)) {
135
            // EOF
136
            return false;
137
        }
138
139
        if (0 === $this->fetchRowLineCounter) {
140
            if ($headerFlag) {
141
                $this->fetchRowKeys = $this->negotiateEncoding($row);
142
                $this->fetchRowLineCounter++;
143
                return $this->{__FUNCTION__}($keys);
144
            } else {
145
                $this->fetchRowKeys = $keys;
146
            }
147
        }
148
149
        $this->fetchRowLineCounter++;
150
151
        return array_combine($this->fetchRowKeys, $this->negotiateEncoding($row));
152
    }
153
154
    /**
155
     * @return array|bool
156
     */
157
    private function getNextRow()
158
    {
159
        $this->assertStream();
160
161
        $tmp = fgetcsv($this->stream, 0, $this->delimiter, $this->enclosure, $this->escape);
162
163
        if ($tmp !== false && !is_array($tmp)) {
164
            throw new LogicException('Unexpected error in fgetcsv call');
165
        }
166
167
        return $tmp;
168
    }
169
170
    /**
171
     * @throws Exception
172
     */
173
    private function assertStream()
174
    {
175
        set_error_handler(function () {}, E_WARNING);
176
        $tmp = get_resource_type($this->stream);
177
        restore_error_handler();
178
179
        if (!is_string($tmp) || 'stream' !== $tmp) {
180
            throw new Exception('Supplied input is not a valid stream resource');
181
        }
182
    }
183
184
    /**
185
     * @param array $elements
186
     * @return array
187
     */
188
    private function negotiateEncoding(array $elements)
189
    {
190
        if ('UTF-8' !== $this->encoding) {
191
            foreach ($elements as &$element) {
192
                $element = mb_convert_encoding($element, 'UTF-8', $this->encoding);
193
            }
194
            unset($element);
195
        }
196
197
        return $elements;
198
    }
199
}
200