Completed
Push — master ( ca4c7f...3d35e2 )
by ignace nyamagana
02:32
created

QueryFilter   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 323
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 18
Bugs 3 Features 0
Metric Value
wmc 31
c 18
b 3
f 0
lcom 1
cbo 1
dl 0
loc 323
ccs 87
cts 87
cp 1
rs 9.8

18 Methods

Rating   Name   Duplication   Size   Complexity  
A setReturnType() 0 10 2
A stripBom() 0 6 1
A isBomStrippable() 0 6 2
getInputBom() 0 1 ?
A setOffset() 0 6 1
A setLimit() 0 6 1
A addSortBy() 0 6 1
validateInteger() 0 1 ?
A addFilter() 0 6 1
A getStripBomIterator() 0 19 4
getEnclosure() 0 1 ?
A getQueryIterator() 0 16 2
getIterator() 0 1 ?
A applyBomStripping() 0 16 3
A applyIteratorFilter() 0 9 2
A applyIteratorSortBy() 0 21 4
A applyIteratorInterval() 0 12 3
A applyReturnType() 0 8 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 8.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
namespace League\Csv\Modifier;
14
15
use ArrayObject;
16
use CallbackFilterIterator;
17
use Iterator;
18
use League\Csv\AbstractCsv;
19
use LimitIterator;
20
use UnexpectedValueException;
21
22
/**
23
 *  A Trait to Query rows against a SplFileObject
24
 *
25
 * @package League.csv
26
 * @since  4.2.1
27
 *
28
 */
