Completed
Push — master ( 88eef6...81252c )
by Joschi
02:44
created

Collection::__construct()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.4661

Importance

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