Completed
Push — 3.3 ( 5fced4...d53524 )
by Jerome
22:15 queued 11s
created

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
	public function __construct($items = [], $item_class = null) {
37
		if ($item_class) {
38
			if (!is_subclass_of($item_class, CollectionItemInterface::class)) {
39
				throw new InvalidArgumentException('Item class must implement ' . CollectionItemInterface::class);
40
			}
41
42
			$this->item_class = $item_class;
43
		}
44
45
		foreach ($items as $item) {
46
			$this->add($item);
47
		}
48
	}
49
50
	/**
51
	 * Validate if item is a valid collection item
52
	 *
53
	 * @param mixed $item Item
54
	 *
55
	 * @return void
56
	 */
57
	protected function assertValidItem($item) {
58
		$class = $this->item_class ? : CollectionItemInterface::class;
59
60
		if (!$item instanceof $class) {
61
			throw new InvalidParameterException('Collection ' . __CLASS__ . ' only accepts instances of ' . $class);
62
		}
63
	}
64
65
	/**
66
	 * {@inheritdoc}
67
	 */
68
	public function all() {
69
		return $this->items;
70
	}
71
72
	/**
73
	 * {@inheritdoc}
74
	 */
75
	public function count() {
76
		return count($this->items);
77
	}
78
79
	/**
80
	 * {@inheritdoc}
81
	 */
82
	public function add($item) {
83
		$this->assertValidItem($item);
84
85
		$this->items[$item->getID()] = $item;
86
87
		return $this;
88
	}
89
90
	/**
91
	 * {@inheritdoc}
92
	 */
93
	public function get($id) {
94
		return elgg_extract($id, $this->items);
95
	}
96
97
	/**
98
	 * {@inheritdoc}
99
	 */
100
	public function has($id) {
101
		return array_key_exists($id, $this->items);
102
	}
103
104
	/**
105
	 * {@inheritdoc}
106
	 */
107
	public function remove($id) {
108
		unset($this->items[$id]);
109
	}
110
111
	/**
112
	 * {@inheritdoc}
113
	 */
114
	public function fill($items) {
115
		$this->items = [];
116
		foreach ($items as $item) {
117
			$this->add($item);
118
		}
119
120
		return $this;
121
	}
122
123
	/**
124
	 * {@inheritdoc}
125
	 */
126
	public function merge($items) {
127
		foreach ($items as $item) {
128
			$this->add($item);
129
		}
130
131
		return $this;
132
	}
133
134
	/**
135
	 * {@inheritdoc}
136
	 */
137
	public function filter(callable $callback = null) {
138
		if ($callback) {
139
			$items = array_filter($this->items, $callback);
140
		} else {
141
			$items = array_values($this->items);
142
		}
143
144
		return new static($items, $this->item_class);
145
	}
146
147
	/**
148
	 * {@inheritdoc}
149
	 */
150
	public function sort(callable $callback = null) {
151
		if (!$callback) {
152
			$callback = function (CollectionItemInterface $f1, CollectionItemInterface $f2) {
153
				$p1 = $f1->getPriority() ? : 500;
154
				$p2 = $f2->getPriority() ? : 500;
155
				if ($p1 === $p2) {
156
					return 0;
157
				}
158
159
				return $p1 < $p2 ? -1 : 1;
160
			};
161
		}
162
163
		uasort($this->items, $callback);
164
165
		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
	public function walk(callable $callback) {
182
		foreach ($this->items as $id => $item) {
183
			call_user_func($callback, $item, $id);
184
		}
185
186
		return $this;
187
	}
188
189
	/**
190
	 * {@inheritdoc}
191
	 */
192
	public function map(callable $callback) {
193
		$map = [];
194
195
		$items = $this->filter()->all();
196
		foreach ($items as $id => &$item) {
197
			$map[$id] = call_user_func($callback, $item, $id);
198
		}
199
200
		return $map;
201
	}
202
203
	/**
204
	 * {@inheritdoc}
205
	 */
206
	public function offsetExists($offset) {
207
		return $this->has($offset);
208
	}
209
210
	/**
211
	 * {@inheritdoc}
212
	 */
213
	public function offsetGet($offset) {
214
		return $this->get($offset);
215
	}
216
217
	/**
218
	 * {@inheritdoc}
219
	 */
220
	public function offsetSet($offset, $value) {
221
		$this->assertValidItem($value);
222
223
		$key = $value->getID();
224
		$this->items[$key] = $value;
225
	}
226
227
	/**
228
	 * {@inheritdoc}
229
	 */
230
	public function offsetUnset($offset) {
231
		unset($this->items[$offset]);
232
	}
233
234
	/**
235
	 * {@inheritdoc}
236
	 */
237
	public function current() {
238
		return current($this->items);
239
	}
240
241
	/**
242
	 * {@inheritdoc}
243
	 */
244
	public function next() {
245
		next($this->items);
246
	}
247
248
	/**
249
	 * {@inheritdoc}
250
	 */
251
	public function key() {
252
		return key($this->items);
253
	}
254
255
	/**
256
	 * {@inheritdoc}
257
	 */
258
	public function valid() {
259
		return key($this->items) !== null;
260
	}
261
262
	/**
263
	 * {@inheritdoc}
264
	 */
265
	public function rewind() {
266
		reset($this->items);
267
	}
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
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