Completed
Push — master ( 2d2097...fee32b )
by ignace nyamagana
22s queued 11s
created

Statement::create()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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