Test Failed
Pull Request — master (#6)
by Roman
01:58
created

Collection::offsetExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
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, \Serializable
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
        $this->type = $type;
23
24
        foreach ($iterables as $iterable) {
25
            foreach ($iterable as $item) {
26
                $this->add($item);
27
            }
28
        }
29
    }
30
31
    final public function type(): string
32
    {
33
        return $this->type;
34
    }
35
36
    public function offsetExists($offset): bool
37
    {
38
        return array_key_exists($offset, $this->container);
39
    }
40
41
    public function offsetGet($offset)
42
    {
43
        return $this->container[$offset];
44
    }
45
46
    public function offsetUnset($offset): void
47
    {
48
        if ($this->offsetExists($offset)) {
49
            unset($this->container[$offset]);
50
        }
51
    }
52
53
    public function getIterator(): \ArrayIterator
54
    {
55
        return new \ArrayIterator($this->container);
56
    }
57
58
    public function append(): void
59
    {
60
        $values = func_get_args();
61
62
        foreach ($values as $item) {
63
            $this->add($item);
64
        }
65
    }
66
67
    public static function makeSafe(string $type, iterable ...$iterables)
68
    {
69
        $collection = new Collection($type);
70
71
        foreach ($iterables as $iterable) {
72
            foreach ($iterable as $key => $item) {
73
                $collection->add($item, $key);
74
            }
75
        }
76
77
        return $collection;
78
    }
79
80
    /**
81
     * @param mixed $index
82
     * @param mixed $value
83
     *
84
     * @throws InvalidElementException
85
     */
86
    public function offsetSet($index, $value): void
87
    {
88
        $this->add($value, $index);
89
    }
90
91
    public function serialize()
92
    {
93
        return serialize($this->container);
94
    }
95
96
    public function unserialize($serialized, array $options = [])
97
    {
98
        $unserialized = unserialize($serialized, $options);
99
100
        if (is_iterable($unserialized)) {
101
            return $this->join($unserialized, true);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->join($unserialized, true) targeting kartavik\Collections\Collection::join() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
102
        }
103
104
        throw new \InvalidArgumentException('Serialized object must be iterable');
105
    }
106
107
    public function join(iterable $iterable, bool $useKeys = false)
108
    {
109
        foreach ($iterable as $key => $item) {
110
            $this->add($item, $useKeys ? $key : null);
111
        }
112
    }
113
114
    public function jsonSerialize(): array
115
    {
116
        return $this->container;
117
    }
118
119
    public function isCompatible($element): bool
120
    {
121
        try {
122
            if ($element instanceof self) {
123
                return true;
124
            } elseif (is_array($element)) {
125
                foreach ($element as $item) {
126
                    try {
127
                        $this->validate($item);
128
                    } catch (InvalidElementException $ex) {
129
                        return false;
130
                    }
131
                }
132
133
                return true;
134
            }
135
        } catch (\InvalidArgumentException $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
136
        } finally {
137
            return false;
138
        }
139
    }
140
141
    public function first()
142
    {
143
        reset($this->container);
144
145
        return $this->container[key($this->container)];
146
    }
147
148
    /**
149
     * @param $item
150
     *
151
     * @throws InvalidElementException
152
     */
153
    public function validate($item): void
154
    {
155
        $type = $this->type();
156
157
        if (!$item instanceof $type) {
158
            throw new InvalidElementException($item, $type);
159
        }
160
    }
161
162
    public function map(callable $callback): Collection
163
    {
164
        $type = get_class(call_user_func(
165
            $callback,
166
            $this->first()
167
        ));
168
169
        $elements = array_map($callback, $this->container, array_keys($this->container));
170
171
        return Collection::{$type}($elements);
172
    }
173
174
    public function walk(callable $callback): bool
175
    {
176
        return array_walk($this->container, $callback);
177
    }
178
179
    public function chunk(int $size): Collection
180
    {
181
        $mappedType = get_class($this->offsetGet(0));
182
        /** @var Collection $collection */
183
        $collection = Collection::{Collection::class}();
184
        $chunked = array_chunk($this->jsonSerialize(), $size);
185
186
        foreach ($chunked as $index => $chunk) {
187
            $collection->append(Collection::{$mappedType}());
188
189
            foreach ($chunk as $item) {
190
                $collection[$index]->append($item);
191
            }
192
        }
193
194
        return $collection;
195
    }
196
197
    public function column(string $property, callable $callback = null): Collection
198
    {
199
        $getterType = get_class($this->offsetGet(0)->$property);
200
201
        if (!is_null($callback)) {
202
            /** @var Collection $collection */
203
            $collection = Collection::{$getterType}();
204
205
            foreach ($this->jsonSerialize() as $item) {
206
                $collection->append(call_user_func($callback, $item->$property));
207
            }
208
209
            return $collection;
210
        } else {
211
            return Collection::{$getterType}(array_map(
212
                function ($item) use ($property) {
213
                    return $item->$property;
214
                },
215
                $this->jsonSerialize()
216
            ));
217
        }
218
    }
219
220
    public function pop()
221
    {
222
        return array_pop($this->container);
223
    }
224
225
    public function sum(callable $callback)
226
    {
227
        $sum = 0;
228
229
        foreach ($this as $element) {
230
            $sum += call_user_func($callback, $element);
231
        }
232
233
        return $sum;
234
    }
235
236
    /**
237
     * @param string $name
238
     * @param array  $arguments
239
     *
240
     * @return Collection
241
     */
242
    public static function __callStatic(string $name, array $arguments = [])
243
    {
244
        if (!empty($arguments) && is_array($arguments[0])) {
245
            $arguments = $arguments[0];
246
        }
247
248
        static::validateType($name);
249
250
        reset($arguments);
251
252
        if (current($arguments) instanceof Collection) {
253
            return new static($name, ...$arguments);
254
        } else {
255
            return new static($name, $arguments);
256
        }
257
    }
258
259
    public function count(): int
260
    {
261
        return count($this->container);
262
    }
263
264
    public function add($item, $index = null): void
265
    {
266
        $this->validate($item);
267
        $this->container[$index ?? $this->count()] = $item;
268
    }
269
270
    protected static function validateType(string $type): void
271
    {
272
        if (!class_exists($type)) {
273
            throw new UnprocessedTypeException($type);
274
        }
275
    }
276
}
277