Passed
Push — develop ( a803f3...78e549 )
by nguereza
03:14
created

Collection::getType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * Platine Collection
5
 *
6
 * Platine Collection provides a flexible and simple PHP collection implementation.
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine Collection
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a copy
13
 * of this software and associated documentation files (the "Software"), to deal
14
 * in the Software without restriction, including without limitation the rights
15
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
 * copies of the Software, and to permit persons to whom the Software is
17
 * furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included in all
20
 * copies or substantial portions of the Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
 * SOFTWARE.
29
 */
30
31
/**
32
 *  @file Collection.php
33
 *
34
 *  The Collection class
35
 *
36
 *  @package    Platine\Collection
37
 *  @author Platine Developers Team
38
 *  @copyright  Copyright (c) 2020
39
 *  @license    http://opensource.org/licenses/MIT  MIT License
40
 *  @link   http://www.iacademy.cf
41
 *  @version 1.0.0
42
 *  @filesource
43
 */
44
45
declare(strict_types=1);
46
47
namespace Platine\Collection;
48
49
use OutOfRangeException;
50
use Platine\Collection\Exception\InvalidOperationException;
51
52
/**
53
 * Class Collection
54
 * @package Platine\Collection
55
 * @template T
56
 * @extends BaseCollection<T>
57
 * @implements MergeableInterface<T>
58
 * @implements SortableInterface<T>
59
 */
60
class Collection extends BaseCollection implements
61
    IterableInterface,
62
    MergeableInterface,
63
    SortableInterface
