Completed
Pull Request — master (#178)
by ignace nyamagana
02:46
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 (empty($this->sort_by)) {
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 Iterator $iterator
254
     * @param string   $input_encoding
255
     *
256
     * @return Iterator
257
     */
258
    protected function convertToUtf8(Iterator $iterator, $input_encoding)
259
    {
260
        $convert_row = function ($row) use ($input_encoding) {
261
            return $this->convertRecord($row, $input_encoding);
262
        };
263
264
        return new MapIterator($iterator, $convert_row);
265
    }
266
267
    /**
268
     * Apply CSV header to the return iterator
269
     *
270
     * @param Iterator $iterator
271
     * @param array    $header
272
     *
273
     * @return Iterator
274
     */
275
    protected function applyHeader(Iterator $iterator, array $header)
276
    {
277
        if (empty($header)) {
278
            return $iterator;
279
        }
280
281
        $count = count($header);
282
        $combine_array = function (array $row) use ($header, $count) {
283
            if ($count != count($row)) {
284
                $row = array_slice(array_pad($row, $count, null), 0, $count);
285
            }
286
287
            return array_combine($header, $row);
288
        };
289
290
        return new MapIterator($iterator, $combine_array);
291
    }
292
}
293