ResultSet   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 220
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 220
ccs 65
cts 65
cp 1
rs 10
c 0
b 0
f 0
wmc 22

13 Methods

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