Completed
Push — master ( 6c1d93...a738a2 )
by Joschi
02:48
created

Collection::removeObject()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 18
ccs 0
cts 11
cp 0
rs 9.4285
cc 3
eloc 12
nc 4
nop 1
crap 12
1
<?php
2
3
/**
4
 * apparat-object
5
 *
6
 * @category    Apparat
7
 * @package     Apparat\Object\Domain
8
 * @author      Joschi Kuphal <[email protected]> / @jkphl
9
 * @copyright   Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
10
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
11
 */
12
13
/***********************************************************************************
14
 *  The MIT License (MIT)
15
 *
16
 *  Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
17
 *
18
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
19
 *  this software and associated documentation files (the "Software"), to deal in
20
 *  the Software without restriction, including without limitation the rights to
21
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
22
 *  the Software, and to permit persons to whom the Software is furnished to do so,
23
 *  subject to the following conditions:
24
 *
25
 *  The above copyright notice and this permission notice shall be included in all
26
 *  copies or substantial portions of the Software.
27
 *
28
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
30
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
31
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
32
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34
 ***********************************************************************************/
35
36
namespace Apparat\Object\Domain\Model\Object;
37
38
use Apparat\Object\Domain\Model\Path\LocalPath;
39
use Apparat\Object\Domain\Model\Path\PathInterface;
40
use Apparat\Object\Domain\Model\Path\RepositoryPath;
41
42
/**
43
 * Lazy loading object collection
44
 *
45
 * @package Apparat\Object
46
 * @subpackage Apparat\Object\Domain
47
 */
