Statement::buildOrderBy()   A
last analyzed

Complexity

Conditions 5
Paths 3

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5

Importance

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