64
{
65
66
    /**
67
     * The type of this collection elements
68
     * @var string
69
     */
70
    protected string $type = '';
71
72
    /**
73
     * Create new instance
74
     * @param mixed ...$data
75
     */
76
    public function __construct(...$data)
77
    {
78
        foreach ($data as $value) {
79
            $this->validateEntry($value);
80
        }
81
82
        parent::__construct(...$data);
0 ignored issues
show
Bug introduced by
$data is expanded, but the parameter $initials of Platine\Collection\BaseCollection::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

82
        parent::__construct(/** @scrutinizer ignore-type */ ...$data);
Loading history...
83
    }
84
85
    /**
86
     * Fill the collection
87
     * @param array<mixed, mixed> $data
88
     * @return void
89
     */
90
    public function fill(array $data): void
91
    {
92
        foreach ($data as $value) {
93
            $this->add($value);
94
        }
95
    }
96
97
    /**
98
     *
99
     * @param mixed $value
100
     * @return void
101
     */
102
    public function add($value): void
103
    {
104
        $this->validateEntry($value);
105
106
        $data = $this->all();
107
        array_push($data, $value);
108
        $this->data->setData($data);
109
    }
110
111
    /**
112
     *
113
     * @param int $offset
114
     * @param mixed $value
115
     * @return bool
116
     * @throws InvalidOperationException
117
     */
118
    public function update(int $offset, $value): bool
119
    {
120
        $this->validateEntry($value);
121
122
        if (!$this->exists($offset)) {
123
            throw new InvalidOperationException(sprintf(
124
                'The collection index [%d] does not exists',
125
                $offset
126
            ));
127
        }
128
129
        $this->data[$offset] = $value;
130
131
        return $this->data[$offset] === $value;
132
    }
133
134
    /**
135
     *
136
     * @return string
137
     */
138
    public function getType(): string
139
    {
140
        return $this->type;
141
    }
142
143
    /**
144
     *
145
     * @param Collection<T> $collection
146
     * @return $this<T>
147
     * @throws InvalidOperationException
148
     */
149
    public function diff(BaseCollection $collection): self
150
    {
151
        if (!$collection instanceof self) {
152
            throw new InvalidOperationException(
153
                'You should only compare a collection of same type'
154
            );
155
        }
156
157
        if ($this->type !== $collection->getType()) {
158
            throw new InvalidOperationException(sprintf(
159
                'This is a collection of type [%s], you '
160
                    . 'cannot pass a collection of type [%s]',
161
                $this->type,
162
                $collection->getType()
163
            ));
164
        }
165
166
        $diffValues = array_udiff(
167
            $this->all(),
168
            $collection->all(),
169
            function ($a, $b) {
170
                return $a <=> $b;
171
            }
172
        );
173
174
        return new $this(...$diffValues);
175
    }
176
177
    /**
178
     *
179
     * @param int $offset
180
     * @return mixed
181
     * @throws OutOfRangeException
182
     */
183
    public function get(int $offset)
184
    {
185
        if ($this->isEmpty()) {
186
            throw new OutOfRangeException('The collection is empty');
187
        }
188
189
        if (!$this->data->offsetExists($offset)) {
190
            throw new OutOfRangeException(sprintf(
191
                'The collection index [%d] does not exists',
192
                $offset
193
            ));
194
        }
195
196
        return $this->data->offsetGet($offset);
197
    }
198
199
    /**
200
     * {@inheritedoc}
201
     */
202
    public function equals(BaseCollection $collection): bool
203
    {
204
        if (!$collection instanceof self) {
205
            throw new InvalidOperationException(
206
                'You should only compare an object collection against another one'
207
            );
208
        }
209
210
        return $this->all() == $collection->all();
211
    }
212
213
    /**
214
     *
215
     * @param callable $callback
216
     * @return $this|null
217
     */
218
    public function filter(callable $callback): ?self
219
    {
220
        $matches = [];
221
222
        foreach ($this->data as $key => $value) {
223
            $val = call_user_func($callback, $key, $value);
224
            if ($val === true) {
225
                $matches[] = $value;
226
            }
227
        }
228
229
        return count($matches) > 0
230
                ? new $this(...array_values($matches))
231
                : null;
232
    }
233
234
    /**
235
     * {@inheritedoc}
236
     */
237
    public function forEach(callable $callback): void
238
    {
239
        $data = $this->all();
240
        array_walk($data, $callback);
241
242
        $this->data->setData($data);
243
    }
244
245
    /**
246
     *
247
     * @param callable $callback
248
     * @return $this|null
249
     */
250
    public function map(callable $callback): ?self
251
    {
252
        $matches = array_map($callback, $this->all());
253
254
        return count($matches) > 0
255
                ? new $this(...array_values($matches))
256
                : null;
257
    }
258
259
    /**
260
     * {@inheritedoc}
261
     */
262
    public function merge(BaseCollection $collection): BaseCollection
263
    {
264
        return new $this(...array_merge($this->all(), $collection->all()));
265
    }
266
267
    /**
268
     * Return a random element of the collection
269
     * @return mixed
270
     */
271
    public function rand()
272
    {
273
        if ($this->isEmpty()) {
274
            throw new InvalidOperationException('The collection is empty');
275
        }
276
277
        /** @var int $offset */
278
        $offset = array_rand($this->all());
279
280
        return $this->get($offset);
281
    }
282
283
    /**
284
     * Remove the element at the given index
285
     * @param int $offset
286
     * @return void
287
     */
288
    public function remove(int $offset): void
289
    {
290
        if ($this->isEmpty()) {
291
            throw new OutOfRangeException('The collection is empty');
292
        }
293
294
        if (!$this->data->offsetExists($offset)) {
295
            throw new OutOfRangeException(sprintf(
296
                'The collection index [%d] does not exists',
297
                $offset
298
            ));
299
        }
300
301
        $this->data->offsetUnset($offset);
302
        $this->repopulate();
303
    }
304
305
    /**
306
     *
307
     * @param int $offset
308
     * @param int|null $length
309
     * @return $this<T>|null
310
     */
311
    public function slice(int $offset, ?int $length): ?self
312
    {
313
        $newData = array_slice($this->all(), $offset, $length);
314
315
        return count($newData) > 0
316
                ? new $this(...$newData)
317
                : null;
318
    }
319
320
    /**
321
     * Sort the collection
322
     * @param callable $callback
323
     * @return Collection<T>|null
324
     */
325
    public function sort(callable $callback): ?BaseCollection
326
    {
327
        $data = $this->all();
328
329
        return usort($data, $callback)
330
                ? new $this(...$data)
331
                : null;
332
    }
333
334
    /**
335
     *
336
     * @return $this
337
     */
338
    public function reverse(): self
339
    {
340
        if ($this->isEmpty()) {
341
            throw new InvalidOperationException('The collection is empty');
342
        }
343
344
        return new $this(...array_reverse($this->all()));
345
    }
346
347
    /**
348
     *
349
     * @return void
350
     */
351
    protected function repopulate(): void
352
    {
353
        $oldData = array_values($this->all());
354
        $this->data->setData($oldData);
355
    }
356
357
    /**
358
     * Validate the collection value type
359
     * @param mixed $value
360
     * @return bool
361
     */
362
    protected function validateEntry($value): bool
363
    {
364
        return true;
365
    }
366
}
367