Completed
Pull Request — master (#49)
by Luke
02:09
created

ObjectCollection::push()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 8
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 = '')
0 ignored issues
show
Unused Code introduced by
The parameter $delimiter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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
        throw new BadMethodCallException(sprintf(
74
            'Objects of type, "%s" cannot be converted to a string.',
75
            __CLASS__
76
        ));
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function set($index, $val)
83
    {
84
        $this->assertValidType($val);
85
86
        return parent::set($index, $val);
87
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92
    public function append($item)
93
    {
94
        $this->assertValidType($item);
95
        return parent::append($item);
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function prepend($item)
102
    {
103
        $this->assertValidType($item);
104
        return parent::prepend($item);
105
    }
106
107
    /**
108
     * Pad this collection to a certain size.
109
     *
110
     * Returns a new collection, padded to the given size, with clones of the given object.
111
     *
112
     * @param int         $size The number of items that should be in the collection
113
     * @param object|null $with The value to pad the collection with
114
     *
115
     * @return ObjectCollection
116
     */
117
    public function pad($size, $with = null)
118
    {
119
        $this->assertValidType($with);
120
        $data = $this->data;
121
        if (($count = count($data)) < $size) {
122
            while ($count < $size) {
123
                $with  = clone $with;
124
                $count = array_push($data, $with);
125
            }
126
        }
127
128
        return new self($data);
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    protected function prepareData($data)
135
    {
136
        if ($data instanceof SplObjectStorage) {
137
            $tmp = [];
138
            foreach ($data as $obj) {
139
                $tmp[spl_object_hash($obj)] = $obj;
140
            }
141
            // @todo Data is potentially still lost even though I copy all the objects from the SplObjectStorage object.
142
            // These objects store not only objects, but also data associated with that object. That data is lost here.
143
            $data = $tmp;
144
        }
145
146
        return $data;
147
    }
148
149
    /**
150
     * Is correct input data type?
151
     *
152
     * @param mixed $data The data to assert correct type of
153
     *
154
     * @return bool
155
     */
156
    protected function isConsistentDataStructure($data)
157
    {
158
        // this collection may only contain scalar or null values
159
        if (!is_traversable($data)) {
160
            return false;
161
        }
162
        foreach ($data as $key => $val) {
163
            try {
164
                $this->assertValidType($val);
165
            } catch (InvalidArgumentException $e) {
166
                return false;
167
            }
168
        }
169
170
        return true;
171
    }
172
173
    /**
174
     * Assert a value is of valid type.
175
     *
176
     * @param mixed $value The value to check type of
177
     *
178
     * @throws InvalidArgumentException
179
     */
180
    protected function assertValidType($value)
181
    {
182
        if (is_object($value)) {
183
            if (is_null($this->type)) {
184
                return;
185
            }
186
            if ($value instanceof $this->type) {
187
                return;
188
            }
189
            throw new InvalidArgumentException(sprintf(
190
                'Invalid object type "%s", expecting "%s".',
191
                get_class($value),
192
                $this->type
193
            ));
194
        }
195
        throw new InvalidArgumentException('Invalid value type: "' . gettype($value) . '". Expecting an object.');
196
    }
197
}
198