Completed
Push — master ( a547b3...f4a55c )
by ignace nyamagana
04:29 queued 02:29
created

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