Completed
Pull Request — master (#178)
by ignace nyamagana
04:41
created

Statement::addSortBy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
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->applyFilter($iterator, $filters);
97
        $iterator = $this->applySortBy($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($iterator, $header);
104
    }
105
106
    /**
107
     * Remove the BOM sequence from the CSV
108
     *
109
     * @param AbstractCsv $csv
110
     *
111
     * @return Iterator
112
     */
113
    protected function applyBOMStripping(AbstractCsv $csv)
114
    {
115
        $bom = $csv->getInputBOM();
116
        if ('' === $bom) {
117
            return $csv->getIterator();
118
        }
119
120
        $enclosure = $csv->getEnclosure();
121
        $strip_bom = function ($row, $index) use ($bom, $enclosure) {
122
            if (0 != $index) {
123
                return $row;
124
            }
125
126
            return $this->stripBOM($row, $bom, $enclosure);
127
        };
128
129
        return new MapIterator($csv->getIterator(), $strip_bom);
130
    }
131
132
    /**
133
    * Filter the Iterator
134
    *
135
    * @param Iterator $iterator
136
    *
137
    * @return Iterator
138
    */
139
    protected function applyFilter(Iterator $iterator, array $filters)
140
    {
141
        $reducer = function ($iterator, $callable) {
142
            return new CallbackFilterIterator($iterator, $callable);
143
        };
144
145
        $filters[] = function ($row) {
146
            return is_array($row) && $row != [null];
147
        };
148
149
        return array_reduce(array_merge($filters, $this->filters), $reducer, $iterator);
150
    }
151
152
    /**
153
    * Sort the Iterator
154
    *
155
    * @param Iterator $iterator
156
    *
157
    * @return Iterator
158
    */
159
    protected function applySortBy(Iterator $iterator)
160
    {
161
        if (empty($this->sort_by)) {
162
            return $iterator;
163
        }
164
165
        $obj = new ArrayIterator(iterator_to_array($iterator));
166
        $obj->uasort(function ($row_a, $row_b) {
167
            $res = 0;
168
            foreach ($this->sort_by as $compare) {
169
                if (0 !== ($res = call_user_func($compare, $row_a, $row_b))) {
170
                    break;
171
                }
172
            }
173
174
            return $res;
175
        });
176
177
        return $obj;
178
    }
179
180
    /**
181
     * Convert Iterator to UTF-8 if needed
182
     *
183
     * @param Iterator $iterator
184
     * @param string   $input_encoding
185
     *
186
     * @return Iterator
187
     */
188
    protected function convertToUtf8(Iterator $iterator, $input_encoding)
189
    {
190
        $convert_row = function ($row) use ($input_encoding) {
191
            return $this->convertRecord($row, $input_encoding);
192
        };
193
194
        return new MapIterator($iterator, $convert_row);
195
    }
196
197
    /**
198
     * Set LimitIterator Offset
199
     *
200
     * @param $offset
201
     *
202
     * @return static
203
     */
204
    public function setOffset($offset)
205
    {
206
        $offset = $this->validateInteger($offset, 0, 'the offset must be a positive integer or 0');
207
        if ($offset === $this->offset) {
208
            return $this;
209
        }
210
211
        $clone = clone $this;
212
        $clone->offset = $offset;
213
214
        return $clone;
215
    }
216
217
    /**
218
     * Set LimitIterator Count
219
     *
220
     * @param int $limit
221
     *
222
     * @return static
223
     */
224
    public function setLimit($limit)
225
    {
226
        $limit = $this->validateInteger($limit, -1, 'the limit must an integer greater or equals to -1');
227
        if ($limit === $this->limit) {
228
            return $this;
229
        }
230
231
        $clone = clone $this;
232
        $clone->limit = $limit;
233
234
        return $clone;
235
    }
236
237
    /**
238
     * Set an Iterator sorting callable function
239
     *
240
     * @param callable $callable
241
     *
242
     * @return static
243
     */
244
    public function addSortBy(callable $callable)
245
    {
246
        $clone = clone $this;
247
        $clone->sort_by[] = $callable;
248
249
        return $clone;
250
    }
251
252
    /**
253
     * Set the Iterator filter method
254
     *
255
     * @param callable $callable
256
     *
257
     * @return static
258
     */
259
    public function addFilter(callable $callable)
260
    {
261
        $clone = clone $this;
262
        $clone->filters[] = $callable;
263
264
        return $clone;
265
    }
266
}
267