Passed
Push — master ( ac9cc1...781f12 )
by Roman
01:54
created

Collection::unserialize()   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 1
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
        /** @var Collection $collection */
140
        $collection = new static(Collection::class);
141
        $chunked = array_chunk($this->container, $size);
142
143
        foreach ($chunked as $chunk) {
144
            $collection->append(new Collection($this->type(), $chunk));
145
        }
146
147
        return $collection;
148
    }
149
150
    public function column(\Closure $callback): Collection
151
    {
152
        $type = get_class(call_user_func($callback, current($this->container)));
153
        $collection = new Collection($type);
154
        $fetched = [];
155
156
        foreach ($this->container as $item) {
157
            $fetched[] = call_user_func($callback, $item);
158
        }
159
160
        $collection->append(...$fetched);
161
162
        return $collection;
163
    }
164
165
    public function pop(): object
166
    {
167
        return array_pop($this->container);
168
    }
169
170
    public function sum(\Closure $callback)
171
    {
172
        $sum = 0;
173
174
        foreach ($this as $element) {
175
            $sum += call_user_func($callback, $element);
176
        }
177
178
        return $sum;
179
    }
180
181
    public function count(): int
182
    {
183
        return count($this->container);
184
    }
185
186
    public function add($item, $index = null): void
187
    {
188
        $this->validate($item);
189
        $this->container[$index ?? $this->count()] = $item;
190
    }
191
192
    /**
193
     * @param string $name
194
     * @param array  $arguments
195
     *
196
     * @return Collection
197
     */
198
    public static function __callStatic(string $name, array $arguments = [])
199
    {
200
        if (!empty($arguments) && is_array($arguments[0])) {
201
            $arguments = $arguments[0];
202
        }
203
204
        static::validateObject($name);
205
206
        reset($arguments);
207
208
        if (current($arguments) instanceof Collection) {
209
            return new static($name, ...$arguments);
210
        } else {
211
            return new static($name, $arguments);
212
        }
213
    }
214
215
    protected static function validateObject(string $type): void
216
    {
217
        if (!class_exists($type)) {
218
            throw new Exception\UnprocessedType($type);
219
        }
220
    }
221
}
222