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

Reader::getFieldIndex()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

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