Completed
Pull Request — master (#178)
by ignace nyamagana
04:41
created

RecordSet::fetchAll()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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 8.1.1
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
namespace League\Csv;
14
15
use CallbackFilterIterator;
16
use Countable;
17
use DOMDocument;
18
use DOMElement;
19
use Generator;
20
use InvalidArgumentException;
21
use Iterator;
22
use IteratorAggregate;
23
use JsonSerializable;
24
use League\Csv\Config\Validator;
25
use LimitIterator;
26
use OutOfRangeException;
27
28
/**
29
 * A class to extract and convert data from a CSV
30
 *
31
 * @package League.csv
32
 * @since  3.0.0
33
 *
34
 */
35
class RecordSet implements Countable, IteratorAggregate, JsonSerializable
36
{
37
    use Validator;
38
39
    /**
40
     * @var array
41
     */
42
    protected $header;
43
44
    /**
45
     * @var array
46
     */
47
    protected $flip_header;
48
49
    /**
50
     * @var int
51
     */
52
    protected $header_column_count;
53
54
    /**
55
     * @var Iterator
56
     */
57
    protected $iterator;
58
59
    /**
60
     * New Instance
61
     *
62
     * @param Iterator $iterator
63
     * @param array    $header
64
     */
65
    public function __construct(Iterator $iterator, array $header)
66
    {
67
        $this->header = $header;
68
        $this->flip_header = array_flip($header);
69
        $this->header_column_count = count($header);
70
        $this->iterator = $this->formatIterator($iterator);
71
    }
72
73
    /**
74
     * Apply CSV header to the return iterator
75
     *
76
     * @param Iterator $iterator
77
     *
78
     * @throws OutOfRangeException if the column is inconsistent
79
     *
80
     * @return Iterator
81
     */
82
    protected function formatIterator(Iterator $iterator)
83
    {
84
        if (0 === $this->header_column_count) {
85
            return $iterator;
86
        }
87
88
        $combine_array = function (array $row) {
89
            if ($this->header_column_count != count($row)) {
90
                throw new OutOfRangeException('The record column count differs from the header column count');
91
            }
92
93
            return array_combine($this->header, $row);
94
        };
95
96
        return new MapIterator($iterator, $combine_array);
97
    }
98
99
    /**
100
     * @inheritdoc
101
     */
102
    public function __destruct()
103
    {
104
        $this->iterator = null;
105
    }
106
107
    /**
108
     * Returns a HTML table representation of the CSV Table
109
     *
110
     * @param string $class_attr optional classname
111
     *
112
     * @return string
113
     */
114
    public function toHTML($class_attr = 'table-csv-data')
115
    {
116
        $doc = $this->toXML('table', 'tr', 'td');
117
        $doc->documentElement->setAttribute('class', $class_attr);
118
119
        return $doc->saveHTML($doc->documentElement);
120
    }
121
122
    /**
123
     * Transforms a CSV into a XML
124
     *
125
     * @param string $root_name XML root node name
126
     * @param string $row_name  XML row node name
127
     * @param string $cell_name XML cell node name
128
     *
129
     * @return DOMDocument
130
     */
131
    public function toXML($root_name = 'csv', $row_name = 'row', $cell_name = 'cell')
132
    {
133
        $doc = new DOMDocument('1.0', 'UTF-8');
134
        $root = $doc->createElement($root_name);
135
136
        if (0 < $this->header_column_count) {
137
            $root->appendChild($this->convertRowToXML($this->header, $doc, $row_name, $cell_name));
138
        }
139
140
        foreach ($this->iterator as $row) {
141
            $root->appendChild($this->convertRowToXML($row, $doc, $row_name, $cell_name));
142
        }
143
        $doc->appendChild($root);
144
145
        return $doc;
146
    }
147
148
    /**
149
     * Convert a row into a DOMElement
150
     *
151
     * @param array       $row       Csv record
152
     * @param DOMDocument $doc
153
     * @param string      $row_name  XML row node name
154
     * @param string      $cell_name XML cell node name
155
     *
156
     * @return DOMElement
157
     */
158
    protected function convertRowToXML(array $row, DOMDocument $doc, $row_name, $cell_name)
159
    {
160
        $node = $doc->createElement($row_name);
161
        foreach ($row as $value) {
162
            $cell = $doc->createElement($cell_name);
163
            $cell->appendChild($doc->createTextNode($value));
164
            $node->appendChild($cell);
165
        }
166
167
        return $node;
168
    }
169
170
    /**
171
     * @inheritdoc
172
     */
173
    public function getIterator()
174
    {
175
        return $this->iterator;
176
    }
177
178
    /**
179
     * @inheritdoc
180
     */
181
    public function count()
182
    {
183
        return iterator_count($this);
184
    }
185
186
    /**
187
     * @inheritdoc
188
     */
189
    public function jsonSerialize()
190
    {
191
        return $this->fetchAll();
192
    }
193
194
    /**
195
     * Returns a sequential array of all founded RecordSet
196
     *
197
     * @return array
198
     */
199
    public function fetchAll()
200
    {
201
        return iterator_to_array($this, false);
202
    }
203
204
    /**
205
     * Returns a single record from the recordSet
206
     *
207
     * @param int $offset the record offset relative to the RecordSet
208
     *
209
     * @return array
210
     */
211
    public function fetchOne($offset = 0)
212
    {
213
        $offset = $this->validateInteger($offset, 0, 'the offset valid is invalid');
214
        $it = new LimitIterator($this->iterator, $offset, 1);
215
        $it->rewind();
216
217
        return (array) $it->current();
218
    }
219
220
    /**
221
     * Returns the next value from a specific record column
222
     *
223
     * By default if no column index is provided the first column of the founded RecordSet is returned
224
     *
225
     * @param string|int $column_index CSV column index or header field name
226
     *
227
     * @return Iterator
228
     */
229
    public function fetchColumn($column_index = 0)
230
    {
231
        $column_index = $this->filterFieldName($column_index, 'the column index value is invalid');
232
        $filter = function ($row) use ($column_index) {
233
            return isset($row[$column_index]);
234
        };
235
        $select = function ($row) use ($column_index) {
236
            return $row[$column_index];
237
        };
238
239
        return new MapIterator(new CallbackFilterIterator($this->iterator, $filter), $select);
240
    }
241
242
    /**
243
     * Filter a field name against the CSV header if any
244
     *
245
     * @param string|int $field         the field name or the field index
246
     * @param string     $error_message the associated error message
247
     *
248
     * @throws InvalidArgumentException if the field is invalid
249
     *
250
     * @return string|int
251
     */
252
    protected function filterFieldName($field, $error_message)
253
    {
254
        if (false !== array_search($field, $this->header, true)) {
255
            return $field;
256
        }
257
258
        $index = $this->validateInteger($field, 0, $error_message);
259
        if (empty($this->header)) {
260
            return $index;
261
        }
262
263
        if (false !== ($index = array_search($index, $this->flip_header, true))) {
264
            return $index;
265
        }
266
267
        throw new InvalidArgumentException($error_message);
268
    }
269
270
    /**
271
     * Fetches the next key-value pairs from a result set (first
272
     * column is the key, second column is the value).
273
     *
274
     * By default if no column index is provided:
275
     * - the first CSV column is used to provide the keys
276
     * - the second CSV column is used to provide the value
277
     *
278
     * @param string|int $offset_index The field index or name to serve as offset
279
     * @param string|int $value_index  The field index or name to serve as value
280
     *
281
     * @return Generator
282
     */
283
    public function fetchPairs($offset_index = 0, $value_index = 1)
284
    {
285
        $offset_index = $this->filterFieldName($offset_index, 'the offset column index is invalid');
286
        $value_index = $this->filterFieldName($value_index, 'the value column index is invalid');
287
        $filter = function ($row) use ($offset_index) {
288
            return isset($row[$offset_index]);
289
        };
290
        $select = function ($row) use ($offset_index, $value_index) {
291
            return [$row[$offset_index], isset($row[$value_index]) ? $row[$value_index] : null];
292
        };
293
294
        $iterator = new MapIterator(new CallbackFilterIterator($this->iterator, $filter), $select);
295
        foreach ($iterator as $row) {
296
            yield $row[0] => $row[1];
297
        }
298
    }
299
}
300