Completed
Push — master ( 189322...ce8c86 )
by Lars
02:08
created

AbstractCollection::getType()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
nc 1
dl 0
loc 1
ccs 0
cts 0
cp 0
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy\Collection;
6
7
use Arrayy\Arrayy;
8
9
/**
10
 * This class provides a full implementation of `CollectionInterface`, to
11
 * minimize the effort required to implement this interface.
12
 */
13
abstract class AbstractCollection extends Arrayy implements CollectionInterface
14
{
15
    /**
16
     * The type of elements stored in this collection.
17
     *
18
     * @var string
19
     */
20
    private $collectionType;
21
22
    /**
23
     * Constructs a collection object of the specified type, optionally with the
24
     * specified data.
25
     *
26
     * @param mixed $data
27
     *                     <p>
28
     *                     The initial items to store in the collection.
29
     *                     </p>
30
     *                     <p>
31
     *                     Should be an array or a generator, otherwise it will try
32
     *                     to convert it into an array.
33
     *                     </p>
34
     */
35 9
    public function __construct($data = [])
36
    {
37 9
        $this->collectionType = $this->getType();
38 9
        parent::__construct($data);
39 8
    }
40
41
    /**
42
     * @return static[]
43
     */
44 2
    public function getCollection(): array
45
    {
46 2
        return $this->array;
47
    }
48
49
    /**
50
     * The type (FQCN) associated with this collection.
51
     *
52
     * @return string
53
     */
54
    abstract public function getType(): string;
55
56
    /**
57
     * Merge current items and items of given collections into a new one.
58
     *
59
     * @param CollectionInterface ...$collections The collections to merge.
60
     *
61
     * @throws \InvalidArgumentException if any of the given collections are not of the same type
62
     *
63
     * @return CollectionInterface
64
     */
65
    public function merge(CollectionInterface ...$collections): CollectionInterface
66
    {
67
        $temp = [$this->array];
68
69
        foreach ($collections as $index => $collection) {
70
            if (!$collection instanceof static) {
71
                throw new \InvalidArgumentException(
72
                    \sprintf('Collection with index %d must be of type %s', $index, static::class)
73
                );
74
            }
75
76
            $temp[] = $collection->toArray();
77
        }
78
79
        return new static(\array_replace(...$temp));
80
    }
81
82
    /**
83
     * Assigns a value to the specified offset + check the type.
84
     *
85
     * @param int|string|null $offset
86
     * @param mixed           $value
87
     */
88 1 View Code Duplication
    public function offsetSet($offset, $value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
89
    {
90 1
        if ($this->checkType($this->collectionType, $value) === false) {
91 1
            throw new \InvalidArgumentException(
92 1
                'Value must be of type ' . $this->collectionType . '; value is ' . $this->valueToString($value)
93
            );
94
        }
95
96
        parent::offsetSet($offset, $value);
97
    }
98
99
    /**
100
     * Prepend a (key) + value to the current array.
101
     *
102
     * @param mixed $value
103
     * @param mixed $key
104
     *
105
     * @return static
106
     *                <p>(Mutable) Return this Arrayy object, with the prepended value.</p>
107
     */
108 2 View Code Duplication
    public function prepend($value, $key = null): Arrayy
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
109
    {
110 2
        if ($this->checkType($this->collectionType, $value) === false) {
111 2
            throw new \InvalidArgumentException(
112 2
                'Value must be of type ' . $this->collectionType . '; value is ' . $this->valueToString($value)
113
            );
114
        }
115
116
        return parent::prepend($value, $key);
117
    }
118
119
    /**
120
     * Append a (key) + value to the current array.
121
     *
122
     * @param mixed $value
123
     * @param mixed $key
124
     *
125
     * @return static
126
     *                <p>(Mutable) Return this Arrayy object, with the appended values.</p>
127
     */
128 2 View Code Duplication
    public function append($value, $key = null): Arrayy
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
129
    {
130 2
        if ($this->checkType($this->collectionType, $value) === false) {
131 1
            throw new \InvalidArgumentException(
132 1
                'Value must be of type ' . $this->collectionType . '; value is ' . $this->valueToString($value)
133
            );
134
        }
135
136 1
        return parent::append($value, $key);
137
    }
138
139
    /**
140
     * Returns the values from given property or method.
141
     *
142
     * @param string $keyOrPropertyOrMethod the property or method name to filter by
143
     *
144
     * @throws \InvalidArgumentException if property or method is not defined
145
     *
146
     * @return array
147
     */
148 1
    public function column(string $keyOrPropertyOrMethod): array
149
    {
150
        // init
151 1
        $temp = [];
152
153 1
        foreach ($this->array as $item) {
154 1
            $temp[] = $this->extractValue($item, $keyOrPropertyOrMethod);
155
        }
156
157 1
        return $temp;
158
    }
159
160
    /**
161
     * Returns a collection of matching items.
162
     *
163
     * @param string $keyOrPropertyOrMethod the property or method to evaluate
164
     * @param mixed  $value                 the value to match
165
     *
166
     * @throws \InvalidArgumentException if property or method is not defined
167
     *
168
     * @return CollectionInterface
169
     */
170
    public function where(string $keyOrPropertyOrMethod, $value): CollectionInterface
171
    {
172
        return $this->filter(
173
            function ($item) use ($keyOrPropertyOrMethod, $value) {
174
                $accessorValue = $this->extractValue(
175
                    $item,
176
                    $keyOrPropertyOrMethod
177
                );
178
179
                return $accessorValue === $value;
180
            }
181
        );
182
    }
183
184
    /**
185
     * Internal mechanic of set method.
186
     *
187
     * @param string|null $key
188
     * @param mixed       $value
189
     * @param bool        $checkProperties
190
     *
191
     * @return bool
192
     */
193 8 View Code Duplication
    protected function internalSet($key, $value, $checkProperties = true): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
194
    {
195 8
        if ($this->checkType($this->collectionType, $value) === false) {
196 1
            throw new \InvalidArgumentException(
197 1
                'Value must be of type ' . $this->collectionType . '; value is ' . $this->valueToString($value)
198
            );
199
        }
200
201 7
        return parent::internalSet($key, $value, $checkProperties);
202
    }
203
204
    /**
205
     * Extracts the value of the given property or method from the object.
206
     *
207
     * @param object $object                the object to extract the value from
208
     * @param string $keyOrPropertyOrMethod the property or method for which the
209
     *                                      value should be extracted
210
     *
211
     * @throws \InvalidArgumentException if the method or property is not defined
212
     *
213
     * @return mixed the value extracted from the specified property or method
214
     */
215 1
    private function extractValue($object, string $keyOrPropertyOrMethod)
216
    {
217 1
        if (\array_key_exists($keyOrPropertyOrMethod, $object->array)) {
218 1
            return $object[$keyOrPropertyOrMethod];
219
        }
220
221
        if (\property_exists($object, $keyOrPropertyOrMethod)) {
222
            return $object->{$keyOrPropertyOrMethod};
223
        }
224
225
        if (\method_exists($object, $keyOrPropertyOrMethod)) {
226
            return $object->{$keyOrPropertyOrMethod}();
227
        }
228
229
        throw new \InvalidArgumentException(
230
            \sprintf('array-key & property & method "%s" not defined in %s', $keyOrPropertyOrMethod, \get_class($object))
231
        );
232
    }
233
234
    /**
235
     * Returns `true` if value is of the specified type.
236
     *
237
     * @param string $type  the type to check the value against
238
     * @param mixed  $value the value to check
239
     *
240
     * @return bool
241
     */
242 8
    private function checkType(string $type, $value): bool
243
    {
244
        switch ($type) {
245 8
            case 'array':
246
                return \is_array($value);
247 8
            case 'bool':
248 8
            case 'boolean':
249
                return \is_bool($value);
250 8
            case 'callable':
251
                return \is_callable($value);
252 8
            case 'float':
253 8
            case 'double':
254
                return \is_float($value);
255 8
            case 'int':
256 8
            case 'integer':
257
                return \is_int($value);
258 8
            case 'null':
259
                return $value === null;
260 8
            case 'numeric':
261
                return \is_numeric($value);
262 8
            case 'object':
263
                return \is_object($value);
264 8
            case 'resource':
265
                return \is_resource($value);
266 8
            case 'scalar':
267
                return \is_scalar($value);
268 8
            case 'string':
269
                return \is_string($value);
270 8
            case 'mixed':
271
                return true;
272
            default:
273 8
                return $value instanceof $type;
274
        }
275
    }
276
277
    /**
278
     * @param mixed $value
279
     *
280
     * @return string
281
     */
282 5
    private function valueToString($value): string
283
    {
284
        // null
285 5
        if ($value === null) {
286
            return 'NULL';
287
        }
288
289
        // bool
290 5
        if (\is_bool($value)) {
291
            return $value ? 'TRUE' : 'FALSE';
292
        }
293
294
        // array
295 5
        if (\is_array($value)) {
296
            return 'Array';
297
        }
298
299
        // scalar types (integer, float, string)
300 5
        if (\is_scalar($value)) {
301
            return (string) $value;
302
        }
303
304
        // resource
305 5
        if (\is_resource($value)) {
306
            return \get_resource_type($value) . ' resource #' . (int) $value;
307
        }
308
309
        // object
310 5
        return \get_class($value) . ' Object';
311
    }
312
}
313