3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the MIT license. For more information, see
17 * <http://www.doctrine-project.org>.
20 namespace Doctrine\Common\Collections;
22 use Closure, ArrayIterator;
23 use Doctrine\Common\Collections\Expr\Expression;
24 use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor;
27 * An ArrayCollection is a Collection implementation that wraps a regular PHP array.
30 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
31 * @author Jonathan Wage <jonwage@gmail.com>
32 * @author Roman Borschel <roman@code-factory.org>
34 class ArrayCollection implements Collection, Selectable
37 * An array containing the entries of this collection.
44 * Initializes a new ArrayCollection.
46 * @param array $elements
48 public function __construct(array $elements = array())
50 $this->_elements = $elements;
54 * Gets the PHP array representation of this collection.
56 * @return array The PHP array representation of this collection.
58 public function toArray()
60 return $this->_elements;
64 * Sets the internal iterator to the first element in the collection and
65 * returns this element.
69 public function first()
71 return reset($this->_elements);
75 * Sets the internal iterator to the last element in the collection and
76 * returns this element.
80 public function last()
82 return end($this->_elements);
86 * Gets the current key/index at the current internal iterator position.
92 return key($this->_elements);
96 * Moves the internal iterator position to the next element.
100 public function next()
102 return next($this->_elements);
106 * Gets the element of the collection at the current internal iterator position.
110 public function current()
112 return current($this->_elements);
116 * Removes an element with a specific key/index from the collection.
119 * @return mixed The removed element or NULL, if no element exists for the given key.
121 public function remove($key)
123 if (isset($this->_elements[$key])) {
124 $removed = $this->_elements[$key];
125 unset($this->_elements[$key]);
134 * Removes the specified element from the collection, if it is found.
136 * @param mixed $element The element to remove.
137 * @return boolean TRUE if this collection contained the specified element, FALSE otherwise.
139 public function removeElement($element)
141 $key = array_search($element, $this->_elements, true);
143 if ($key !== false) {
144 unset($this->_elements[$key]);
153 * ArrayAccess implementation of offsetExists()
157 * @param mixed $offset
160 public function offsetExists($offset)
162 return $this->containsKey($offset);
166 * ArrayAccess implementation of offsetGet()
170 * @param mixed $offset
173 public function offsetGet($offset)
175 return $this->get($offset);
179 * ArrayAccess implementation of offsetSet()
184 * @param mixed $offset
185 * @param mixed $value
188 public function offsetSet($offset, $value)
190 if ( ! isset($offset)) {
191 return $this->add($value);
193 return $this->set($offset, $value);
197 * ArrayAccess implementation of offsetUnset()
201 * @param mixed $offset
204 public function offsetUnset($offset)
206 return $this->remove($offset);
210 * Checks whether the collection contains a specific key/index.
212 * @param mixed $key The key to check for.
213 * @return boolean TRUE if the given key/index exists, FALSE otherwise.
215 public function containsKey($key)
217 return isset($this->_elements[$key]);
221 * Checks whether the given element is contained in the collection.
222 * Only element values are compared, not keys. The comparison of two elements
223 * is strict, that means not only the value but also the type must match.
224 * For objects this means reference equality.
226 * @param mixed $element
227 * @return boolean TRUE if the given element is contained in the collection,
230 public function contains($element)
232 foreach ($this->_elements as $collectionElement) {
233 if ($element === $collectionElement) {
242 * Tests for the existence of an element that satisfies the given predicate.
244 * @param Closure $p The predicate.
245 * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise.
247 public function exists(Closure $p)
249 foreach ($this->_elements as $key => $element) {
250 if ($p($key, $element)) {
258 * Searches for a given element and, if found, returns the corresponding key/index
259 * of that element. The comparison of two elements is strict, that means not
260 * only the value but also the type must match.
261 * For objects this means reference equality.
263 * @param mixed $element The element to search for.
264 * @return mixed The key/index of the element or FALSE if the element was not found.
266 public function indexOf($element)
268 return array_search($element, $this->_elements, true);
272 * Gets the element with the given key/index.
274 * @param mixed $key The key.
275 * @return mixed The element or NULL, if no element exists for the given key.
277 public function get($key)
279 if (isset($this->_elements[$key])) {
280 return $this->_elements[$key];
286 * Gets all keys/indexes of the collection elements.
290 public function getKeys()
292 return array_keys($this->_elements);
300 public function getValues()
302 return array_values($this->_elements);
306 * Returns the number of elements in the collection.
308 * Implementation of the Countable interface.
310 * @return integer The number of elements in the collection.
312 public function count()
314 return count($this->_elements);
318 * Adds/sets an element in the collection at the index / with the specified key.
320 * When the collection is a Map this is like put(key,value)/add(key,value).
321 * When the collection is a List this is like add(position,value).
324 * @param mixed $value
326 public function set($key, $value)
328 $this->_elements[$key] = $value;
332 * Adds an element to the collection.
334 * @param mixed $value
335 * @return boolean Always TRUE.
337 public function add($value)
339 $this->_elements[] = $value;
344 * Checks whether the collection is empty.
346 * Note: This is preferable over count() == 0.
348 * @return boolean TRUE if the collection is empty, FALSE otherwise.
350 public function isEmpty()
352 return ! $this->_elements;
356 * Gets an iterator for iterating over the elements in the collection.
358 * @return ArrayIterator
360 public function getIterator()
362 return new ArrayIterator($this->_elements);
366 * Applies the given function to each element in the collection and returns
367 * a new collection with the elements returned by the function.
369 * @param Closure $func
372 public function map(Closure $func)
374 return new static(array_map($func, $this->_elements));
378 * Returns all the elements of this collection that satisfy the predicate p.
379 * The order of the elements is preserved.
381 * @param Closure $p The predicate used for filtering.
382 * @return Collection A collection with the results of the filter operation.
384 public function filter(Closure $p)
386 return new static(array_filter($this->_elements, $p));
390 * Applies the given predicate p to all elements of this collection,
391 * returning true, if the predicate yields true for all elements.
393 * @param Closure $p The predicate.
394 * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.
396 public function forAll(Closure $p)
398 foreach ($this->_elements as $key => $element) {
399 if ( ! $p($key, $element)) {
408 * Partitions this collection in two collections according to a predicate.
409 * Keys are preserved in the resulting collections.
411 * @param Closure $p The predicate on which to partition.
412 * @return array An array with two elements. The first element contains the collection
413 * of elements where the predicate returned TRUE, the second element
414 * contains the collection of elements where the predicate returned FALSE.
416 public function partition(Closure $p)
418 $coll1 = $coll2 = array();
419 foreach ($this->_elements as $key => $element) {
420 if ($p($key, $element)) {
421 $coll1[$key] = $element;
423 $coll2[$key] = $element;
426 return array(new static($coll1), new static($coll2));
430 * Returns a string representation of this object.
434 public function __toString()
436 return __CLASS__ . '@' . spl_object_hash($this);
440 * Clears the collection.
442 public function clear()
444 $this->_elements = array();
448 * Extract a slice of $length elements starting at position $offset from the Collection.
450 * If $length is null it returns all elements from $offset to the end of the Collection.
451 * Keys have to be preserved by this method. Calling this method will only return the
452 * selected slice and NOT change the elements contained in the collection slice is called on.
458 public function slice($offset, $length = null)
460 return array_slice($this->_elements, $offset, $length, true);
464 * Select all elements from a selectable that match the criteria and
465 * return a new collection containing these elements.
467 * @param Criteria $criteria
470 public function matching(Criteria $criteria)
472 $expr = $criteria->getWhereExpression();
473 $filtered = $this->_elements;
476 $visitor = new ClosureExpressionVisitor();
477 $filter = $visitor->dispatch($expr);
478 $filtered = array_filter($filtered, $filter);
481 if ($orderings = $criteria->getOrderings()) {
483 foreach (array_reverse($orderings) as $field => $ordering) {
484 $next = ClosureExpressionVisitor::sortByField($field, $ordering == 'DESC' ? -1 : 1, $next);
487 usort($filtered, $next);
490 $offset = $criteria->getFirstResult();
491 $length = $criteria->getMaxResults();
493 if ($offset || $length) {
494 $filtered = array_slice($filtered, (int)$offset, $length);
497 return new static($filtered);