Completed
Push — master ( e61ddd...51ce52 )
by ignace nyamagana
06:15 queued 04:30
created

ResultSet::jsonSerialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 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 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 CallbackFilterIterator;
18
use Countable;
19
use Generator;
20
use Iterator;
21
use IteratorAggregate;
22
use League\Csv\Exception\RuntimeException;
23
use LimitIterator;
24
25
/**
26
 * Represents the result set of a {@link Reader} processed by a {@link Statement}
27
 *
28
 * @package League.csv
29
 * @since   9.0.0
30
 * @author  Ignace Nyamagana Butera <[email protected]>
31
 *
32
 */
33
class ResultSet implements Countable, IteratorAggregate
34
{
35
    use ValidatorTrait;
36
37
    /**
38
     * The CSV records collection
39
     *
40
     * @var Iterator
41
     */
42
    protected $iterator;
43
44
    /**
45
     * The CSV records collection column names
46
     *
47
     * @var array
48
     */
49
    protected $column_names = [];
50
51
    /**
52
     * Tell whether the CSV records offset must be kept on output
53
     *
54
     * @var bool
55
     */
56
    protected $preserve_offset = false;
57
58
    /**
59
     * New instance
60
     *
61
     * @param Iterator $iterator     a CSV records collection iterator
62
     * @param array    $column_names the associated collection column names
63
     */
64 98
    public function __construct(Iterator $iterator, array $column_names)
65
    {
66 98
        $this->iterator = $iterator;
67 98
        $this->column_names = $column_names;
68 98
    }
69
70
    /**
71
     * @inheritdoc
72
     */
73 98
    public function __destruct()
74
    {
75 98
        $this->iterator = null;
76 98
    }
77
78
    /**
79
     * Returns the column names associated with the ResultSet
80
     *
81
     * @return string[]
82
     */
83 4
    public function getColumnNames(): array
84
    {
85 4
        return $this->column_names;
86
    }
87
88
    /**
89
     * Tell whether the CSV document record offset must be kept on output
90
     *
91
     * @return bool
92
     */
93 2
    public function isRecordOffsetPreserved(): bool
94
    {
95 2
        return $this->preserve_offset;
96
    }
97
98
    /**
99
     * @inheritdoc
100
     */
101 10
    public function getIterator(): Generator
102
    {
103 10
        return $this->iteratorToGenerator($this->iterator);
104
    }
105
106
    /**
107
     * Return the generator depending on the preserveRecordOffset setting
108
     *
109
     * @param Iterator $iterator
110
     *
111
     * @return Generator
112
     */
113 18
    protected function iteratorToGenerator(Iterator $iterator): Generator
114
    {
115 18
        if ($this->preserve_offset) {
116 2
            foreach ($iterator as $offset => $value) {
117 2
                yield $offset => $value;
118
            }
119 2
            return;
120
        }
121
122 16
        foreach ($iterator as $value) {
123 14
            yield $value;
124
        }
125 12
    }
126
127
    /**
128
     * @inheritdoc
129
     */
130 2
    public function count(): int
131
    {
132 2
        return iterator_count($this->iterator);
133
    }
134
135
    /**
136
     * Returns a sequential array of all CSV records found
137
     *
138
     * @return array
139
     */
140 56
    public function fetchAll(): array
141
    {
142 56
        return iterator_to_array($this->iterator, $this->preserve_offset);
143
    }
144
145
    /**
146
     * Returns a single record from the CSV
147
     *
148
     * By default if no offset is provided the first row of the CSV is selected
149
     *
150
     * @param int $offset the CSV record offset
151
     *
152
     * @return array
153
     */
154 6
    public function fetchOne(int $offset = 0): array
155
    {
156 6
        $offset = $this->filterMinRange($offset, 0, 'The submitted offset must be a positive integer or 0');
157 4
        $it = new LimitIterator($this->iterator, $offset, 1);
158 4
        $it->rewind();
159
160 4
        return (array) $it->current();
161
    }
162
163
    /**
164
     * Returns the next value from a single CSV record field
165
     *
166
     * By default if no column index is provided the first column of the CSV is selected
167
     *
168
     * @param string|int $index CSV column index
169
     *
170
     * @return Generator
171
     */
172 14
    public function fetchColumn($index = 0): Generator
173
    {
174 14
        $offset = $this->getColumnIndex($index, 'The column index `%s` value is invalid');
175
        $filter = function (array $record) use ($offset): bool {
176 8
            return isset($record[$offset]);
177 8
        };
178
179
        $select = function (array $record) use ($offset): string {
180 6
            return $record[$offset];
181 8
        };
182
183 8
        $iterator = new MapIterator(new CallbackFilterIterator($this->iterator, $filter), $select);
184
185 8
        return $this->iteratorToGenerator($iterator);
186
    }
187
188
    /**
189
     * Filter a column name against the CSV header if any
190
     *
191
     * @param string|int $field         the field name or the field index
192
     * @param string     $error_message the associated error message
193
     *
194
     * @throws RuntimeException if the field is invalid
195
     * @throws RuntimeException if the column is not found
196
     *
197
     * @return string|int
198
     */
199 22
    protected function getColumnIndex($field, string $error_message)
200
    {
201 22
        if (false !== array_search($field, $this->column_names, true)) {
202 2
            return $field;
203
        }
204
205 22
        if (is_string($field)) {
206 2
            throw new RuntimeException(sprintf($error_message, $field));
207
        }
208
209 20
        $index = $this->filterMinRange($field, 0, $error_message);
210 18
        if (empty($this->column_names)) {
211 14
            return $index;
212
        }
213
214 4
        $index = array_search($index, array_flip($this->column_names), true);
215 4
        if (false !== $index) {
216 2
            return $index;
217
        }
218
219 2
        throw new RuntimeException(sprintf($error_message, $field));
220
    }
221
222
    /**
223
     * Fetches the next key-value pairs from a result set (first
224
     * column is the key, second column is the value).
225
     *
226
     * By default if no column index is provided:
227
     * - the first CSV column is used to provide the keys
228
     * - the second CSV column is used to provide the value
229
     *
230
     * @param string|int $offset_index The column index to serve as offset
231
     * @param string|int $value_index  The column index to serve as value
232
     *
233
     * @return Generator
234
     */
235 8
    public function fetchPairs($offset_index = 0, $value_index = 1): Generator
236
    {
237 8
        $offset = $this->getColumnIndex($offset_index, 'The offset index value  `%s` is invalid');
238 8
        $value = $this->getColumnIndex($value_index, 'The value index value `%s` is invalid');
239
240
        $filter = function (array $record) use ($offset): bool {
241 8
            return isset($record[$offset]);
242 8
        };
243
244 8
        $select = function (array $record) use ($offset, $value): array {
245 6
            return [$record[$offset], $record[$value] ?? null];
246 8
        };
247
248 8
        $iterator = new MapIterator(new CallbackFilterIterator($this->iterator, $filter), $select);
249 8
        foreach ($iterator as $pair) {
250 6
            yield $pair[0] => $pair[1];
251
        }
252 8
    }
253
254
    /**
255
     * Whether we should preserve the CSV document record offset.
256
     *
257
     * If set to true CSV document record offset will be added to
258
     * method output where it makes sense.
259
     *
260
     * @param bool $status
261
     *
262
     * @return self
263
     */
264 4
    public function preserveRecordOffset(bool $status): self
265
    {
266 4
        $this->preserve_offset = $status;
267
268 4
        return $this;
269
    }
270
}
271