29
trait QueryFilter
30
{
31
    /**
32
     * Callables to filter the iterator
33
     *
34
     * @var callable[]
35
     */
36
    protected $iterator_filters = [];
37
38
    /**
39
     * Callables to sort the iterator
40
     *
41
     * @var callable[]
42
     */
43
    protected $iterator_sort_by = [];
44
45
    /**
46
     * iterator Offset
47
     *
48
     * @var int
49
     */
50
    protected $iterator_offset = 0;
51
52
    /**
53
     * iterator maximum length
54
     *
55
     * @var int
56
     */
57
    protected $iterator_limit = -1;
58
59
    /**
60
     * Stripping BOM status
61
     *
62
     * @var boolean
63
     */
64
    protected $strip_bom = false;
65
66
    /**
67
     * Reader return type
68
     *
69
     * @var int
70
     */
71
    protected $returnType = AbstractCsv::TYPE_ARRAY;
72
73
    /**
74
     * Set the return type for the next fetch call
75
     *
76
     * @param int $type
77
     *
78
     * @throws UnexpectedValueException If the value is not one of the defined constant
79
     *
80
     * @return static
81
     */
82 36
    public function setReturnType($type)
83
    {
84 36
        $modes = [AbstractCsv::TYPE_ARRAY => 1, AbstractCsv::TYPE_ITERATOR => 1];
85 36
        if (!isset($modes[$type])) {
86 3
            throw new UnexpectedValueException('Unknown return type');
87
        }
88 33
        $this->returnType = $type;
89
90 33
        return $this;
91
    }
92
93
    /**
94
     * Stripping BOM setter
95
     *
96
     * @param bool $status
97
     *
98
     * @return $this
99
     */
100 24
    public function stripBom($status)
101
    {
102 24
        $this->strip_bom = (bool) $status;
103
104 24
        return $this;
105
    }
106
107
    /**
108
     * Tell whether we can strip or not the leading BOM sequence
109
     *
110
     * @return bool
111
     */
112 27
    protected function isBomStrippable()
113
    {
114 27
        $bom = $this->getInputBom();
115
116 27
        return !empty($bom) && $this->strip_bom;
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122
    abstract public function getInputBom();
123
124
    /**
125
     * Set LimitIterator Offset
126
     *
127
     * @param $offset
128
     *
129
     * @return $this
130
     */
131 33
    public function setOffset($offset = 0)
132
    {
133 33
        $this->iterator_offset = $this->validateInteger($offset, 0, 'the offset must be a positive integer or 0');
134
135 30
        return $this;
136
    }
137
138
    /**
139
     * @inheritdoc
140
     */
141
    abstract protected function validateInteger($int, $minValue, $errorMessage);
142
143
    /**
144
     * Set LimitIterator Count
145
     *
146
     * @param int $limit
147
     *
148
     * @return $this
149
     */
150 30
    public function setLimit($limit = -1)
151
    {
152 30
        $this->iterator_limit = $this->validateInteger($limit, -1, 'the limit must an integer greater or equals to -1');
153
154 27
        return $this;
155
    }
156
157
    /**
158
     * Set an Iterator sorting callable function
159
     *
160
     * @param callable $callable
161
     *
162
     * @return $this
163
     */
164 3
    public function addSortBy(callable $callable)
165
    {
166 3
        $this->iterator_sort_by[] = $callable;
167
168 3
        return $this;
169
    }
170
171
    /**
172
     * Set the Iterator filter method
173
     *
174
     * @param callable $callable
175
     *
176
     * @return $this
177
     */
178 66
    public function addFilter(callable $callable)
179
    {
180 66
        $this->iterator_filters[] = $callable;
181
182 66
        return $this;
183
    }
184
185
    /**
186
     * Return the Iterator without the BOM sequence
187
     *
188
     * @param Iterator $iterator
189
     *
190
     * @return Iterator
191
     */
192 21
    protected function getStripBomIterator(Iterator $iterator)
193
    {
194 21
        $bom = $this->getInputBom();
195
196
        $stripBom = function ($row, $index) use ($bom) {
197 21
            if (0 == $index) {
198 21
                $row[0] = mb_substr($row[0], mb_strlen($bom));
199 21
                $enclosure = $this->getEnclosure();
200
                //enclosure should be remove when a BOM sequence is stripped
201 21
                if ($row[0][0] === $enclosure && mb_substr($row[0], -1, 1) == $enclosure) {
202 12
                    $row[0] = mb_substr($row[0], 1, -1);
203 8
                }
204 14
            }
205
206 21
            return $row;
207 21
        };
208
209 21
        return new MapIterator($iterator, $stripBom);
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215
    abstract public function getEnclosure();
216
217
    /**
218
     * Returns the CSV Iterator
219
     *
220
     * @return Iterator
221
     */
222 174
    protected function getQueryIterator()
223
    {
224
        $normalizedCsv = function ($row) {
225 171
            return is_array($row) && $row != [null];
226 174
        };
227 174
        array_unshift($this->iterator_filters, $normalizedCsv);
228 174
        $iterator = $this->getIterator();
229 174
        $iterator = $this->applyBomStripping($iterator);
230 174
        $iterator = $this->applyIteratorFilter($iterator);
231 174
        $iterator = $this->applyIteratorSortBy($iterator);
232 174
        $iterator = $this->applyIteratorInterval($iterator);
233
234 174
        $this->returnType = AbstractCsv::TYPE_ARRAY;
235
236 174
        return $iterator;
237
    }
238
239
    /**
240
     * {@inheritdoc}
241
     */
242
    abstract public function getIterator();
243
244
    /**
245
     * Remove the BOM sequence from the CSV
246
     *
247
     * @param Iterator $iterator
248
     *
249
     * @return Iterator
250
     */
251 174
    protected function applyBomStripping(Iterator $iterator)
252
    {
253 174
        if (!$this->strip_bom) {
254 150
            return $iterator;
255
        }
256
257 24
        if (!$this->isBomStrippable()) {
258 3
            $this->strip_bom = false;
259
260 3
            return $iterator;
261
        }
262
263 21
        $this->strip_bom = false;
264
265 21
        return $this->getStripBomIterator($iterator);
266
    }
267
268
    /**
269
    * Filter the Iterator
270
    *
271
    * @param Iterator $iterator
272
    *
273
    * @return Iterator
274
    */
275 174
    protected function applyIteratorFilter(Iterator $iterator)
276
    {
277 174
        foreach ($this->iterator_filters as $callable) {
278 174
            $iterator = new CallbackFilterIterator($iterator, $callable);
279 116
        }
280 174
        $this->iterator_filters = [];
281
282 174
        return $iterator;
283
    }
284
285
    /**
286
    * Sort the Iterator
287
    *
288
    * @param Iterator $iterator
289
    *
290
    * @return Iterator
291
    */
292 174
    protected function applyIteratorSortBy(Iterator $iterator)
293
    {
294 174
        if (!$this->iterator_sort_by) {
295 171
            return $iterator;
296
        }
297
298 3
        $obj = new ArrayObject(iterator_to_array($iterator));
299 3
        $obj->uasort(function ($rowA, $rowB) {
300 3
            $sortRes = 0;
301 3
            foreach ($this->iterator_sort_by as $callable) {
302 3
                if (0 !== ($sortRes = call_user_func($callable, $rowA, $rowB))) {
303 3
                    break;
304
                }
305 2
            }
306
307 3
            return $sortRes;
308 3
        });
309 3
        $this->iterator_sort_by = [];
310
311 3
        return $obj->getIterator();
312
    }
313
314
    /**
315
    * Sort the Iterator
316
    *
317
    * @param Iterator $iterator
318
    *
319
    * @return Iterator
320
    */
321 174
    protected function applyIteratorInterval(Iterator $iterator)
322
    {
323 174
        if (0 == $this->iterator_offset && -1 == $this->iterator_limit) {
324 141
            return $iterator;
325
        }
326 33
        $offset = $this->iterator_offset;
327 33
        $limit  = $this->iterator_limit;
328 33
        $this->iterator_limit  = -1;
329 33
        $this->iterator_offset = 0;
330
331 33
        return new LimitIterator($iterator, $offset, $limit);
332
    }
333
334
    /**
335
     * Convert the Iterator into an array depending on the selected return type
336
     *
337
     * @param int      $type
338
     * @param Iterator $iterator
339
     * @param bool     $use_keys Whether to use the iterator element keys as index
340
     *
341
     * @return Iterator|array
342
     */
343 135
    protected function applyReturnType($type, Iterator $iterator, $use_keys = true)
344
    {
345 135
        if (AbstractCsv::TYPE_ARRAY == $type) {
346 120
            return iterator_to_array($iterator, $use_keys);
347
        }
348
349 21
        return $iterator;
350
    }
351
}
352