Completed
Push — master ( ecd0f8...3ad52b )
by ignace nyamagana
15:08
created

ResultSet::getRecords()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 6
ccs 2
cts 2
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * League.Csv (https://csv.thephpleague.com).
5
 *
6
 * @author  Ignace Nyamagana Butera <[email protected]>
7
 * @license https://github.com/thephpleague/csv/blob/master/LICENSE (MIT License)
8
 * @version 9.1.5
9
 * @link    https://github.com/thephpleague/csv
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
declare(strict_types=1);
16
17
namespace League\Csv;
18
19
use CallbackFilterIterator;
20
use Countable;
21
use Generator;
22
use Iterator;
23
use IteratorAggregate;
24
use JsonSerializable;
25
use LimitIterator;
26
use function array_flip;
27
use function array_search;
28
use function is_string;
29
use function iterator_count;
30
use function iterator_to_array;
31
use function sprintf;
32
33
/**
34
 * Represents the result set of a {@link Reader} processed by a {@link Statement}.
35
 *
36
 * @package League.csv
37
 * @since   9.0.0
38
 * @author  Ignace Nyamagana Butera <[email protected]>
39
 */
40
class ResultSet implements Countable, IteratorAggregate, JsonSerializable
41
{
42
    /**
43
     * The CSV records collection.
44
     *
45
     * @var Iterator
46
     */
47
    protected $records;
48
49
    /**
50
     * The CSV records collection header.
51
     *
52
     * @var array
53
     */
54 8
    protected $header = [];
55
56 8
    /**
57 8
     * New instance.
58 8
     *
59
     * @param Iterator $records a CSV records collection iterator
60
     * @param array    $header  the associated collection column names
61
     */
62
    public function __construct(Iterator $records, array $header)
63 10
    {
64
        $this->records = $records;
65 10
        $this->header = $header;
66 10
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71
    public function __destruct()
72
    {
73 2
        unset($this->records);
74
    }
75 2
76
    /**
77
     * Returns the header associated with the result set.
78
     *
79
     * @return string[]
80
     */
81 8
    public function getHeader(): array
82
    {
83 8
        return $this->header;
84 4
    }
85
86 8
    /**
87
     * {@inheritdoc}
88
     */
89
    public function getRecords(): Generator
90
    {
91 12
        foreach ($this->records as $offset => $value) {
92
            yield $offset => $value;
93 12
        }
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99 2
    public function getIterator(): Generator
100
    {
101 2
        return $this->getRecords();
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107 2
    public function count(): int
108
    {
109 2
        return iterator_count($this->records);
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115
    public function jsonSerialize(): array
116
    {
117
        return iterator_to_array($this->records, false);
118
    }
119
120
    /**
121
     * Returns the nth record from the result set.
122
     *
123 4
     * By default if no index is provided the first record of the resultet is returned
124
     *
125 4
     * @param int $nth_record the CSV record offset
126 2
     *
127
     * @throws Exception if argument is lesser than 0
128
     */
129 2
    public function fetchOne(int $nth_record = 0): array
130 2
    {
131
        if ($nth_record < 0) {
132 2
            throw new Exception(sprintf('%s() expects the submitted offset to be a positive integer or 0, %s given', __METHOD__, $nth_record));
133
        }
134
135
        $iterator = new LimitIterator($this->records, $nth_record, 1);
136
        $iterator->rewind();
137
138
        return (array) $iterator->current();
139
    }
140
141
    /**
142
     * Returns a single column from the next record of the result set.
143
     *
144 14
     * By default if no value is supplied the first column is fetch
145
     *
146 14
     * @param string|int $index CSV column index
147 8
     */
148 8
    public function fetchColumn($index = 0): Generator
149 8
    {
150
        $offset = $this->getColumnIndex($index, __METHOD__.'() expects the column index to be a valid string or integer, `%s` given');
151 8
        $filter = function (array $record) use ($offset): bool {
152 6
            return isset($record[$offset]);
153 8
        };
154
155 8
        $select = function (array $record) use ($offset): string {
156 8
            return $record[$offset];
157 6
        };
158
159 4
        $iterator = new MapIterator(new CallbackFilterIterator($this->records, $filter), $select);
160
        foreach ($iterator as $offset => $value) {
161
            yield $offset => $value;
162
        }
163
    }
164
165
    /**
166
     * Filter a column name against the header if any.
167
     *
168
     * @param string|int $field         the field name or the field index
169 20
     * @param string     $error_message the associated error message
170
     *
171 20
     * @return string|int
172
     */
173 20
    protected function getColumnIndex($field, string $error_message)
174
    {
175
        $method = is_string($field) ? 'getColumnIndexByValue' : 'getColumnIndexByKey';
176
177
        return $this->$method($field, $error_message);
178
    }
179
180
    /**
181
     * Returns the selected column name.
182
     *
183
     * @throws Exception if the column is not found
184
     */
185
    protected function getColumnIndexByValue(string $value, string $error_message): string
186 4
    {
187
        if (false !== array_search($value, $this->header, true)) {
188 4
            return $value;
189 2
        }
190
191
        throw new Exception(sprintf($error_message, $value));
192 2
    }
193
194
    /**
195
     * Returns the selected column name according to its offset.
196
     *
197
     * @throws Exception if the field is invalid or not found
198
     *
199
     * @return int|string
200
     */
201
    protected function getColumnIndexByKey(int $index, string $error_message)
202
    {
203
        if ($index < 0) {
204
            throw new Exception($error_message);
205 12
        }
206
207 12
        if ([] === $this->header) {
208 2
            return $index;
209
        }
210
211 10
        $value = array_search($index, array_flip($this->header), true);
212 6
        if (false !== $value) {
213
            return $value;
214
        }
215 4
216 4
        throw new Exception(sprintf($error_message, $index));
217 2
    }
218
219
    /**
220 2
     * Returns the next key-value pairs from a result set (first
221
     * column is the key, second column is the value).
222
     *
223
     * By default if no column index is provided:
224
     * - the first column is used to provide the keys
225
     * - the second column is used to provide the value
226
     *
227
     * @param string|int $offset_index The column index to serve as offset
228
     * @param string|int $value_index  The column index to serve as value
229
     */
230
    public function fetchPairs($offset_index = 0, $value_index = 1): Generator
231
    {
232
        $offset = $this->getColumnIndex($offset_index, __METHOD__.'() expects the offset index value to be a valid string or integer, `%s` given');
233
        $value = $this->getColumnIndex($value_index, __METHOD__.'() expects the value index value to be a valid string or integer, `%s` given');
234
235
        $filter = function (array $record) use ($offset): bool {
236 8
            return isset($record[$offset]);
237
        };
238 8
239 8
        $select = function (array $record) use ($offset, $value): array {
240
            return [$record[$offset], $record[$value] ?? null];
241 8
        };
242 8
243 8
        $iterator = new MapIterator(new CallbackFilterIterator($this->records, $filter), $select);
244
        foreach ($iterator as $pair) {
245 8
            yield $pair[0] => $pair[1];
246 6
        }
247 8
    }
248
}
249