Completed
Pull Request — master (#210)
by ignace nyamagana
04:08
created

Statement   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 243
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 4
dl 0
loc 243
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A offset() 0 6 1
A limit() 0 6 1
A orderBy() 0 6 1
A where() 0 6 1
A headers() 0 6 1
A process() 0 19 1
A stripBOM() 0 17 4
A setRecordsHeader() 0 15 3
A addRecordsHeader() 0 18 3
A filterRecords() 0 13 2
A orderRecords() 0 20 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.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 League\Csv\Config\ValidatorTrait;
21
use LimitIterator;
22
use SplFileObject;
23
24
/**
25
 *  A trait to manage filtering a CSV
26
 *
27
 * @package League.csv
28
 * @since  9.0.0
29
 *
30
 */
31
class Statement
32
{
33
    use ValidatorTrait;
34
35
    /**
36
     * Callables to filter the iterator
37
     *
38
     * @var callable[]
39
     */
40
    protected $where = [];
41
42
    /**
43
     * Callables to sort the iterator
44
     *
45
     * @var callable[]
46
     */
47
    protected $filter = [];
48
49
    /**
50
     * iterator Offset
51
     *
52
     * @var int
53
     */
54
    protected $offset = 0;
55
56
    /**
57
     * iterator maximum length
58
     *
59
     * @var int
60
     */
61
    protected $limit = -1;
62
63
    /**
64
     * CSV headers
65
     *
66
     * @var string[]
67
     */
68
    protected $headers = [];
69
70
    /**
71
     * Set LimitIterator Offset
72
     *
73
     * @param $offset
74
     *
75
     * @return $this
76
     */
77
    public function offset(int $offset = 0): self
78
    {
79
        $this->offset = $this->filterInteger($offset, 0, 'the offset must be a positive integer or 0');
80
81
        return $this;
82
    }
83
84
    /**
85
     * Set LimitIterator Count
86
     *
87
     * @param int $limit
88
     *
89
     * @return $this
90
     */
91
    public function limit(int $limit = -1): self
92
    {
93
        $this->limit = $this->filterInteger($limit, -1, 'the limit must an integer greater or equals to -1');
94
95
        return $this;
96
    }
97
98
    /**
99
     * Set an Iterator sorting callable function
100
     *
101
     * @param callable $callable
102
     *
103
     * @return $this
104
     */
105
    public function orderBy(callable $callable): self
106
    {
107
        $this->order_by[] = $callable;
0 ignored issues
show
Bug introduced by
The property order_by does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
108
109
        return $this;
110
    }
111
112
    /**
113
     * Set the Iterator filter method
114
     *
115
     * @param callable $callable
116
     *
117
     * @return $this
118
     */
119
    public function where(callable $callable): self
120
    {
121
        $this->where[] = $callable;
122
123
        return $this;
124
    }
125
126
    public function headers(array $headers)
127
    {
128
        $this->headers = $this->filterHeader($headers);
129
130
        return $this;
131
    }
132
133
    /**
134
     * Returns the inner CSV Document Iterator object
135
     *
136
     * @return Iterator
137
     */
138
    public function process(Reader $reader)
139
    {
140
        $bom = $reader->getInputBOM();
141
        $enclosure = $reader->getEnclosure();
142
        $this->setRecordsHeader($reader);
143
144
        $csv = $reader->getDocument();
145
        $csv->setFlags(SplFileObject::READ_AHEAD | SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY);
146
147
        $iterator = $this->stripBOM($csv, $bom, $enclosure);
148
        $iterator = $this->addRecordsHeader($iterator);
149
        $iterator = $this->filterRecords($iterator);
150
        $iterator = $this->orderRecords($iterator);
151
152
        return new RecordSet(
153
            new LimitIterator($iterator, $this->offset, $this->limit),
154
            $this->headers
155
        );
156
    }
157
158
    /**
159
     * Remove the BOM sequence from the CSV
160
     *
161
     * @param Iterator $iterator
162
     *
163
     * @return Iterator
164
     */
165
    protected function stripBOM(Iterator $iterator, string $bom, string $enclosure): Iterator
166
    {
167
        if ('' == $bom) {
168
            return $iterator;
169
        }
170
171
        $bom_length = mb_strlen($bom);
172
        $strip_bom = function ($row, $index) use ($bom_length, $enclosure) {
173
            if (0 != $index || !is_array($row)) {
174
                return $row;
175
            }
176
177
            return $this->removeBOM($row, $bom_length, $enclosure);
178
        };
179
180
        return new MapIterator($iterator, $strip_bom);
181
    }
182
183
    protected function setRecordsHeader(Reader $reader)
184
    {
185
        if (!empty($this->headers)) {
186
            return;
187
        }
188
        $this->headers = $reader->getHeader();
189
190
        $header_offset = $reader->getHeaderOffset();
191
        if (null !== $header_offset) {
192
            $strip_header = function ($row, $index) use ($header_offset) {
193
                return $index !== $header_offset;
194
            };
195
            array_unshift($this->where, $strip_header);
196
        }
197
    }
198
199
    /**
200
     * Add the CSV header if present
201
     *
202
     * @param Iterator $iterator
203
     *
204
     * @return Iterator
205
     */
206
    public function addRecordsHeader(Iterator $iterator, array $headers = []): Iterator
0 ignored issues
show
Unused Code introduced by
The parameter $headers is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
207
    {
208
        $header = $this->headers;
209
        if (empty($header)) {
210
            return $iterator;
211
        }
212
213
        $header_count = count($header);
214
        $combine = function (array $row) use ($header, $header_count) {
215
            if ($header_count != count($row)) {
216
                $row = array_slice(array_pad($row, $header_count, null), 0, $header_count);
217
            }
218
219
            return array_combine($header, $row);
220
        };
221
222
        return new MapIterator($iterator, $combine);
223
    }
224
225
    /**
226
    * Filter the Iterator
227
    *
228
    * @param Iterator $iterator
229
    *
230
    * @return Iterator
231
    */
232
    protected function filterRecords(Iterator $iterator): Iterator
233
    {
234
        $normalized_csv = function ($row) {
235
            return is_array($row) && $row != [null];
236
        };
237
        array_unshift($this->where, $normalized_csv);
238
239
        $reducer = function ($iterator, $callable) {
240
            return new CallbackFilterIterator($iterator, $callable);
241
        };
242
243
        return array_reduce($this->where, $reducer, $iterator);
244
    }
245
246
    /**
247
    * Sort the Iterator
248
    *
249
    * @param Iterator $iterator
250
    *
251
    * @return Iterator
252
    */
253
    protected function orderRecords(Iterator $iterator): Iterator
254
    {
255
        if (empty($this->order_by)) {
256
            return $iterator;
257
        }
258
259
        $obj = new ArrayIterator(iterator_to_array($iterator));
260
        $obj->uasort(function ($row_a, $row_b) {
261
            $res = 0;
262
            foreach ($this->order_by as $compare) {
263
                if (0 !== ($res = $compare($row_a, $row_b))) {
264
                    break;
265
                }
266
            }
267
268
            return $res;
269
        });
270
271
        return $obj;
272
    }
273
}
274