48
class Collection implements CollectionInterface
49
{
50
    /**
51
     * Objects
52
     *
53
     * @var ObjectInterface[]|RepositoryPath[]
54
     */
55
    protected $_objects = array();
56
    /**
57
     * Object IDs
58
     *
59
     * @var array
60
     */
61
    protected $_objectIds = array();
62
    /**
63
     * Internal object pointer
64
     *
65
     * @var int
66
     */
67
    protected $_pointer = 0;
68
69
    /*******************************************************************************
70
     * PUBLIC METHODS
71
     *******************************************************************************/
72
73
    /**
74
     * Collection constructor
75
     *
76
     * @param array $objects Collection objects
77
     * @throws InvalidArgumentException If the an invalid object or path is provided
78
     */
79 2
    public function __construct(array $objects = [])
80
    {
81 2
        foreach ($objects as $object) {
82
            // If it's an object
83 2
            if ($object instanceof ObjectInterface) {
84
                $this->_objects[$object->getId()->getId()] = $object;
85
86
                // Else if it's an object path
87
            } elseif ($object instanceof RepositoryPath) {
88 2
                $this->_objects[$object->getId()->getId()] = $object;
0 ignored issues
show
Bug introduced by
The method getId cannot be called on $object->getId() (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
89
90
                // Else: Error
91
            } else {
92
                throw new InvalidArgumentException(
93
                    'Invalid collection object or path',
94 2
                    InvalidArgumentException::INVALID_COLLECTION_OBJECT_OR_PATH
95
                );
96
            }
97
        }
98
99 2
        $this->_objectIds = array_keys($this->_objects);
100 2
    }
101
102
    /**
103
     * Return the current object
104
     *
105
     * @return ObjectInterface Current object
106
     */
107
    public function current()
108
    {
109
        return $this->_loadObject($this->_objectIds[$this->_pointer]);
110
    }
111
112
    /**
113
     * Move forward to next object
114
     *
115
     * @return void
116
     */
117
    public function next()
118
    {
119
        ++$this->_pointer;
120
    }
121
122
    /**
123
     * Return the ID of the current object
124
     *
125
     * @return int Object ID
126
     */
127
    public function key()
128
    {
129
        return $this->_objectIds[$this->_pointer];
130
    }
131
132
    /**
133
     * Checks if current position is valid
134
     *
135
     * @return boolean The current position is valid
136
     */
137
    public function valid()
138
    {
139
        return isset($this->_objectIds[$this->_pointer]);
140
    }
141
142
    /**
143
     * Rewind the Iterator to the first object
144
     *
145
     * @return void
146
     */
147
    public function rewind()
148
    {
149
        $this->_pointer = 0;
150
    }
151
152
    /**
153
     * Whether an object ID exists
154
     *
155
     * @param int $offset Object ID
156
     * @return boolean Whether the object ID exists
157
     */
158
    public function offsetExists($offset)
159
    {
160
        return isset($this->_objects[$offset]);
161
    }
162
163
    /**
164
     * Get an object with a particular ID
165
     *
166
     * @param int $offset Object ID
167
     * @return ObjectInterface Object
168
     */
169
    public function offsetGet($offset)
170
    {
171
        return $this->_objects[$offset];
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->_objects[$offset]; of type Apparat\Object\Domain\Mo...del\Path\RepositoryPath adds the type Apparat\Object\Domain\Model\Path\RepositoryPath to the return on line 171 which is incompatible with the return type documented by Apparat\Object\Domain\Mo...t\Collection::offsetGet of type Apparat\Object\Domain\Model\Object\ObjectInterface.
Loading history...
172
    }
173
174
    /**
175
     * Set an object by ID
176
     *
177
     * @param int $offset Object ID
178
     * @param ObjectInterface $value Object
179
     * @return void
180
     * @throws RuntimeException When an object should be set by ID
181
     */
182
    public function offsetSet($offset, $value)
0 ignored issues
show
Unused Code introduced by
The parameter $offset 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...
Unused Code introduced by
The parameter $value 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...
183
    {
184
185
    }
186
187
    /**
188
     * Unset an object by ID
189
     *
190
     * @param int $offset Object ID
191
     * @return Collection Object collection with the object removed
192
     */
193
    public function offsetUnset($offset)
194
    {
195
        $objects = $this->_objects;
196
        unset($objects[$offset]);
197
        return new self($objects);
198
    }
199
200
    /**
201
     * Add an object to the collection
202
     *
203
     * @param string|ObjectInterface $object Object or object URL
204
     * @return Collection Modified object collection
205
     */
206
    public function addObject($object)
207
    {
208
209
        // If the object is not yet an object instance
210
        if (!($object instanceof ObjectInterface)) {
211
            if (!($object instanceof PathInterface)) {
212
                $object = new LocalPath(strval($object));
213
            }
214
        }
215
216
        $objects = $this->_objects;
217
        $objects[] = $object;
218
        return new self(array_values($objects));
219
    }
220
221
    /**
222
     * Remove an object out of this collection
223
     *
224
     * @param string|ObjectInterface $object Object or object ID
225
     * @return Collection Modified object collection
226
     */
227
    public function removeObject($object)
228
    {
229
        if ($object instanceof ObjectInterface) {
230
            $object = $object->getId();
231
        } else {
232
            $object = intval($object);
233
        }
234
        if (empty($this->_objects[$object])) {
235
            throw new InvalidArgumentException(
236
                sprintf('Unknown object ID "%s"', $object),
237
                InvalidArgumentException::UNKNOWN_OBJECT_ID
238
            );
239
        }
240
241
        $objects = $this->_objects;
242
        unset($objects[$object]);
243
        return new self(array_values($objects));
244
    }
245
246
    /**
247
     * Count objects in this collection
248
     *
249
     * @return int The number of objects in this collection
250
     */
251 2
    public function count()
252
    {
253 2
        return count($this->_objects);
254
    }
255
256
    /**
257
     * Append another collection
258
     *
259
     * @param Collection $collection Collection
260
     * @return Collection Combined collections
261
     */
262
    public function append(Collection $collection)
263
    {
264
        $objects = array_merge($this->_objects, $collection->_objects);
265
        return new self(array_values($objects));
266
    }
267
268
    /*******************************************************************************
269
     * PRIVATE METHODS
270
     *******************************************************************************/
271
272
    /**
273
     * Load and return an object by ID
274
     *
275
     * @param int $objectId Object ID
276
     * @return ObjectInterface Object
277
     */
278
    protected function _loadObject($objectId)
279
    {
280
        // Lazy-load the object once
281
        if ($this->_objects[$objectId] instanceof RepositoryPath) {
282
            $this->_objects[$objectId] = $this->_objects[$objectId]->getRepository()->loadObject(
0 ignored issues
show
Bug introduced by
The method getRepository does only exist in Apparat\Object\Domain\Model\Path\RepositoryPath, but not in Apparat\Object\Domain\Model\Object\ObjectInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
283
                $this->_objects[$objectId]
284
            );
285
        }
286
287
        return $this->_objects[$objectId];
288
    }
289
}
290