Completed
Push — master ( 03f444...9d4407 )
by ignace nyamagana
03:37 queued 02:25
created

ResultSet::jsonPreserveOffset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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