Completed
Pull Request — master (#178)
by ignace nyamagana
02:35
created

Statement::process()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 23
rs 9.0856
cc 3
eloc 16
nc 4
nop 1
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.1.1
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;
14
15
use ArrayIterator;
16
use CallbackFilterIterator;
17
use InvalidArgumentException;
18
use Iterator;
19
use League\Csv\Config\Validator;
20
use LimitIterator;
21
22
/**
23
 * A simple Statement class to fetch rows against a Csv file object
24
 *
25
 * @package League.csv
26
 * @since  4.2.1
27
 *
28
 */
29
class Statement
30
{
31
    use Validator;
32
33
    /**
34
     * Callables to filter the iterator
35
     *
36
     * @var callable[]
37
     */
38
    protected $filters = [];
39
40
    /**
41
     * Callables to sort the iterator
42
     *
43
     * @var callable[]
44
     */
45
    protected $sort_by = [];
46
47
    /**
48
     * iterator Offset
49
     *
50
     * @var int
51
     */
52
    protected $offset = 0;
53
54
    /**
55
     * iterator maximum length
56
     *
57
     * @var int
58
     */
59
    protected $limit = -1;
60
61
    /**
62
     * @inheritdoc
63
     */
64
    public function __set($property, $value)
65
    {
66
        throw new InvalidArgumentException(sprintf('%s is an undefined property', $property));
67
    }
68
69
    /**
70
     * @inheritdoc
71
     */
72
    public function __unset($property)
73
    {
74
        throw new InvalidArgumentException(sprintf('%s is an undefined property', $property));
75
    }
76
77
    /**
78
     * Returns a Record object
79
     *
80
     * @return RecordSet
81
     */
82
    public function process(AbstractCsv $csv)
83
    {
84
        $input_encoding = $csv->getInputEncoding();
85
        $use_converter = $this->useInternalConverter($csv);
86
        $header = $csv->getHeader();
87
        $header_offset = $csv->getHeaderOffset();
88
        $filters = [];
89
        if (null !== $header_offset) {
90
            $filters[] = function ($row, $index) use ($header_offset) {
91
                return $index !== $header_offset;
92
            };
93
        }
94
95
        $iterator = $this->applyBOMStripping($csv);
96
        $iterator = $this->applyIteratorFilter($iterator, $filters);
97
        $iterator = $this->applyIteratorSortBy($iterator);
98
        $iterator = new LimitIterator($iterator, $this->offset, $this->limit);
99
        if ($use_converter) {
100
            $iterator = $this->convertToUtf8($iterator, $input_encoding);
101
        }
102
103
        return new RecordSet($this->applyHeader($iterator, $header), $header);
104
    }
105
106
    /**
107
     * Set LimitIterator Offset
108
     *
109
     * @param $offset
110
     *
111
     * @return static
112
     */
113
    public function setOffset($offset)
114
    {
115
        $offset = $this->validateInteger($offset, 0, 'the offset must be a positive integer or 0');
116
        if ($offset === $this->offset) {
117
            return $this;
118
        }
119
120
        $clone = clone $this;
121
        $clone->offset = $offset;
122
123
        return $clone;
124
    }
125
126
    /**
127
     * Set LimitIterator Count
128
     *
129
     * @param int $limit
130
     *
131
     * @return static
132
     */
133
    public function setLimit($limit)
134
    {
135
        $limit = $this->validateInteger($limit, -1, 'the limit must an integer greater or equals to -1');
136
        if ($limit === $this->limit) {
137
            return $this;
138
        }
139
140
        $clone = clone $this;
141
        $clone->limit = $limit;
142
143
        return $clone;
144
    }
145
146
    /**
147
     * Set an Iterator sorting callable function
148
     *
149
     * @param callable $callable
150
     *
151
     * @return static
152
     */
153
    public function addSortBy(callable $callable)
154
    {
155
        $clone = clone $this;
156
        $clone->sort_by[] = $callable;
157
158
        return $clone;
159
    }
160
161
    /**
162
     * Set the Iterator filter method
163
     *
164
     * @param callable $callable
165
     *
166
     * @return static
167
     */
168
    public function addFilter(callable $callable)
169
    {
170
        $clone = clone $this;
171
        $clone->filters[] = $callable;
172
173
        return $clone;
174
    }
175
176
    /**
177
     * Remove the BOM sequence from the CSV
178
     *
179
     * @param AbstractCsv $csv
180
     *
181
     * @return Iterator
182
     */
183
    protected function applyBOMStripping(AbstractCsv $csv)
184
    {
185
        $bom = $csv->getInputBOM();
186
        if ('' === $bom) {
187
            return $csv->getIterator();
188
        }
189
190
        $enclosure = $csv->getEnclosure();
191
        $strip_bom = function ($row, $index) use ($bom, $enclosure) {
192
            if (0 != $index) {
193
                return $row;
194
            }
195
196
            return $this->stripBOM($row, $bom, $enclosure);
197
        };
198
199
        return new MapIterator($csv->getIterator(), $strip_bom);
200
    }
201
202
    /**
203
    * Filter the Iterator
204
    *
205
    * @param Iterator $iterator
206
    *
207
    * @return Iterator
208
    */
209
    protected function applyIteratorFilter(Iterator $iterator, array $filters)
210
    {
211
        $reducer = function ($iterator, $callable) {
212
            return new CallbackFilterIterator($iterator, $callable);
213
        };
214
215
        $filters[] = function ($row) {
216
            return is_array($row) && $row != [null];
217
        };
218
219
        return array_reduce(array_merge($filters, $this->filters), $reducer, $iterator);
220
    }
221
222
    /**
223
    * Sort the Iterator
224
    *
225
    * @param Iterator $iterator
226
    *
227
    * @return Iterator
228
    */
229
    protected function applyIteratorSortBy(Iterator $iterator)
230
    {
231
        if (!$this->sort_by) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->sort_by of type callable[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
232
            return $iterator;
233
        }
234
235
        $obj = new ArrayIterator(iterator_to_array($iterator));
236
        $obj->uasort(function ($row_a, $row_b) {
237
            $res = 0;
238
            foreach ($this->sort_by as $compare) {
239
                if (0 !== ($res = call_user_func($compare, $row_a, $row_b))) {
240
                    break;
241
                }
242
            }
243
244
            return $res;
245
        });
246
247
        return $obj;
248
    }
249
250
    /**
251
     * Convert Iterator to UTF-8 if needed
252
     *
253
     * @param string   $input_encoding
254
     * @param Iterator $iterator
255
     * @param bool     $user_converter
0 ignored issues
show
Bug introduced by
There is no parameter named $user_converter. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
256
     *
257
     * @return Iterator
258
     */
259
    protected function convertToUtf8(Iterator $iterator, $input_encoding)
260
    {
261
        $convert_row = function ($row) use ($input_encoding) {
262
            return $this->convertRecord($row, $input_encoding);
263
        };
264
265
        return new MapIterator($iterator, $convert_row);
266
    }
267
268
    /**
269
     * Apply CSV header to the return iterator
270
     *
271
     * @param Iterator $iterator
272
     * @param array    $header
273
     *
274
     * @return Iterator
275
     */
276
    protected function applyHeader(Iterator $iterator, array $header)
277
    {
278
        if (empty($header)) {
279
            return $iterator;
280
        }
281
282
        $count = count($header);
283
        $combine_array = function (array $row) use ($header, $count) {
284
            if ($count != count($row)) {
285
                $row = array_slice(array_pad($row, $count, null), 0, $count);
286
            }
287
288
            return array_combine($header, $row);
289
        };
290
291
        return new MapIterator($iterator, $combine_array);
292
    }
293
}
294