Completed
Pull Request — master (#355)
by Patrick
02:10
created

Statement::buildOrderBy()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 2
nop 1
dl 0
loc 21
ccs 12
cts 12
cp 1
crap 4
rs 9.584
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * League.Csv (https://csv.thephpleague.com)
5
 *
6
 * (c) Ignace Nyamagana Butera <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace League\Csv;
15
16
use ArrayIterator;
17
use CallbackFilterIterator;
18
use Iterator;
19
use LimitIterator;
20
use function array_reduce;
21
use function iterator_to_array;
22
23
/**
24
 * Criteria to filter a {@link Reader} object.
25
 */
26
class Statement
27
{
28
    /**
29
     * Callables to filter the iterator.
30
     *
31
     * @var callable[]
32
     */
33
    protected $where = [];
34
35
    /**
36
     * Callables to sort the iterator.
37
     *
38
     * @var callable[]
39
     */
40
    protected $order_by = [];
41
42
    /**
43
     * iterator Offset.
44
     *
45
     * @var int
46
     */
47
    protected $offset = 0;
48
49
    /**
50
     * iterator maximum length.
51
     *
52
     * @var int
53
     */
54
    protected $limit = -1;
55
56
    /**
57
     * Set the Iterator filter method.
58
     */
59 18
    public function where(callable $callable): self
60
    {
61 18
        $clone = clone $this;
62 18
        $clone->where[] = $callable;
63
64 18
        return $clone;
65
    }
66
67
    /**
68
     * Set an Iterator sorting callable function.
69
     */
70 6
    public function orderBy(callable $callable): self
71
    {
72 6
        $clone = clone $this;
73 6
        $clone->order_by[] = $callable;
74
75 6
        return $clone;
76
    }
77
78
    /**
79
     * Set LimitIterator Offset.
80
     *
81
     * @throws Exception if the offset is lesser than 0
82
     */
83 18
    public function offset(int $offset): self
84
    {
85 18
        if (0 > $offset) {
86 3
            throw new Exception(sprintf('%s() expects the offset to be a positive integer or 0, %s given', __METHOD__, $offset));
87
        }
88
89 15
        if ($offset === $this->offset) {
90 3
            return $this;
91
        }
92
93 12
        $clone = clone $this;
94 12
        $clone->offset = $offset;
95
96 12
        return $clone;
97
    }
98
99
    /**
100
     * Set LimitIterator Count.
101
     *
102
     * @throws Exception if the limit is lesser than -1
103
     */
104 33
    public function limit(int $limit): self
105
    {
106 33
        if (-1 > $limit) {
107 6
            throw new Exception(sprintf('%s() expects the limit to be greater or equal to -1, %s given', __METHOD__, $limit));
108
        }
109
110 27
        if ($limit === $this->limit) {
111 3
            return $this;
112
        }
113
114 24
        $clone = clone $this;
115 24
        $clone->limit = $limit;
116
117 24
        return $clone;
118
    }
119
120
    /**
121
     * Execute the prepared Statement on the {@link Reader} object.
122
     *
123
     * @param string[] $header an optional header to use instead of the CSV document header
124
     */
125 27
    public function process(Reader $csv, array $header = []): ResultSet
126
    {
127 27
        if ([] === $header) {
128 27
            $header = $csv->getHeader();
129
        }
130
131 27
        $iterator = array_reduce($this->where, [$this, 'filter'], $csv->getRecords($header));
132 27
        $iterator = $this->buildOrderBy($iterator);
133
134 27
        return new ResultSet(new LimitIterator($iterator, $this->offset, $this->limit), $header);
135
    }
136
137 3
    public function processResultSet(ResultSet $resultSet, array $header = []): ResultSet
138
    {
139 3
        if ([] === $header) {
140 3
            $header = $resultSet->getHeader();
141
        }
142
143 3
        $iterator = array_reduce($this->where, [$this, 'filter'], $resultSet->getRecords());
144 3
        $iterator = $this->buildOrderBy($iterator);
145
146 3
        return new ResultSet(new LimitIterator($iterator, $this->offset, $this->limit), $header);
147
    }
148
149
    /**
150
     * Filters elements of an Iterator using a callback function.
151
     */
152 12
    protected function filter(Iterator $iterator, callable $callable): CallbackFilterIterator
153
    {
154 12
        return new CallbackFilterIterator($iterator, $callable);
155
    }
156
157
    /**
158
     * Sort the Iterator.
159
     */
160 21
    protected function buildOrderBy(Iterator $iterator): Iterator
161
    {
162 21
        if ([] === $this->order_by) {
163 15
            return $iterator;
164
        }
165
166 6
        $compare = function (array $record_a, array $record_b): int {
167 6
            foreach ($this->order_by as $callable) {
168 6
                if (0 !== ($cmp = $callable($record_a, $record_b))) {
169 4
                    return $cmp;
170
                }
171
            }
172
173 3
            return $cmp ?? 0;
174 6
        };
175
176 6
        $iterator = new ArrayIterator(iterator_to_array($iterator));
177 6
        $iterator->uasort($compare);
178
179 6
        return $iterator;
180
    }
181
}
182