Completed
Pull Request — master (#6)
by Roman
03:03
created

Collection::validate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace kartavik\Collections;
4
5
use kartavik\Collections\Exceptions\InvalidElementException;
6
use kartavik\Collections\Exceptions\UnprocessedTypeException;
7
8
/**
9
 * Class Collection
10
 * @package kartavik\Collections
11
 */
12
class Collection implements \ArrayAccess, \Countable, \IteratorAggregate, \JsonSerializable
13
{
14
    /** @var string */
15
    private $type = null;
16
17
    /** @var array */
18
    protected $container = [];
19
20
    public function __construct(string $type, iterable ...$iterables)
21
    {
22
        static::validateType($type);
23
24
        $this->type = $type;
25
26
        foreach ($iterables as $iterable) {
27
            foreach ($iterable as $index => $item) {
28
                $this->add($item, $index);
29
            }
30
        }
31
    }
32
33
    final public function type(): string
34
    {
35
        return $this->type;
36
    }
37
38
    public function offsetExists($offset): bool
39
    {
40
        return array_key_exists($offset, $this->container);
41
    }
42
43
    public function offsetGet($offset)
44
    {
45
        return $this->container[$offset];
46
    }
47
48
    public function offsetUnset($offset): void
49
    {
50
        if ($this->offsetExists($offset)) {
51
            unset($this->container[$offset]);
52
        }
53
    }
54
55
    public function getIterator(): \ArrayIterator
56
    {
57
        return new \ArrayIterator($this->container);
58
    }
59
60
    public function append(): void
61
    {
62
        $items = func_get_args();
63
64
        foreach ($items as $item) {
65
            $this->add($item);
66
        }
67
    }
68
69
    /**
70
     * @param mixed $index
71
     * @param mixed $value
72
     *
73
     * @throws InvalidElementException
74
     */
75
    public function offsetSet($index, $value): void
76
    {
77
        $this->add($value, $index);
78
    }
79
80
    public function jsonSerialize(): array
81
    {
82
        return $this->container;
83
    }
84
85
    public function isCompatible($var): bool
86
    {
87
        try {
88
            if ($var instanceof self) {
89
                return true;
90
            } elseif (is_array($var)) {
91
                foreach ($var as $item) {
92
                    try {
93
                        $this->validate($item);
94
                    } catch (InvalidElementException $ex) {
95
                        return false;
96
                    }
97
                }
98
99
                return true;
100
            }
101
        } catch (\InvalidArgumentException $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
102
        } finally {
103
            return false;
104
        }
105
    }
106
107
    public function first()
108
    {
109
        reset($this->container);
110
111
        return $this->container[key($this->container)];
112
    }
113
114
    /**
115
     * @param $item
116
     *
117
     * @throws InvalidElementException
118
     */
119
    public function validate($item): void
120
    {
121
        $type = $this->type();
122
123
        if (!$item instanceof $type) {
124
            throw new UnprocessedTypeException($item, $type);
125
        }
126
    }
127
128
    public function chunk(int $size): Collection
129
    {
130
        $mappedType = get_class($this->offsetGet(0));
131
        /** @var Collection $collection */
132
        $collection = Collection::{Collection::class}();
133
        $chunked = array_chunk($this->jsonSerialize(), $size);
134
135
        foreach ($chunked as $index => $chunk) {
136
            $collection->append(Collection::{$mappedType}());
137
138
            foreach ($chunk as $item) {
139
                $collection[$index]->append($item);
140
            }
141
        }
142
143
        return $collection;
144
    }
145
146
    public function column(string $property, callable $callback = null): Collection
147
    {
148
        $getterType = get_class($this->offsetGet(0)->$property);
149
150
        if (!is_null($callback)) {
151
            /** @var Collection $collection */
152
            $collection = Collection::{$getterType}();
153
154
            foreach ($this->jsonSerialize() as $item) {
155
                $collection->append(call_user_func($callback, $item->$property));
156
            }
157
158
            return $collection;
159
        } else {
160
            return Collection::{$getterType}(array_map(
161
                function ($item) use ($property) {
162
                    return $item->$property;
163
                },
164
                $this->jsonSerialize()
165
            ));
166
        }
167
    }
168
169
    public function pop()
170
    {
171
        return array_pop($this->container);
172
    }
173
174
    public function sum(callable $callback)
175
    {
176
        $sum = 0;
177
178
        foreach ($this as $element) {
179
            $sum += call_user_func($callback, $element);
180
        }
181
182
        return $sum;
183
    }
184
185
    /**
186
     * @param string $name
187
     * @param array  $arguments
188
     *
189
     * @return Collection
190
     */
191
    public static function __callStatic(string $name, array $arguments = [])
192
    {
193
        if (!empty($arguments) && is_array($arguments[0])) {
194
            $arguments = $arguments[0];
195
        }
196
197
        static::validateType($name);
198
199
        reset($arguments);
200
201
        if (current($arguments) instanceof Collection) {
202
            return new static($name, ...$arguments);
203
        } else {
204
            return new static($name, $arguments);
205
        }
206
    }
207
208
    public function count(): int
209
    {
210
        return count($this->container);
211
    }
212
213
    public function add($item, $index = null): void
214
    {
215
        $this->validate($item);
216
        $this->container[$index ?? $this->count()] = $item;
217
    }
218
219
    protected static function validateType(string $type): void
220
    {
221
        if (!class_exists($type)) {
222
            throw new UnprocessedTypeException($type);
223
        }
224
    }
225
}
226