Issues (946)

engine/classes/Elgg/Collections/Collection.php (1 issue)

1
<?php
2
3
namespace Elgg\Collections;
4
5
use ArrayAccess;
6
use Countable;
7
use InvalidArgumentException;
8
use InvalidParameterException;
9
use SeekableIterator;
10
11
/**
12
 * A collection of unique items
13
 */
14
class Collection implements CollectionInterface,
15
							ArrayAccess,
16
							SeekableIterator,
17
							Countable {
18
19
	/**
20
	 * @var CollectionItemInterface[]
21
	 */
22
	protected $items = [];
23
24
	/**
25
	 * @var string
26
	 */
27
	protected $item_class;
28
29
	/**
30
	 * Constructor
31
	 *
32
	 * @param CollectionItemInterface[] $items      Items
33
	 * @param string                    $item_class Member class
34
	 *                                              Restrict members of the collection to instances of this class
35
	 */
36 175
	public function __construct($items = [], $item_class = null) {
37 175
		if ($item_class) {
38 159
			if (!is_subclass_of($item_class, CollectionItemInterface::class)) {
39 1
				throw new InvalidArgumentException('Item class must implement ' . CollectionItemInterface::class);
40
			}
41
42 158
			$this->item_class = $item_class;
43
		}
44
45 174
		foreach ($items as $item) {
46 48
			$this->add($item);
47
		}
48 173
	}
49
50
	/**
51
	 * Validate if item is a valid collection item
52
	 *
53
	 * @param mixed $item Item
54
	 *
55
	 * @return void
56
	 */
57 138
	protected function assertValidItem($item) {
58 138
		$class = $this->item_class ? : CollectionItemInterface::class;
59
60 138
		if (!$item instanceof $class) {
61 1
			throw new InvalidParameterException('Collection ' . __CLASS__ . ' only accepts instances of ' . $class);
62
		}
63 138
	}
64
65
	/**
66
	 * {@inheritdoc}
67
	 */
68 79
	public function all() {
69 79
		return $this->items;
70
	}
71
72
	/**
73
	 * {@inheritdoc}
74
	 */
75 5
	public function count() {
76 5
		return count($this->items);
77
	}
78
79
	/**
80
	 * {@inheritdoc}
81
	 */
82 126
	public function add($item) {
83 126
		$this->assertValidItem($item);
84
85 126
		$this->items[$item->getID()] = $item;
86
87 126
		return $this;
88
	}
89
90
	/**
91
	 * {@inheritdoc}
92
	 */
93 62
	public function get($id) {
94 62
		return elgg_extract($id, $this->items);
95
	}
96
97
	/**
98
	 * {@inheritdoc}
99
	 */
100 34
	public function has($id) {
101 34
		return array_key_exists($id, $this->items);
102
	}
103
104
	/**
105
	 * {@inheritdoc}
106
	 */
107 3
	public function remove($id) {
108 3
		unset($this->items[$id]);
109 3
	}
110
111
	/**
112
	 * {@inheritdoc}
113
	 */
114 36
	public function fill($items) {
115 36
		$this->items = [];
116 36
		foreach ($items as $item) {
117 34
			$this->add($item);
118
		}
119
120 36
		return $this;
121
	}
122
123
	/**
124
	 * {@inheritdoc}
125
	 */
126 20
	public function merge($items) {
127 20
		foreach ($items as $item) {
128 20
			$this->add($item);
129
		}
130
131 20
		return $this;
132
	}
133
134
	/**
135
	 * {@inheritdoc}
136
	 */
137 59
	public function filter(callable $callback = null) {
138 59
		if ($callback) {
139 57
			$items = array_filter($this->items, $callback);
140
		} else {
141 58
			$items = array_values($this->items);
142
		}
143
144 59
		return new static($items, $this->item_class);
145
	}
146
147
	/**
148
	 * {@inheritdoc}
149
	 */
150 58
	public function sort(callable $callback = null) {
151 58
		if (!$callback) {
152 1
			$callback = function (CollectionItemInterface $f1, CollectionItemInterface $f2) {
153 1
				$p1 = $f1->getPriority() ? : 500;
154 1
				$p2 = $f2->getPriority() ? : 500;
155 1
				if ($p1 === $p2) {
156
					return 0;
157
				}
158
159 1
				return $p1 < $p2 ? -1 : 1;
160 1
			};
161
		}
162
163 58
		uasort($this->items, $callback);
164
165 58
		return $this;
166
	}
167
168
	/**
169
	 * Walk through members of the collection and apply a callback
170
	 *
171
	 * Unlike CollectionInterface::map(), this method does not return the result of mapping,
172
	 * rather returns the exact same instance of the collection after walking through
173
	 * its members by reference
174
	 *
175
	 * @see CollectionInterface::map()
176
	 *
177
	 * @param callable $callback Callback function
178
	 *
179
	 * @return static
180
	 */
181 57
	public function walk(callable $callback) {
182 57
		foreach ($this->items as $id => $item) {
183 34
			call_user_func($callback, $item, $id);
184
		}
185
186 57
		return $this;
187
	}
188
189
	/**
190
	 * {@inheritdoc}
191
	 */
192 57
	public function map(callable $callback) {
193 57
		$map = [];
194
195 57
		$items = $this->filter()->all();
196 57
		foreach ($items as $id => &$item) {
197 34
			$map[$id] = call_user_func($callback, $item, $id);
198
		}
199
200 57
		return $map;
201
	}
202
203
	/**
204
	 * {@inheritdoc}
205
	 */
206 6
	public function offsetExists($offset) {
207 6
		return $this->has($offset);
208
	}
209
210
	/**
211
	 * {@inheritdoc}
212
	 */
213 5
	public function offsetGet($offset) {
214 5
		return $this->get($offset);
215
	}
216
217
	/**
218
	 * {@inheritdoc}
219
	 */
220 36
	public function offsetSet($offset, $value) {
221 36
		$this->assertValidItem($value);
222
223 36
		$key = $value->getID();
224 36
		$this->items[$key] = $value;
225 36
	}
226
227
	/**
228
	 * {@inheritdoc}
229
	 */
230 1
	public function offsetUnset($offset) {
231 1
		unset($this->items[$offset]);
232 1
	}
233
234
	/**
235
	 * {@inheritdoc}
236
	 */
237 36
	public function current() {
238 36
		return current($this->items);
239
	}
240
241
	/**
242
	 * {@inheritdoc}
243
	 */
244 36
	public function next() {
245 36
		next($this->items);
246 36
	}
247
248
	/**
249
	 * {@inheritdoc}
250
	 */
251 2
	public function key() {
252 2
		return key($this->items);
253
	}
254
255
	/**
256
	 * {@inheritdoc}
257
	 */
258 59
	public function valid() {
259 59
		return key($this->items) !== null;
260
	}
261
262
	/**
263
	 * {@inheritdoc}
264
	 */
265 59
	public function rewind() {
266 59
		reset($this->items);
267 59
	}
268
269
	/**
270
	 * {@inheritdoc}
271
	 */
272
	public function seek($position) {
273
		$keys = array_keys($this->items);
274
275
		if (isset($keys[$position])) {
276
			throw new \OutOfBoundsException();
277
		}
278
279
		$key = $keys[$position];
280
281
		return $this->items[$key];
0 ignored issues
show
Bug Best Practice introduced by Ismayil Khayredinov
The expression return $this->items[$key] returns the type Elgg\Collections\CollectionItemInterface which is incompatible with the return type mandated by SeekableIterator::seek() of void.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
282
	}
283
284
}
285