Skip to content

Commit

Permalink
Extract read-only part of Collection
Browse files Browse the repository at this point in the history
This allows us to mark the extracted interface as covariant, thus
allowing consumers to ensure through static analysis that a collection
will not be altered in a way that would no longer make it a collection
of items of a specific type.

Closes #298
  • Loading branch information
greg0ire committed Aug 24, 2022
1 parent 07d15c8 commit 7126702
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 114 deletions.
5 changes: 5 additions & 0 deletions docs/en/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ explicitly retrieve an iterator though ``getIterator()`` which can then be
used to iterate over the collection. You can not rely on the internal iterator
of the collection being at a certain position unless you explicitly positioned it before.

Methods that do not alter the collection or have template types
appearing in invariant or contravariant positions are not directly
defined in ``Doctrine\Common\Collections\Collection``, but are inherited
from the ``Doctrine\Common\Collections\ReadableCollection`` interface.

The methods available on the interface are:

add
Expand Down
116 changes: 2 additions & 114 deletions lib/Doctrine/Common/Collections/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

use ArrayAccess;
use Closure;
use Countable;
use IteratorAggregate;

/**
* The missing (SPL) Collection/Array/OrderedMap interface.
Expand All @@ -26,10 +24,10 @@
*
* @psalm-template TKey of array-key
* @psalm-template T
* @template-extends IteratorAggregate<TKey, T>
* @template-extends ReadableCollection<TKey, T>
* @template-extends ArrayAccess<TKey, T>
*/
interface Collection extends Countable, IteratorAggregate, ArrayAccess
interface Collection extends ReadableCollection, ArrayAccess
{
/**
* Adds an element at the end of the collection.
Expand Down Expand Up @@ -59,13 +57,6 @@ public function clear();
*/
public function contains($element);

/**
* Checks whether the collection is empty (contains no elements).
*
* @return bool TRUE if the collection is empty, FALSE otherwise.
*/
public function isEmpty();

/**
* Removes the element at the specified index from the collection.
*
Expand All @@ -87,46 +78,6 @@ public function remove($key);
*/
public function removeElement($element);

/**
* Checks whether the collection contains an element with the specified key/index.
*
* @param string|int $key The key/index to check for.
* @psalm-param TKey $key
*
* @return bool TRUE if the collection contains an element with the specified key/index,
* FALSE otherwise.
*/
public function containsKey($key);

/**
* Gets the element at the specified key/index.
*
* @param string|int $key The key/index of the element to retrieve.
* @psalm-param TKey $key
*
* @return mixed
* @psalm-return T|null
*/
public function get($key);

/**
* Gets all keys/indices of the collection.
*
* @return int[]|string[] The keys/indices of the collection, in the order of the corresponding
* elements in the collection.
* @psalm-return TKey[]
*/
public function getKeys();

/**
* Gets all values of the collection.
*
* @return mixed[] The values of all elements in the collection, in the
* order they appear in the collection.
* @psalm-return list<T>
*/
public function getValues();

/**
* Sets an element in the collection at the specified key/index.
*
Expand All @@ -139,54 +90,6 @@ public function getValues();
*/
public function set($key, $value);

/**
* Gets a native PHP array representation of the collection.
*
* @return mixed[]
* @psalm-return array<TKey,T>
*/
public function toArray();

/**
* Sets the internal iterator to the first element in the collection and returns this element.
*
* @return mixed
* @psalm-return T|false
*/
public function first();

/**
* Sets the internal iterator to the last element in the collection and returns this element.
*
* @return mixed
* @psalm-return T|false
*/
public function last();

/**
* Gets the key/index of the element at the current iterator position.
*
* @return int|string|null
* @psalm-return TKey|null
*/
public function key();

/**
* Gets the element of the collection at the current iterator position.
*
* @return mixed
* @psalm-return T|false
*/
public function current();

/**
* Moves the internal iterator position to the next element and returns this element.
*
* @return mixed
* @psalm-return T|false
*/
public function next();

/**
* Tests for the existence of an element that satisfies the given predicate.
*
Expand Down Expand Up @@ -258,19 +161,4 @@ public function partition(Closure $p);
* @psalm-return TKey|false
*/
public function indexOf($element);

/**
* Extracts a slice of $length elements starting at position $offset from the Collection.
*
* If $length is null it returns all elements from $offset to the end of the Collection.
* Keys have to be preserved by this method. Calling this method will only return the
* selected slice and NOT change the elements contained in the collection slice is called on.
*
* @param int $offset The offset to start from.
* @param int|null $length The maximum number of elements to return, or null for no limit.
*
* @return mixed[]
* @psalm-return array<TKey,T>
*/
public function slice($offset, $length = null);
}
124 changes: 124 additions & 0 deletions lib/Doctrine/Common/Collections/ReadableCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

namespace Doctrine\Common\Collections;

use Countable;
use IteratorAggregate;

/**
* @psalm-template TKey of array-key
* @template-covariant T
* @template-extends IteratorAggregate<TKey, T>
*/
interface ReadableCollection extends Countable, IteratorAggregate
{
/**
* Checks whether the collection is empty (contains no elements).
*
* @return bool TRUE if the collection is empty, FALSE otherwise.
*/
public function isEmpty();

/**
* Checks whether the collection contains an element with the specified key/index.
*
* @param string|int $key The key/index to check for.
* @psalm-param TKey $key
*
* @return bool TRUE if the collection contains an element with the specified key/index,
* FALSE otherwise.
*/
public function containsKey($key);

/**
* Gets the element at the specified key/index.
*
* @param string|int $key The key/index of the element to retrieve.
* @psalm-param TKey $key
*
* @return mixed
* @psalm-return T|null
*/
public function get($key);

/**
* Gets all keys/indices of the collection.
*
* @return int[]|string[] The keys/indices of the collection, in the order of the corresponding
* elements in the collection.
* @psalm-return TKey[]
*/
public function getKeys();

/**
* Gets all values of the collection.
*
* @return mixed[] The values of all elements in the collection, in the
* order they appear in the collection.
* @psalm-return list<T>
*/
public function getValues();

/**
* Gets a native PHP array representation of the collection.
*
* @return mixed[]
* @psalm-return array<TKey,T>
*/
public function toArray();

/**
* Sets the internal iterator to the first element in the collection and returns this element.
*
* @return mixed
* @psalm-return T|false
*/
public function first();

/**
* Sets the internal iterator to the last element in the collection and returns this element.
*
* @return mixed
* @psalm-return T|false
*/
public function last();

/**
* Gets the key/index of the element at the current iterator position.
*
* @return int|string|null
* @psalm-return TKey|null
*/
public function key();

/**
* Gets the element of the collection at the current iterator position.
*
* @return mixed
* @psalm-return T|false
*/
public function current();

/**
* Moves the internal iterator position to the next element and returns this element.
*
* @return mixed
* @psalm-return T|false
*/
public function next();

/**
* Extracts a slice of $length elements starting at position $offset from the Collection.
*
* If $length is null it returns all elements from $offset to the end of the Collection.
* Keys have to be preserved by this method. Calling this method will only return the
* selected slice and NOT change the elements contained in the collection slice is called on.
*
* @param int $offset The offset to start from.
* @param int|null $length The maximum number of elements to return, or null for no limit.
*
* @return mixed[]
* @psalm-return array<TKey,T>
*/
public function slice($offset, $length = null);
}

0 comments on commit 7126702

Please sign in to comment.