Completed
Push — master ( 4f8323...7523f4 )
by Luke
37s
created

ObjectCollection::set()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * Nozavroni/Collections
5
 * Just another collections library for PHP5.6+.
6
 *
7
 * @copyright Copyright (c) 2016 Luke Visinoni <[email protected]>
8
 * @author    Luke Visinoni <[email protected]>
9
 * @license   https://github.com/nozavroni/collections/blob/master/LICENSE The MIT License (MIT)
10
 */
11
namespace Noz\Collection;
12
13
use BadMethodCallException;
14
use InvalidArgumentException;
15
16
use SplObjectStorage;
17
use function Noz\is_traversable;
18
19
/**
20
 * Class ObjectCollection.
21
 *
22
 * An object collection - stores objects.
23
 *
24
 * @package Noz\Collection
25
 */
26
class ObjectCollection extends AbstractCollection
27
{
28
    /**
29
     * The required object type.
30
     *
31
     * @var string
32
     */
33
    protected $type;
34
35
    /**
36
     * ObjectCollection constructor.
37
     *
38
     * @param array<object>|SplObjectStorage|null $data An array of objects or SplObjectStorage object
39
     * @param string                              $type A class that all objects should be an instance of
0 ignored issues
show
Documentation introduced by
Should the type for parameter $type not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
40
     */
41
    public function __construct($data = null, $type = null)
42
    {
43
        if (is_null($data)) {
44
            $data = [];
45
        }
46
        $this->setRequiredType($type);
47
        parent::__construct($data);
48
    }
49
50
    /**
51
     * Set the required object type.
52
     *
53
     * If a required type is set, all objects added to this collection must be of this type.
54
     *
55
     * @param string|null $type The required object type
56
     *
57
     * @return $this
58
     */
59
    public function setRequiredType($type)
60
    {
61
        $this->type = $type;
62
63
        return $this;
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69
    public function join($delimiter = '')
70
    {
71
        // @todo I need to remove join and __toString
72
        // from AbstractCollection and only use it on collections where it makes sense, such as
73
        // CharCollection and the like...
74
        throw new BadMethodCallException(sprintf(
75
            'Objects of type, "%s" cannot be converted to a string.',
76
            __CLASS__
77
        ));
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function set($index, $val)
84
    {
85
        $this->assertValidType($val);
86
87
        return parent::set($index, $val);
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93
    public function push(...$items)
94
    {
95
        foreach ($items as $item) {
96
            $this->assertValidType($item);
97
        }
98
99
        return parent::push(...$items);
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function unshift(...$items)
106
    {
107
        foreach ($items as $item) {
108
            $this->assertValidType($item);
109
        }
110
111
        return parent::unshift(...$items);
112
    }
113
114
    /**
115
     * Pad this collection to a certain size.
116
     *
117
     * Returns a new collection, padded to the given size, with clones of the given object.
118
     *
119
     * @param int         $size The number of items that should be in the collection
120
     * @param object|null $with The value to pad the collection with
121
     *
122
     * @return ObjectCollection
123
     */
124
    public function pad($size, $with = null)
125
    {
126
        $this->assertValidType($with);
127
        $data = $this->data;
128
        if (($count = count($data)) < $size) {
129
            while ($count < $size) {
130
                $with  = clone $with;
131
                $count = array_push($data, $with);
132
            }
133
        }
134
135
        return new self($data);
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141
    protected function prepareData($data)
142
    {
143
        if ($data instanceof SplObjectStorage) {
144
            $tmp = [];
145
            foreach ($data as $obj) {
146
                $tmp[spl_object_hash($obj)] = $obj;
147
            }
148
            // @todo Data is potentially still lost even though I copy all the objects from the SplObjectStorage object.
149
            // These objects store not only objects, but also data associated with that object. That data is lost here.
150
            $data = $tmp;
151
        }
152
153
        return $data;
154
    }
155
156
    /**
157
     * Is correct input data type?
158
     *
159
     * @param mixed $data The data to assert correct type of
160
     *
161
     * @return bool
162
     */
163
    protected function isConsistentDataStructure($data)
164
    {
165
        // this collection may only contain scalar or null values
166
        if (!is_traversable($data)) {
167
            return false;
168
        }
169
        foreach ($data as $key => $val) {
170
            try {
171
                $this->assertValidType($val);
172
            } catch (InvalidArgumentException $e) {
173
                return false;
174
            }
175
        }
176
177
        return true;
178
    }
179
180
    /**
181
     * Assert a value is of valid type.
182
     *
183
     * @param mixed $value The value to check type of
184
     *
185
     * @throws InvalidArgumentException
186
     */
187
    protected function assertValidType($value)
188
    {
189
        if (is_object($value)) {
190
            if (is_null($this->type)) {
191
                return;
192
            }
193
            if ($value instanceof $this->type) {
194
                return;
195
            }
196
            throw new InvalidArgumentException(sprintf(
197
                'Invalid object type "%s", expecting "%s".',
198
                get_class($value),
199
                $this->type
200
            ));
201
        }
202
        throw new InvalidArgumentException('Invalid value type: "' . gettype($value) . '". Expecting an object.');
203
    }
204
}
205