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

Collection::last()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace kartavik\Collections;
4
5
use kartavik\Collections\Exception;
6
7
/**
8
 * Class Collection
9
 * @package kartavik\Collections
10
 */
11
class Collection implements \ArrayAccess, \Countable, \IteratorAggregate, \JsonSerializable
12
{
13
    /** @var string */
14
    private $type = null;
15
16
    /** @var array */
17
    protected $container = [];
18
19
    public function __construct(string $type, iterable ...$iterables)
20
    {
21
        static::validateType($type);
22
23
        $this->type = $type;
24
25
        foreach ($iterables as $iterable) {
26
            foreach ($iterable as $index => $item) {
27
                $this->add($item, $index);
28
            }
29
        }
30
    }
31
32
    final public function type(): string
33
    {
34
        return $this->type;
35
    }
36
37
    public function offsetExists($offset): bool
38
    {
39
        return array_key_exists($offset, $this->container);
40
    }
41
42
    public function offsetGet($offset): object
43
    {
44
        return $this->container[$offset];
45
    }
46
47
    public function offsetUnset($offset): void
48
    {
49
        if ($this->offsetExists($offset)) {
50
            unset($this->container[$offset]);
51
        }
52
    }
53
54
    public function getIterator(): \ArrayIterator
55
    {
56
        return new \ArrayIterator($this->container);
57
    }
58
59
    public function append(): void
60
    {
61
        $items = func_get_args();
62
63
        foreach ($items as $item) {
64
            $this->add($item);
65
        }
66
    }
67
68
    /**
69
     * @param mixed $index
70
     * @param object $value
71
     *
72
     * @throws Exception\InvalidElement
73
     */
74
    public function offsetSet($index, $value): void
75
    {
76
        $this->add($value, $index);
77
    }
78
79
    public function jsonSerialize(): array
80
    {
81
        return $this->container;
82
    }
83
84
    public function isCompatible($var): bool
85
    {
86
        try {
87
            if ($var instanceof self) {
88
                return true;
89
            } elseif (is_array($var)) {
90
                foreach ($var as $item) {
91
                    try {
92
                        $this->validate($item);
93
                    } catch (Exception\InvalidElement $ex) {
94
                        return false;
95
                    }
96
                }
97
98
                return true;
99
            } else {
100
                try {
101
                    $this->validate($var);
102
                } catch (Exception\InvalidElement $ex) {
103
                    return false;
104
                }
105
106
                return true;
107
            }
108
        } catch (\InvalidArgumentException $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
109
        } finally {
110
            return false;
111
        }
112
    }
113
114
    public function first(): object
115
    {
116
        reset($this->container);
117
118
        return current($this->container);
119
    }
120
121
    public function last(): object
122
    {
123
        end($this->container);
124
125
        return current($this->container);
126
    }
127
128
    public function validate(object $item): void
129
    {
130
        $type = $this->type();
131
132
        if (!$item instanceof $type) {
133
            throw new Exception\UnprocessedType($item, $type);
0 ignored issues
show
Bug introduced by
$type of type string is incompatible with the type integer expected by parameter $code of kartavik\Collections\Exc...ssedType::__construct(). ( Ignorable by Annotation )

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

133
            throw new Exception\UnprocessedType($item, /** @scrutinizer ignore-type */ $type);
Loading history...
Bug introduced by
$item of type object is incompatible with the type null|string expected by parameter $type of kartavik\Collections\Exc...ssedType::__construct(). ( Ignorable by Annotation )

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

133
            throw new Exception\UnprocessedType(/** @scrutinizer ignore-type */ $item, $type);
Loading history...
134
        }
135
    }
136
137
    public function chunk(int $size): Collection
138
    {
139
        $mappedType = get_class($this->offsetGet(0));
140
        /** @var Collection $collection */
141
        $collection = Collection::{Collection::class}();
142
        $chunked = array_chunk($this->jsonSerialize(), $size);
143
144
        foreach ($chunked as $index => $chunk) {
145
            $collection->append(Collection::{$mappedType}());
146
147
            foreach ($chunk as $item) {
148
                $collection[$index]->append($item);
149
            }
150
        }
151
152
        return $collection;
153
    }
154
155
    public function column(string $property, \Closure $callback = null): Collection
156
    {
157
        $getterType = get_class($this->offsetGet(0)->$property);
158
159
        if (!is_null($callback)) {
160
            /** @var Collection $collection */
161
            $collection = Collection::{$getterType}();
162
163
            foreach ($this->jsonSerialize() as $item) {
164
                $collection->append(call_user_func($callback, $item->$property));
165
            }
166
167
            return $collection;
168
        } else {
169
            return Collection::{$getterType}(array_map(
170
                function ($item) use ($property) {
171
                    return $item->$property;
172
                },
173
                $this->jsonSerialize()
174
            ));
175
        }
176
    }
177
178
    /**
179
     * @return object
180
     */
181
    public function pop()
182
    {
183
        return array_pop($this->container);
184
    }
185
186
    public function sum(callable $callback)
187
    {
188
        $sum = 0;
189
190
        foreach ($this as $element) {
191
            $sum += call_user_func($callback, $element);
192
        }
193
194
        return $sum;
195
    }
196
197
    /**
198
     * @param string $name
199
     * @param array  $arguments
200
     *
201
     * @return Collection
202
     */
203
    public static function __callStatic(string $name, array $arguments = [])
204
    {
205
        if (!empty($arguments) && is_array($arguments[0])) {
206
            $arguments = $arguments[0];
207
        }
208
209
        static::validateType($name);
210
211
        reset($arguments);
212
213
        if (current($arguments) instanceof Collection) {
214
            return new static($name, ...$arguments);
215
        } else {
216
            return new static($name, $arguments);
217
        }
218
    }
219
220
    public function count(): int
221
    {
222
        return count($this->container);
223
    }
224
225
    public function add(object $item, $index = null): void
226
    {
227
        $this->validate($item);
228
        $this->container[$index ?? $this->count()] = $item;
229
    }
230
231
    protected static function validateType(string $type): void
232
    {
233
        if (!class_exists($type)) {
234
            throw new Exception\UnprocessedType($type);
235
        }
236
    }
237
}
238