Completed
Pull Request — master (#210)
by ignace nyamagana
02:20
created

Statement::offset()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 1
crap 2
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 ArrayIterator;
18
use CallbackFilterIterator;
19
use Iterator;
20
use LimitIterator;
21
22
/**
23
 *  A trait to manage filtering a CSV
24
 *
25
 * @package League.csv
26
 * @since   9.0.0
27
 * @author  Ignace Nyamagana Butera <[email protected]>
28
 *
29
 */
30
class Statement
31
{
32
    use ValidatorTrait;
33
34
    /**
35
     * Callables to filter the iterator
36
     *
37
     * @var callable[]
38
     */
39
    protected $where = [];
40
41
    /**
42
     * Callables to sort the iterator
43
     *
44
     * @var callable[]
45
     */
46
    protected $order_by = [];
47
48
    /**
49
     * iterator Offset
50
     *
51
     * @var int
52
     */
53
    protected $offset = 0;
54
55
    /**
56
     * iterator maximum length
57
     *
58
     * @var int
59
     */
60
    protected $limit = -1;
61
62
    /**
63
     * CSV headers
64
     *
65
     * @var string[]
66
     */
67
    protected $header = [];
68
69
    /**
70
     * Set LimitIterator Offset
71
     *
72
     * @param $offset
73
     *
74
     * @return self
75
     */
76 12
    public function offset(int $offset = 0): self
77
    {
78 12
        $offset = $this->filterInteger($offset, 0, 'the offset must be a positive integer or 0');
79 12
        if ($offset === $this->offset) {
80 2
            return $this;
81
        }
82
83 10
        $clone = clone $this;
84 10
        $clone->offset = $offset;
85
86 10
        return $clone;
87
    }
88
89
    /**
90
     * Set LimitIterator Count
91
     *
92
     * @param int $limit
93
     *
94
     * @return self
95
     */
96 16
    public function limit(int $limit = -1): self
97
    {
98 16
        $limit = $this->filterInteger($limit, -1, 'the limit must an integer greater or equals to -1');
99 14
        if ($limit === $this->limit) {
100 2
            return $this;
101
        }
102
103 12
        $clone = clone $this;
104 12
        $clone->limit = $limit;
105
106 12
        return $clone;
107
    }
108
109
    /**
110
     * Set an Iterator sorting callable function
111
     *
112
     * @param callable $callable
113
     *
114
     * @return self
115
     */
116 2
    public function orderBy(callable $callable): self
117
    {
118 2
        $clone = clone $this;
119 2
        $clone->order_by[] = $callable;
120
121 2
        return $clone;
122
    }
123
124
    /**
125
     * Set the Iterator filter method
126
     *
127
     * @param callable $callable
128
     *
129
     * @return self
130
     */
131 2
    public function where(callable $callable): self
132
    {
133 2
        $clone = clone $this;
134 2
        $clone->where[] = $callable;
135
136 2
        return $clone;
137
    }
138
139
    /**
140
     * Set the headers to be used by the RecordSet object
141
     *
142
     * @param string[] $header
143
     *
144
     * @return self
145
     */
146 18
    public function header(array $header): self
147
    {
148 18
        $header = $this->filterHeader($header);
149 16
        if ($header === $this->header) {
150 2
            return $this;
151
        }
152
153 14
        $clone = clone $this;
154 14
        $clone->header = $header;
155
156 14
        return $clone;
157
    }
158
159
    /**
160
     * Returns the inner CSV Document Iterator object
161
     *
162
     * @return RecordSet
163
     */
164 108
    public function process(Reader $reader): RecordSet
165
    {
166 108
        $header = $this->header;
167 108
        if (empty($header) && null !== $reader->getHeaderOffset()) {
168 16
            $header = $this->filterHeader($reader->getHeader());
169
        }
170
171 106
        $iterator = $this->combineHeader($reader->getIterator(), $header);
0 ignored issues
show
Compatibility introduced by
$reader->getIterator() of type object<Traversable> is not a sub-type of object<Iterator>. It seems like you assume a child interface of the interface Traversable to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
172 106
        $iterator = $this->filterRecords($iterator);
173 106
        $iterator = $this->orderRecords($iterator);
174
175 106
        return new RecordSet(new LimitIterator($iterator, $this->offset, $this->limit), $header);
176
    }
177
178
    /**
179
     * Add the CSV header if present
180
     *
181
     * @param Iterator $iterator
182
     * @param string[] $header
183
     *
184
     * @return Iterator
185
     */
186 106
    protected function combineHeader(Iterator $iterator, array $header): Iterator
187
    {
188 106
        if (empty($header)) {
189 78
            return $iterator;
190
        }
191
192 28
        $header_count = count($header);
193
        $combine = function (array $row) use ($header_count, $header) {
194 22
            if ($header_count != count($row)) {
195 4
                $row = array_slice(array_pad($row, $header_count, null), 0, $header_count);
196
            }
197
198 22
            return array_combine($header, $row);
199 28
        };
200
201 28
        return new MapIterator($iterator, $combine);
202
    }
203
204
    /**
205
    * Filter the Iterator
206
    *
207
    * @param Iterator $iterator
208
    *
209
    * @return Iterator
210
    */
211 106
    protected function filterRecords(Iterator $iterator): Iterator
212
    {
213
        $reducer = function ($iterator, $callable) {
214 2
            return new CallbackFilterIterator($iterator, $callable);
215 106
        };
216
217 106
        return array_reduce($this->where, $reducer, $iterator);
218
    }
219
220
    /**
221
    * Sort the Iterator
222
    *
223
    * @param Iterator $iterator
224
    *
225
    * @return Iterator
226
    */
227 106
    protected function orderRecords(Iterator $iterator): Iterator
228
    {
229 106
        if (empty($this->order_by)) {
230 104
            return $iterator;
231
        }
232
233 2
        $obj = new ArrayIterator(iterator_to_array($iterator));
234 2
        $obj->uasort(function ($row_a, $row_b) {
235 2
            $res = 0;
236 2
            foreach ($this->order_by as $compare) {
237 2
                if (0 !== ($res = $compare($row_a, $row_b))) {
238 2
                    break;
239
                }
240
            }
241
242 2
            return $res;
243 2
        });
244
245 2
        return $obj;
246
    }
247
}
248