Passed
Pull Request — master (#20)
by Roman
02:03
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, \Serializable
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::validateObject($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 isset($this->container[$offset]);
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 mixed $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 get_object_vars($this);
82
    }
83
84
    public function serialize(): string
85
    {
86
        return json_encode($this);
87
    }
88
89
    public function unserialize($serialized): Collection
90
    {
91
        $json = json_decode($serialized);
92
93
        return new static($json['type'], $json['container']);
0 ignored issues
show
Bug Best Practice introduced by
The expression return new static($json[...'], $json['container']) returns the type kartavik\Collections\Collection which is incompatible with the return type mandated by Serializable::unserialize() of void.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
94
    }
95
96
    public function isCompatible(iterable $collection): bool
97
    {
98
        try {
99
            foreach ($collection as $item) {
100
                $this->validate($item);
101
            }
102
        } catch (Exception\InvalidElement $exception) {
103
            return false;
104
        }
105
106
        return true;
107
    }
108
109
    public function first(): object
110
    {
111
        reset($this->container);
112
113
        return current($this->container);
114
    }
115
116
    public function last(): object
117
    {
118
        end($this->container);
119
120
        return current($this->container);
121
    }
122
123
    /**
124
     * @param object $item
125
     *
126
     * @throws Exception\InvalidElement
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, callable $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
    public function pop(): object
179
    {
180
        return array_pop($this->container);
181
    }
182
183
    public function sum(callable $callback)
184
    {
185
        $sum = 0;
186
187
        foreach ($this as $element) {
188
            $sum += call_user_func($callback, $element);
189
        }
190
191
        return $sum;
192
    }
193
194
    /**
195
     * @param string $name
196
     * @param array  $arguments
197
     *
198
     * @return Collection
199
     */
200
    public static function __callStatic(string $name, array $arguments = [])
201
    {
202
        if (!empty($arguments) && is_array($arguments[0])) {
203
            $arguments = $arguments[0];
204
        }
205
206
        static::validateObject($name);
207
208
        reset($arguments);
209
210
        if (current($arguments) instanceof Collection) {
211
            return new static($name, ...$arguments);
212
        } else {
213
            return new static($name, $arguments);
214
        }
215
    }
216
217
    public function count(): int
218
    {
219
        return count($this->container);
220
    }
221
222
    public function add($item, $index = null): void
223
    {
224
        $this->validate($item);
225
        $this->container[$index ?? $this->count()] = $item;
226
    }
227
228
    protected static function validateObject(string $type): void
229
    {
230
        if (!class_exists($type)) {
231
            throw new Exception\UnprocessedType($type);
232
        }
233
    }
234
}
235