Completed
Push — master ( 9d4407...627ff4 )
by ignace nyamagana
03:38 queued 02:22
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
     * New instance
53
     *
54
     * @param Iterator $records a CSV records collection iterator
55
     * @param array    $header  the associated collection column names
56
     */
57 8
    public function __construct(Iterator $records, array $header)
58
    {
59 8
        $this->records = $records;
60 8
        $this->header = $header;
61 8
    }
62
63
    /**
64
     * @inheritdoc
65
     */
66 10
    public function __destruct()
67
    {
68 10
        unset($this->records);
69 10
    }
70
71
    /**
72
     * Returns the column names associated with the ResultSet
73
     *
74
     * @return string[]
75
     */
76 2
    public function getHeader(): array
77
    {
78 2
        return $this->header;
79
    }
80
81
    /**
82
     * @inheritdoc
83
     */
84 8
    public function getRecords(): Generator
85
    {
86 8
        foreach ($this->records as $value) {
87 4
            yield $value;
88
        }
89 8
    }
90
91
    /**
92
     * @inheritdoc
93
     */
94 12
    public function getIterator(): Generator
95
    {
96 12
        return $this->getRecords();
97
    }
98
99
    /**
100
     * @inheritdoc
101
     */
102 2
    public function count(): int
103
    {
104 2
        return iterator_count($this->records);
105
    }
106
107
    /**
108
     * @inheritdoc
109
     */
110 2
    public function jsonSerialize(): array
111
    {
112 2
        return iterator_to_array($this->records, false);
113
    }
114
115
    /**
116
     * Returns a the nth record from the resultset
117
     *
118
     * By default if no index is provided the first record of the resultet is returned
119
     *
120
     * @param int $nth_record the CSV record offset
121
     *
122
     * @throws OutOfRangeException if argument is lesser than 0
123
     *
124
     * @return array
125
     */
126 4
    public function fetchOne(int $nth_record = 0): array
127
    {
128 4
        if ($nth_record < 0) {
129 2
            throw new OutOfRangeException(sprintf('%s() expects the submitted offset to be a positive integer or 0, %s given', __METHOD__, $nth_record));
130
        }
131
132 2
        $iterator = new LimitIterator($this->records, $nth_record, 1);
133 2
        $iterator->rewind();
134
135 2
        return (array) $iterator->current();
136
    }
137
138
    /**
139
     * Returns the next value from a single CSV record field
140
     *
141
     * By default if no column index is provided the first column of the CSV is selected
142
     *
143
     * @param string|int $index CSV column index
144
     *
145
     * @return Generator
146
     */
147 14
    public function fetchColumn($index = 0): Generator
148
    {
149 14
        $offset = $this->getColumnIndex($index, __METHOD__.'() expects the column index to be a valid string or integer, `%s` given');
150
        $filter = function (array $record) use ($offset): bool {
151 8
            return isset($record[$offset]);
152 8
        };
153
154
        $select = function (array $record) use ($offset): string {
155 6
            return $record[$offset];
156 8
        };
157
158 8
        $iterator = new MapIterator(new CallbackFilterIterator($this->records, $filter), $select);
159 8
        foreach ($iterator as $offset => $value) {
160 6
            yield $offset => $value;
161
        }
162 4
    }
163
164
    /**
165
     * Filter a column name against the CSV header if any
166
     *
167
     * @param string|int $field         the field name or the field index
168
     * @param string     $error_message the associated error message
169
     *
170
     * @return string|int
171
     */
172 20
    protected function getColumnIndex($field, string $error_message)
173
    {
174 20
        $method = 'getColumnIndexByKey';
175 20
        if (is_string($field)) {
176 4
            $method = 'getColumnIndexByValue';
177
        }
178
179 20
        return $this->$method($field, $error_message);
180
    }
181
182
    /**
183
     * Returns the selected column name
184
     *
185
     * @param string $value
186
     * @param string $error_message
187
     *
188
     * @throws RuntimeException if the column is not found
189
     *
190
     * @return string
191
     */
192 4
    protected function getColumnIndexByValue(string $value, string $error_message): string
193
    {
194 4
        if (false !== array_search($value, $this->header, true)) {
195 2
            return $value;
196
        }
197
198 2
        throw new RuntimeException(sprintf($error_message, $value));
199
    }
200
201
    /**
202
     * Returns the selected column name according to its offset
203
     *
204
     * @param int    $index
205
     * @param string $error_message
206
     *
207
     * @throws OutOfRangeException if the field index is invalid
208
     * @throws RuntimeException    if the field is invalid or not found
209
     *
210
     * @return int|string
211
     */
212 12
    protected function getColumnIndexByKey(int $index, string $error_message)
213
    {
214 12
        if ($index < 0) {
215 2
            throw new OutOfRangeException($error_message);
216
        }
217
218 10
        if (empty($this->header)) {
219 6
            return $index;
220
        }
221
222 4
        $value = array_search($index, array_flip($this->header), true);
223 4
        if (false !== $value) {
224 2
            return $value;
225
        }
226
227 2
        throw new RuntimeException(sprintf($error_message, $index));
228
    }
229
230
    /**
231
     * Fetches the next key-value pairs from a result set (first
232
     * column is the key, second column is the value).
233
     *
234
     * By default if no column index is provided:
235
     * - the first CSV column is used to provide the keys
236
     * - the second CSV column is used to provide the value
237
     *
238
     * @param string|int $offset_index The column index to serve as offset
239
     * @param string|int $value_index  The column index to serve as value
240
     *
241
     * @return Generator
242
     */
243 8
    public function fetchPairs($offset_index = 0, $value_index = 1): Generator
244
    {
245 8
        $offset = $this->getColumnIndex($offset_index, __METHOD__.'() expects the offset index value to be a valid string or integer, `%s` given');
246 8
        $value = $this->getColumnIndex($value_index, __METHOD__.'() expects the value index value to be a valid string or integer, `%s` given');
247
248
        $filter = function (array $record) use ($offset): bool {
249 8
            return isset($record[$offset]);
250 8
        };
251
252 8
        $select = function (array $record) use ($offset, $value): array {
253 6
            return [$record[$offset], $record[$value] ?? null];
254 8
        };
255
256 8
        $iterator = new MapIterator(new CallbackFilterIterator($this->records, $filter), $select);
257 8
        foreach ($iterator as $pair) {
258 6
            yield $pair[0] => $pair[1];
259
        }
260 8
    }
261
}
262