Completed
Pull Request — master (#207)
by ignace nyamagana
09:14
created

Reader::fetchAll()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 0
cp 0
cc 1
eloc 2
nc 1
nop 0
crap 2
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 DomDocument;
18
use Generator;
19
use InvalidArgumentException;
20
use Iterator;
21
use IteratorAggregate;
22
use JsonSerializable;
23
use League\Csv\Config\StatementTrait;
24
25
/**
26
 *  A class to manage extracting and filtering a CSV
27
 *
28
 * @package League.csv
29
 * @since  3.0.0
30
 *
31
 */
32
class Reader extends AbstractCsv implements JsonSerializable, IteratorAggregate
33
{
34
    use StatementTrait;
35
36
    /**
37
     * Charset Encoding for the CSV
38
     *
39
     * @var string
40
     */
41
    protected $input_encoding = 'UTF-8';
42
43 159
    /**
44
     * @inheritdoc
45 159
     */
46
    protected $stream_filter_mode = STREAM_FILTER_READ;
47
48
    /**
49
     * Gets the source CSV encoding charset
50
     *
51
     * @return string
52
     */
53
    public function getInputEncoding(): string
54
    {
55
        return $this->input_encoding;
56 159
    }
57
58 159
    /**
59 75
     * Sets the CSV encoding charset
60
     *
61
     * @param string $str
62 144
     *
63
     * @return static
64
     */
65
    public function setInputEncoding(string $str): self
66
    {
67
        $str = str_replace('_', '-', $str);
68
        $str = filter_var($str, FILTER_SANITIZE_STRING, ['flags' => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH]);
69
        if (empty($str)) {
70
            throw new InvalidArgumentException('you should use a valid charset');
71
        }
72
        $this->input_encoding = strtoupper($str);
73
74 63
        return $this;
75
    }
76 63
77
    /**
78
     * Returns a HTML table representation of the CSV Table
79
     *
80
     * @param string $class_attr optional classname
81
     *
82
     * @return string
83
     */
84
    public function toHTML(string $class_attr = 'table-csv-data'): string
85
    {
86
        $doc = $this->toXML('table', 'tr', 'td');
87
        $doc->documentElement->setAttribute('class', $class_attr);
88
89 9
        return $doc->saveHTML($doc->documentElement);
90
    }
91 9
92 9
    /**
93 9
     * Transforms a CSV into a XML
94 9
     *
95 6
     * @param string $root_name XML root node name
96 9
     * @param string $row_name  XML row node name
97 9
     * @param string $cell_name XML cell node name
98
     *
99 6
     * @return DomDocument
100 9
     */
101 9
    public function toXML(string $root_name = 'csv', string $row_name = 'row', string $cell_name = 'cell'): DomDocument
102 6
    {
103
        $doc = new DomDocument('1.0', 'UTF-8');
104 9
        $root = $doc->createElement($root_name);
105
        foreach ($this->convertToUtf8($this->getIterator()) as $row) {
106
            $rowElement = $doc->createElement($row_name);
107
            array_walk($row, function ($value) use (&$rowElement, $doc, $cell_name) {
108
                $content = $doc->createTextNode($value);
109
                $cell = $doc->createElement($cell_name);
110
                $cell->appendChild($content);
111
                $rowElement->appendChild($cell);
112
            });
113
            $root->appendChild($rowElement);
114
        }
115
        $doc->appendChild($root);
116 12
117
        return $doc;
118 12
    }
119 12
120 12
    /**
121 12
     * Convert Csv file into UTF-8
122
     *
123 12
     * @param Iterator $iterator
124
     *
125
     * @return Iterator
126
     */
127
    protected function convertToUtf8(Iterator $iterator): Iterator
128
    {
129
        if (stripos($this->input_encoding, 'UTF-8') !== false) {
130
            return $iterator;
131
        }
132
133
        $convert_cell = function ($value) {
134
            return mb_convert_encoding($value, 'UTF-8', $this->input_encoding);
135
        };
136
137
        $convert_row = function (array $row) use ($convert_cell) {
138 21
            return array_map($convert_cell, $row);
139
        };
140 21
141
        return new MapIterator($iterator, $convert_row);
142
    }
143 18
144 18
    /**
145
     * @inheritdoc
146
     */
147 15
    public function jsonSerialize()
148 18
    {
149
        return iterator_to_array($this->convertToUtf8($this->getIterator()), false);
150 18
    }
151 18
152 18
    /**
153 18
     * Returns a sequential array of all CSV lines
154
     *
155 18
     * The callable function will be applied to each Iterator item
156
     *
157
     * @return array
158
     */
159
    public function fetchAll(): array
160
    {
161
        return iterator_to_array($this->getIterator(), false);
162
    }
163
164
    /**
165
     * Returns a single row from the CSV
166
     *
167
     * By default if no offset is provided the first row of the CSV is selected
168
     *
169
     * @param int $offset the CSV row offset
170
     *
171
     * @return array
172
     */
173
    public function fetchOne(int $offset = 0): array
174 27
    {
175
        $this->setOffset($offset);
176 27
        $this->setLimit(1);
177 27
        $iterator = $this->getIterator();
178
        $iterator->rewind();
179 27
180 27
        return (array) $iterator->current();
181
    }
182
183 24
    /**
184 24
     * Returns the next value from a single CSV column
185 16
     *
186 27
     * The callable function will be applied to each value to be return
187
     *
188 27
     * By default if no column index is provided the first column of the CSV is selected
189 27
     *
190 27
     * @param string|int $column_index CSV column index
191 27
     *
192
     * @return Iterator
193 27
     */
194
    public function fetchColumn($column_index = 0): Iterator
195
    {
196
        $column_index = $this->getFieldIndex($column_index, 'the column index value is invalid');
197
        $filter = function (array $row) use ($column_index) {
198
            return isset($row[$column_index]);
199
        };
200
201
        $select = function ($row) use ($column_index) {
202
            return $row[$column_index];
203 27
        };
204
205 27
        $this->addFilter($filter);
206 24
207 18
        return new MapIterator($this->getIterator(), $select);
208 27
    }
209
210
    /**
211
     * Filter a field name against the CSV header if any
212
     *
213
     * @param string|int $field         the field name or the field index
214
     * @param string     $error_message the associated error message
215
     *
216
     * @throws InvalidArgumentException if the field is invalid
217
     *
218
     * @return string|int
219
     */
220
    protected function getFieldIndex($field, $error_message)
221
    {
222
        if (false !== array_search($field, $this->header, true)) {
223
            return $field;
224
        }
225 39
226
        $index = $this->filterInteger($field, 0, $error_message);
227 39
        if (empty($this->header)) {
228 27
            return $index;
229
        }
230 27
231 6
        if (false !== ($index = array_search($index, array_flip($this->header), true))) {
232 4
            return $index;
233
        }
234 27
235 27
        throw new InvalidArgumentException($error_message);
236
    }
237 27
238 27
    /**
239 27
     * Fetches the next key-value pairs from a result set (first
240
     * column is the key, second column is the value).
241 27
     *
242
     * By default if no column index is provided:
243
     * - the first CSV column is used to provide the keys
244
     * - the second CSV column is used to provide the value
245
     *
246
     * @param string|int $offset_index The column index to serve as offset
247
     * @param string|int $value_index  The column index to serve as value
248
     *
249
     * @return Generator
250
     */
251
    public function fetchPairs($offset_index = 0, $value_index = 1): Generator
252
    {
253
        $offset = $this->getFieldIndex($offset_index, 'the offset index value is invalid');
254 39
        $value = $this->getFieldIndex($value_index, 'the value index value is invalid');
255
        $filter = function ($row) use ($offset) {
256 39
            return isset($row[$offset]);
257 21
        };
258
259
        $select = function ($row) use ($offset, $value) {
260 18
            return [$row[$offset], isset($row[$value]) ? $row[$value] : null];
261 12
        };
262 18
263 6
        $this->addFilter($filter);
264 12
        foreach (new MapIterator($this->getIterator(), $select) as $row) {
265 15
            yield $row[0] => $row[1];
266 12
        }
267 12
    }
268
}
269