This commit is contained in:
Paolo A
2024-08-13 13:44:16 +00:00
parent 1bbb23088d
commit e796d76612
4001 changed files with 30101 additions and 40075 deletions

View File

@@ -18,8 +18,6 @@ use ArrayIterator;
use Traversable;
use function count;
use function serialize;
use function unserialize;
/**
* This class provides a basic implementation of `ArrayInterface`, to minimize
@@ -70,7 +68,7 @@ abstract class AbstractArray implements ArrayInterface
*
* @param array-key $offset The offset to check.
*/
public function offsetExists($offset): bool
public function offsetExists(mixed $offset): bool
{
return isset($this->data[$offset]);
}
@@ -82,13 +80,12 @@ abstract class AbstractArray implements ArrayInterface
*
* @param array-key $offset The offset for which a value should be returned.
*
* @return T|null the value stored at the offset, or null if the offset
* @return T the value stored at the offset, or null if the offset
* does not exist.
*/
#[\ReturnTypeWillChange] // phpcs:ignore
public function offsetGet($offset)
public function offsetGet(mixed $offset): mixed
{
return $this->data[$offset] ?? null;
return $this->data[$offset];
}
/**
@@ -96,12 +93,11 @@ abstract class AbstractArray implements ArrayInterface
*
* @link http://php.net/manual/en/arrayaccess.offsetset.php ArrayAccess::offsetSet()
*
* @param array-key|null $offset The offset to set. If `null`, the value may be
* set at a numerically-indexed offset.
* @param array-key | null $offset The offset to set. If `null`, the value
* may be set at a numerically-indexed offset.
* @param T $value The value to set at the given offset.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function offsetSet($offset, $value): void
public function offsetSet(mixed $offset, mixed $value): void
{
if ($offset === null) {
$this->data[] = $value;
@@ -117,25 +113,11 @@ abstract class AbstractArray implements ArrayInterface
*
* @param array-key $offset The offset to remove from the array.
*/
public function offsetUnset($offset): void
public function offsetUnset(mixed $offset): void
{
unset($this->data[$offset]);
}
/**
* Returns a serialized string representation of this array object.
*
* @deprecated The Serializable interface will go away in PHP 9.
*
* @link http://php.net/manual/en/serializable.serialize.php Serializable::serialize()
*
* @return string a PHP serialized string.
*/
public function serialize(): string
{
return serialize($this->data);
}
/**
* Returns data suitable for PHP serialization.
*
@@ -149,25 +131,6 @@ abstract class AbstractArray implements ArrayInterface
return $this->data;
}
/**
* Converts a serialized string representation into an instance object.
*
* @deprecated The Serializable interface will go away in PHP 9.
*
* @link http://php.net/manual/en/serializable.unserialize.php Serializable::unserialize()
*
* @param string $serialized A PHP serialized string to unserialize.
*
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
*/
public function unserialize($serialized): void
{
/** @var array<array-key, T> $data */
$data = unserialize($serialized, ['allowed_classes' => false]);
$this->data = $data;
}
/**
* Adds unserialized data to the object.
*
@@ -203,6 +166,6 @@ abstract class AbstractArray implements ArrayInterface
public function isEmpty(): bool
{
return count($this->data) === 0;
return $this->data === [];
}
}

View File

@@ -17,27 +17,27 @@ namespace Ramsey\Collection;
use Closure;
use Ramsey\Collection\Exception\CollectionMismatchException;
use Ramsey\Collection\Exception\InvalidArgumentException;
use Ramsey\Collection\Exception\InvalidSortOrderException;
use Ramsey\Collection\Exception\OutOfBoundsException;
use Ramsey\Collection\Exception\InvalidPropertyOrMethod;
use Ramsey\Collection\Exception\NoSuchElementException;
use Ramsey\Collection\Exception\UnsupportedOperationException;
use Ramsey\Collection\Tool\TypeTrait;
use Ramsey\Collection\Tool\ValueExtractorTrait;
use Ramsey\Collection\Tool\ValueToStringTrait;
use function array_filter;
use function array_key_first;
use function array_key_last;
use function array_map;
use function array_merge;
use function array_reduce;
use function array_search;
use function array_udiff;
use function array_uintersect;
use function current;
use function end;
use function in_array;
use function is_int;
use function is_object;
use function reset;
use function spl_object_id;
use function sprintf;
use function unserialize;
use function usort;
/**
@@ -55,27 +55,24 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
use ValueExtractorTrait;
/**
* @inheritDoc
* @throws InvalidArgumentException if $element is of the wrong type.
*/
public function add($element): bool
public function add(mixed $element): bool
{
$this[] = $element;
return true;
}
/**
* @inheritDoc
*/
public function contains($element, bool $strict = true): bool
public function contains(mixed $element, bool $strict = true): bool
{
return in_array($element, $this->data, $strict);
}
/**
* @inheritDoc
* @throws InvalidArgumentException if $element is of the wrong type.
*/
public function offsetSet($offset, $value): void
public function offsetSet(mixed $offset, mixed $value): void
{
if ($this->checkType($this->getType(), $value) === false) {
throw new InvalidArgumentException(
@@ -91,10 +88,7 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
}
}
/**
* @inheritDoc
*/
public function remove($element): bool
public function remove(mixed $element): bool
{
if (($position = array_search($element, $this->data, true)) !== false) {
unset($this[$position]);
@@ -106,6 +100,11 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
}
/**
* @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
* on the elements in this collection.
* @throws UnsupportedOperationException if unable to call column() on this
* collection.
*
* @inheritDoc
*/
public function column(string $propertyOrMethod): array
@@ -113,55 +112,55 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
$temp = [];
foreach ($this->data as $item) {
/** @var mixed $value */
$value = $this->extractValue($item, $propertyOrMethod);
/** @psalm-suppress MixedAssignment */
$temp[] = $value;
$temp[] = $this->extractValue($item, $propertyOrMethod);
}
return $temp;
}
/**
* @inheritDoc
* @return T
*
* @throws NoSuchElementException if this collection is empty.
*/
public function first()
public function first(): mixed
{
if ($this->isEmpty()) {
throw new OutOfBoundsException('Can\'t determine first item. Collection is empty');
$firstIndex = array_key_first($this->data);
if ($firstIndex === null) {
throw new NoSuchElementException('Can\'t determine first item. Collection is empty');
}
reset($this->data);
/** @var T $first */
$first = current($this->data);
return $first;
return $this->data[$firstIndex];
}
/**
* @inheritDoc
* @return T
*
* @throws NoSuchElementException if this collection is empty.
*/
public function last()
public function last(): mixed
{
if ($this->isEmpty()) {
throw new OutOfBoundsException('Can\'t determine last item. Collection is empty');
$lastIndex = array_key_last($this->data);
if ($lastIndex === null) {
throw new NoSuchElementException('Can\'t determine last item. Collection is empty');
}
/** @var T $item */
$item = end($this->data);
reset($this->data);
return $item;
return $this->data[$lastIndex];
}
public function sort(string $propertyOrMethod, string $order = self::SORT_ASC): CollectionInterface
/**
* @return CollectionInterface<T>
*
* @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
* on the elements in this collection.
* @throws UnsupportedOperationException if unable to call sort() on this
* collection.
*/
public function sort(?string $propertyOrMethod = null, Sort $order = Sort::Ascending): CollectionInterface
{
if (!in_array($order, [self::SORT_ASC, self::SORT_DESC], true)) {
throw new InvalidSortOrderException('Invalid sort order given: ' . $order);
}
$collection = clone $this;
usort(
@@ -170,20 +169,25 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
* @param T $a
* @param T $b
*/
function ($a, $b) use ($propertyOrMethod, $order): int {
function (mixed $a, mixed $b) use ($propertyOrMethod, $order): int {
/** @var mixed $aValue */
$aValue = $this->extractValue($a, $propertyOrMethod);
/** @var mixed $bValue */
$bValue = $this->extractValue($b, $propertyOrMethod);
return ($aValue <=> $bValue) * ($order === self::SORT_DESC ? -1 : 1);
return ($aValue <=> $bValue) * ($order === Sort::Descending ? -1 : 1);
},
);
return $collection;
}
/**
* @param callable(T): bool $callback A callable to use for filtering elements.
*
* @return CollectionInterface<T>
*/
public function filter(callable $callback): CollectionInterface
{
$collection = clone $this;
@@ -193,23 +197,66 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
}
/**
* {@inheritdoc}
* @return CollectionInterface<T>
*
* @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
* on the elements in this collection.
* @throws UnsupportedOperationException if unable to call where() on this
* collection.
*/
public function where(string $propertyOrMethod, $value): CollectionInterface
public function where(?string $propertyOrMethod, mixed $value): CollectionInterface
{
return $this->filter(function ($item) use ($propertyOrMethod, $value) {
/** @var mixed $accessorValue */
$accessorValue = $this->extractValue($item, $propertyOrMethod);
return $this->filter(
/**
* @param T $item
*/
function (mixed $item) use ($propertyOrMethod, $value): bool {
/** @var mixed $accessorValue */
$accessorValue = $this->extractValue($item, $propertyOrMethod);
return $accessorValue === $value;
});
return $accessorValue === $value;
},
);
}
/**
* @param callable(T): TCallbackReturn $callback A callable to apply to each
* item of the collection.
*
* @return CollectionInterface<TCallbackReturn>
*
* @template TCallbackReturn
*/
public function map(callable $callback): CollectionInterface
{
/** @var Collection<TCallbackReturn> */
return new Collection('mixed', array_map($callback, $this->data));
}
/**
* @param callable(TCarry, T): TCarry $callback A callable to apply to each
* item of the collection to reduce it to a single value.
* @param TCarry $initial This is the initial value provided to the callback.
*
* @return TCarry
*
* @template TCarry
*/
public function reduce(callable $callback, mixed $initial): mixed
{
/** @var TCarry */
return array_reduce($this->data, $callback, $initial);
}
/**
* @param CollectionInterface<T> $other The collection to check for divergent
* items.
*
* @return CollectionInterface<T>
*
* @throws CollectionMismatchException if the compared collections are of
* differing types.
*/
public function diff(CollectionInterface $other): CollectionInterface
{
$this->compareCollectionTypes($other);
@@ -226,6 +273,15 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
return $collection;
}
/**
* @param CollectionInterface<T> $other The collection to check for
* intersecting items.
*
* @return CollectionInterface<T>
*
* @throws CollectionMismatchException if the compared collections are of
* differing types.
*/
public function intersect(CollectionInterface $other): CollectionInterface
{
$this->compareCollectionTypes($other);
@@ -239,6 +295,15 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
return $collection;
}
/**
* @param CollectionInterface<T> ...$collections The collections to merge.
*
* @return CollectionInterface<T>
*
* @throws CollectionMismatchException if unable to merge any of the given
* collections or items within the given collections due to type
* mismatch errors.
*/
public function merge(CollectionInterface ...$collections): CollectionInterface
{
$mergedCollection = clone $this;
@@ -274,19 +339,10 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
return $mergedCollection;
}
/**
* @inheritDoc
*/
public function unserialize($serialized): void
{
/** @var array<array-key, T> $data */
$data = unserialize($serialized, ['allowed_classes' => [$this->getType()]]);
$this->data = $data;
}
/**
* @param CollectionInterface<T> $other
*
* @throws CollectionMismatchException
*/
private function compareCollectionTypes(CollectionInterface $other): void
{
@@ -307,7 +363,7 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
* @param T $a
* @param T $b
*/
function ($a, $b): int {
function (mixed $a, mixed $b): int {
// If the two values are object, we convert them to unique scalars.
// If the collection contains mixed values (unlikely) where some are objects
// and some are not, we leave them as they are.
@@ -327,15 +383,11 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
*/
private function getUniformType(CollectionInterface $collection): string
{
switch ($collection->getType()) {
case 'integer':
return 'int';
case 'boolean':
return 'bool';
case 'double':
return 'float';
default:
return $collection->getType();
}
return match ($collection->getType()) {
'integer' => 'int',
'boolean' => 'bool',
'double' => 'float',
default => $collection->getType(),
};
}
}

View File

@@ -24,10 +24,7 @@ namespace Ramsey\Collection;
*/
abstract class AbstractSet extends AbstractCollection
{
/**
* @inheritDoc
*/
public function add($element): bool
public function add(mixed $element): bool
{
if ($this->contains($element)) {
return false;
@@ -36,10 +33,7 @@ abstract class AbstractSet extends AbstractCollection
return parent::add($element);
}
/**
* @inheritDoc
*/
public function offsetSet($offset, $value): void
public function offsetSet(mixed $offset, mixed $value): void
{
if ($this->contains($value)) {
return;

View File

@@ -17,7 +17,6 @@ namespace Ramsey\Collection;
use ArrayAccess;
use Countable;
use IteratorAggregate;
use Serializable;
/**
* `ArrayInterface` provides traversable array functionality to data types.
@@ -29,8 +28,7 @@ use Serializable;
interface ArrayInterface extends
ArrayAccess,
Countable,
IteratorAggregate,
Serializable
IteratorAggregate
{
/**
* Removes all items from this array.

View File

@@ -75,25 +75,16 @@ namespace Ramsey\Collection;
*/
class Collection extends AbstractCollection
{
/**
* The type of elements stored in this collection.
*
* A collection's type is immutable once it is set. For this reason, this
* property is set private.
*/
private string $collectionType;
/**
* Constructs a collection object of the specified type, optionally with the
* specified data.
*
* @param string $collectionType The type (FQCN) associated with this
* @param string $collectionType The type or class name associated with this
* collection.
* @param array<array-key, T> $data The initial items to store in the collection.
*/
public function __construct(string $collectionType, array $data = [])
public function __construct(private readonly string $collectionType, array $data = [])
{
$this->collectionType = $collectionType;
parent::__construct($data);
}

View File

@@ -14,8 +14,14 @@ declare(strict_types=1);
namespace Ramsey\Collection;
use Ramsey\Collection\Exception\CollectionMismatchException;
use Ramsey\Collection\Exception\InvalidArgumentException;
use Ramsey\Collection\Exception\InvalidPropertyOrMethod;
use Ramsey\Collection\Exception\NoSuchElementException;
use Ramsey\Collection\Exception\UnsupportedOperationException;
/**
* A collection represents a group of objects, known as its elements.
* A collection represents a group of values, known as its elements.
*
* Some collections allow duplicate elements and others do not. Some are ordered
* and others unordered.
@@ -25,16 +31,6 @@ namespace Ramsey\Collection;
*/
interface CollectionInterface extends ArrayInterface
{
/**
* Ascending sort type.
*/
public const SORT_ASC = 'asc';
/**
* Descending sort type.
*/
public const SORT_DESC = 'desc';
/**
* Ensures that this collection contains the specified element (optional
* operation).
@@ -58,9 +54,11 @@ interface CollectionInterface extends ArrayInterface
* @param T $element The element to add to the collection.
*
* @return bool `true` if this collection changed as a result of the call.
*
* @throws InvalidArgumentException if the collection refuses to add the
* $element for any reason other than that it already contains the element.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function add($element): bool;
public function add(mixed $element): bool;
/**
* Returns `true` if this collection contains the specified element.
@@ -68,8 +66,7 @@ interface CollectionInterface extends ArrayInterface
* @param T $element The element to check whether the collection contains.
* @param bool $strict Whether to perform a strict type check on the value.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function contains($element, bool $strict = true): bool;
public function contains(mixed $element, bool $strict = true): bool;
/**
* Returns the type associated with this collection.
@@ -84,15 +81,20 @@ interface CollectionInterface extends ArrayInterface
*
* @return bool `true` if an element was removed as a result of this call.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function remove($element): bool;
public function remove(mixed $element): bool;
/**
* Returns the values from the given property or method.
* Returns the values from the given property, method, or array key.
*
* @param string $propertyOrMethod The property or method name to filter by.
* @param string $propertyOrMethod The name of the property, method, or
* array key to evaluate and return.
*
* @return list<mixed>
* @return array<int, mixed>
*
* @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
* on the elements in this collection.
* @throws UnsupportedOperationException if unable to call column() on this
* collection.
*/
public function column(string $propertyOrMethod): array;
@@ -100,29 +102,41 @@ interface CollectionInterface extends ArrayInterface
* Returns the first item of the collection.
*
* @return T
*
* @throws NoSuchElementException if this collection is empty.
*/
public function first();
public function first(): mixed;
/**
* Returns the last item of the collection.
*
* @return T
*
* @throws NoSuchElementException if this collection is empty.
*/
public function last();
public function last(): mixed;
/**
* Sort the collection by a property or method with the given sort order.
* Sort the collection by a property, method, or array key with the given
* sort order.
*
* If $propertyOrMethod is `null`, this will sort by comparing each element.
*
* This will always leave the original collection untouched and will return
* a new one.
*
* @param string $propertyOrMethod The property or method to sort by.
* @param string $order The sort order for the resulting collection (one of
* this interface's `SORT_*` constants).
* @param string | null $propertyOrMethod The property, method, or array key
* to sort by.
* @param Sort $order The sort order for the resulting collection.
*
* @return CollectionInterface<T>
*
* @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
* on the elements in this collection.
* @throws UnsupportedOperationException if unable to call sort() on this
* collection.
*/
public function sort(string $propertyOrMethod, string $order = self::SORT_ASC): self;
public function sort(?string $propertyOrMethod = null, Sort $order = Sort::Ascending): self;
/**
* Filter out items of the collection which don't match the criteria of
@@ -134,25 +148,31 @@ interface CollectionInterface extends ArrayInterface
* See the {@link http://php.net/manual/en/function.array-filter.php PHP array_filter() documentation}
* for examples of how the `$callback` parameter works.
*
* @param callable(T):bool $callback A callable to use for filtering elements.
* @param callable(T): bool $callback A callable to use for filtering elements.
*
* @return CollectionInterface<T>
*/
public function filter(callable $callback): self;
/**
* Create a new collection where items match the criteria of given callback.
* Create a new collection where the result of the given property, method,
* or array key of each item in the collection equals the given value.
*
* This will always leave the original collection untouched and will return
* a new one.
*
* @param string $propertyOrMethod The property or method to evaluate.
* @param string | null $propertyOrMethod The property, method, or array key
* to evaluate. If `null`, the element itself is compared to $value.
* @param mixed $value The value to match.
*
* @return CollectionInterface<T>
*
* @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
* on the elements in this collection.
* @throws UnsupportedOperationException if unable to call where() on this
* collection.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function where(string $propertyOrMethod, $value): self;
public function where(?string $propertyOrMethod, mixed $value): self;
/**
* Apply a given callback method on each item of the collection.
@@ -164,7 +184,7 @@ interface CollectionInterface extends ArrayInterface
* See the {@link http://php.net/manual/en/function.array-map.php PHP array_map() documentation}
* for examples of how the `$callback` parameter works.
*
* @param callable(T):TCallbackReturn $callback A callable to apply to each
* @param callable(T): TCallbackReturn $callback A callable to apply to each
* item of the collection.
*
* @return CollectionInterface<TCallbackReturn>
@@ -173,6 +193,23 @@ interface CollectionInterface extends ArrayInterface
*/
public function map(callable $callback): self;
/**
* Apply a given callback method on each item of the collection
* to reduce it to a single value.
*
* See the {@link http://php.net/manual/en/function.array-reduce.php PHP array_reduce() documentation}
* for examples of how the `$callback` and `$initial` parameters work.
*
* @param callable(TCarry, T): TCarry $callback A callable to apply to each
* item of the collection to reduce it to a single value.
* @param TCarry $initial This is the initial value provided to the callback.
*
* @return TCarry
*
* @template TCarry
*/
public function reduce(callable $callback, mixed $initial): mixed;
/**
* Create a new collection with divergent items between current and given
* collection.
@@ -181,6 +218,9 @@ interface CollectionInterface extends ArrayInterface
* items.
*
* @return CollectionInterface<T>
*
* @throws CollectionMismatchException if the compared collections are of
* differing types.
*/
public function diff(CollectionInterface $other): self;
@@ -192,6 +232,9 @@ interface CollectionInterface extends ArrayInterface
* intersecting items.
*
* @return CollectionInterface<T>
*
* @throws CollectionMismatchException if the compared collections are of
* differing types.
*/
public function intersect(CollectionInterface $other): self;
@@ -201,6 +244,10 @@ interface CollectionInterface extends ArrayInterface
* @param CollectionInterface<T> ...$collections The collections to merge.
*
* @return CollectionInterface<T>
*
* @throws CollectionMismatchException if unable to merge any of the given
* collections or items within the given collections due to type
* mismatch errors.
*/
public function merge(CollectionInterface ...$collections): self;
}

View File

@@ -17,6 +17,10 @@ namespace Ramsey\Collection;
use Ramsey\Collection\Exception\InvalidArgumentException;
use Ramsey\Collection\Exception\NoSuchElementException;
use function array_key_last;
use function array_pop;
use function array_unshift;
/**
* This class provides a basic implementation of `DoubleEndedQueueInterface`, to
* minimize the effort required to implement this interface.
@@ -28,33 +32,21 @@ use Ramsey\Collection\Exception\NoSuchElementException;
class DoubleEndedQueue extends Queue implements DoubleEndedQueueInterface
{
/**
* Index of the last element in the queue.
* Constructs a double-ended queue (dequeue) object of the specified type,
* optionally with the specified data.
*
* @param string $queueType The type or class name associated with this dequeue.
* @param array<array-key, T> $data The initial items to store in the dequeue.
*/
private int $tail = -1;
/**
* @inheritDoc
*/
public function offsetSet($offset, $value): void
public function __construct(private readonly string $queueType, array $data = [])
{
if ($this->checkType($this->getType(), $value) === false) {
throw new InvalidArgumentException(
'Value must be of type ' . $this->getType() . '; value is '
. $this->toolValueToString($value),
);
}
$this->tail++;
$this->data[$this->tail] = $value;
parent::__construct($this->queueType, $data);
}
/**
* @throws InvalidArgumentException if $element is of the wrong type
*
* @inheritDoc
*/
public function addFirst($element): bool
public function addFirst(mixed $element): bool
{
if ($this->checkType($this->getType(), $element) === false) {
throw new InvalidArgumentException(
@@ -63,125 +55,112 @@ class DoubleEndedQueue extends Queue implements DoubleEndedQueueInterface
);
}
$this->index--;
$this->data[$this->index] = $element;
array_unshift($this->data, $element);
return true;
}
/**
* @inheritDoc
* @throws InvalidArgumentException if $element is of the wrong type
*/
public function addLast($element): bool
public function addLast(mixed $element): bool
{
return $this->add($element);
}
/**
* @inheritDoc
*/
public function offerFirst($element): bool
public function offerFirst(mixed $element): bool
{
try {
return $this->addFirst($element);
} catch (InvalidArgumentException $e) {
} catch (InvalidArgumentException) {
return false;
}
}
/**
* @inheritDoc
*/
public function offerLast($element): bool
public function offerLast(mixed $element): bool
{
return $this->offer($element);
}
/**
* @inheritDoc
* @return T the first element in this queue.
*
* @throws NoSuchElementException if the queue is empty
*/
public function removeFirst()
public function removeFirst(): mixed
{
return $this->remove();
}
/**
* @inheritDoc
* @return T the last element in this queue.
*
* @throws NoSuchElementException if this queue is empty.
*/
public function removeLast()
public function removeLast(): mixed
{
$tail = $this->pollLast();
if ($tail === null) {
throw new NoSuchElementException('Can\'t return element from Queue. Queue is empty.');
}
return $tail;
return $this->pollLast() ?? throw new NoSuchElementException(
'Can\'t return element from Queue. Queue is empty.',
);
}
/**
* @inheritDoc
* @return T | null the head of this queue, or `null` if this queue is empty.
*/
public function pollFirst()
public function pollFirst(): mixed
{
return $this->poll();
}
/**
* @inheritDoc
* @return T | null the tail of this queue, or `null` if this queue is empty.
*/
public function pollLast()
public function pollLast(): mixed
{
if ($this->count() === 0) {
return null;
}
$tail = $this[$this->tail];
unset($this[$this->tail]);
$this->tail--;
return $tail;
return array_pop($this->data);
}
/**
* @inheritDoc
* @return T the head of this queue.
*
* @throws NoSuchElementException if this queue is empty.
*/
public function firstElement()
public function firstElement(): mixed
{
return $this->element();
}
/**
* @inheritDoc
* @return T the tail of this queue.
*
* @throws NoSuchElementException if this queue is empty.
*/
public function lastElement()
public function lastElement(): mixed
{
if ($this->count() === 0) {
throw new NoSuchElementException('Can\'t return element from Queue. Queue is empty.');
}
return $this->data[$this->tail];
return $this->peekLast() ?? throw new NoSuchElementException(
'Can\'t return element from Queue. Queue is empty.',
);
}
/**
* @inheritDoc
* @return T | null the head of this queue, or `null` if this queue is empty.
*/
public function peekFirst()
public function peekFirst(): mixed
{
return $this->peek();
}
/**
* @inheritDoc
* @return T | null the tail of this queue, or `null` if this queue is empty.
*/
public function peekLast()
public function peekLast(): mixed
{
if ($this->count() === 0) {
$lastIndex = array_key_last($this->data);
if ($lastIndex === null) {
return null;
}
return $this->data[$this->tail];
return $this->data[$lastIndex];
}
}

View File

@@ -181,8 +181,7 @@ interface DoubleEndedQueueInterface extends QueueInterface
* Implementations should use a more-specific exception that extends
* `\RuntimeException`.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function addFirst($element): bool;
public function addFirst(mixed $element): bool;
/**
* Inserts the specified element at the end of this queue if it is possible
@@ -202,8 +201,7 @@ interface DoubleEndedQueueInterface extends QueueInterface
* Implementations should use a more-specific exception that extends
* `\RuntimeException`.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function addLast($element): bool;
public function addLast(mixed $element): bool;
/**
* Inserts the specified element at the front of this queue if it is
@@ -217,8 +215,7 @@ interface DoubleEndedQueueInterface extends QueueInterface
*
* @return bool `true` if the element was added to this queue, else `false`.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function offerFirst($element): bool;
public function offerFirst(mixed $element): bool;
/**
* Inserts the specified element at the end of this queue if it is possible
@@ -232,8 +229,7 @@ interface DoubleEndedQueueInterface extends QueueInterface
*
* @return bool `true` if the element was added to this queue, else `false`.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function offerLast($element): bool;
public function offerLast(mixed $element): bool;
/**
* Retrieves and removes the head of this queue.
@@ -245,7 +241,7 @@ interface DoubleEndedQueueInterface extends QueueInterface
*
* @throws NoSuchElementException if this queue is empty.
*/
public function removeFirst();
public function removeFirst(): mixed;
/**
* Retrieves and removes the tail of this queue.
@@ -257,23 +253,23 @@ interface DoubleEndedQueueInterface extends QueueInterface
*
* @throws NoSuchElementException if this queue is empty.
*/
public function removeLast();
public function removeLast(): mixed;
/**
* Retrieves and removes the head of this queue, or returns `null` if this
* queue is empty.
*
* @return T|null the head of this queue, or `null` if this queue is empty.
* @return T | null the head of this queue, or `null` if this queue is empty.
*/
public function pollFirst();
public function pollFirst(): mixed;
/**
* Retrieves and removes the tail of this queue, or returns `null` if this
* queue is empty.
*
* @return T|null the tail of this queue, or `null` if this queue is empty.
* @return T | null the tail of this queue, or `null` if this queue is empty.
*/
public function pollLast();
public function pollLast(): mixed;
/**
* Retrieves, but does not remove, the head of this queue.
@@ -285,7 +281,7 @@ interface DoubleEndedQueueInterface extends QueueInterface
*
* @throws NoSuchElementException if this queue is empty.
*/
public function firstElement();
public function firstElement(): mixed;
/**
* Retrieves, but does not remove, the tail of this queue.
@@ -297,21 +293,21 @@ interface DoubleEndedQueueInterface extends QueueInterface
*
* @throws NoSuchElementException if this queue is empty.
*/
public function lastElement();
public function lastElement(): mixed;
/**
* Retrieves, but does not remove, the head of this queue, or returns `null`
* if this queue is empty.
*
* @return T|null the head of this queue, or `null` if this queue is empty.
* @return T | null the head of this queue, or `null` if this queue is empty.
*/
public function peekFirst();
public function peekFirst(): mixed;
/**
* Retrieves, but does not remove, the tail of this queue, or returns `null`
* if this queue is empty.
*
* @return T|null the tail of this queue, or `null` if this queue is empty.
* @return T | null the tail of this queue, or `null` if this queue is empty.
*/
public function peekLast();
public function peekLast(): mixed;
}

View File

@@ -19,6 +19,6 @@ use RuntimeException;
/**
* Thrown when attempting to operate on collections of differing types.
*/
class CollectionMismatchException extends RuntimeException
class CollectionMismatchException extends RuntimeException implements CollectionException
{
}

View File

@@ -14,9 +14,11 @@ declare(strict_types=1);
namespace Ramsey\Collection\Exception;
use InvalidArgumentException as PhpInvalidArgumentException;
/**
* Thrown to indicate an argument is not of the expected type.
*/
class InvalidArgumentException extends \InvalidArgumentException
class InvalidArgumentException extends PhpInvalidArgumentException implements CollectionException
{
}

View File

@@ -1,24 +0,0 @@
<?php
/**
* This file is part of the ramsey/collection library
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
* @license http://opensource.org/licenses/MIT MIT
*/
declare(strict_types=1);
namespace Ramsey\Collection\Exception;
use RuntimeException;
/**
* Thrown when attempting to use a sort order that is not recognized.
*/
class InvalidSortOrderException extends RuntimeException
{
}

View File

@@ -19,6 +19,6 @@ use RuntimeException;
/**
* Thrown when attempting to access an element that does not exist.
*/
class NoSuchElementException extends RuntimeException
class NoSuchElementException extends RuntimeException implements CollectionException
{
}

View File

@@ -14,9 +14,11 @@ declare(strict_types=1);
namespace Ramsey\Collection\Exception;
use OutOfBoundsException as PhpOutOfBoundsException;
/**
* Thrown when attempting to access an element out of the range of the collection.
*/
class OutOfBoundsException extends \OutOfBoundsException
class OutOfBoundsException extends PhpOutOfBoundsException implements CollectionException
{
}

View File

@@ -19,6 +19,6 @@ use RuntimeException;
/**
* Thrown to indicate that the requested operation is not supported.
*/
class UnsupportedOperationException extends RuntimeException
class UnsupportedOperationException extends RuntimeException implements CollectionException
{
}

View File

@@ -1,24 +0,0 @@
<?php
/**
* This file is part of the ramsey/collection library
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
* @license http://opensource.org/licenses/MIT MIT
*/
declare(strict_types=1);
namespace Ramsey\Collection\Exception;
use RuntimeException;
/**
* Thrown when attempting to extract a value for a method or property that does not exist.
*/
class ValueExtractionException extends RuntimeException
{
}

View File

@@ -16,6 +16,7 @@ namespace Ramsey\Collection\Map;
use Ramsey\Collection\AbstractArray;
use Ramsey\Collection\Exception\InvalidArgumentException;
use Traversable;
use function array_key_exists;
use function array_keys;
@@ -26,16 +27,37 @@ use function var_export;
* This class provides a basic implementation of `MapInterface`, to minimize the
* effort required to implement this interface.
*
* @template K of array-key
* @template T
* @extends AbstractArray<T>
* @implements MapInterface<T>
* @implements MapInterface<K, T>
*/
abstract class AbstractMap extends AbstractArray implements MapInterface
{
/**
* @inheritDoc
* @param array<K, T> $data The initial items to add to this map.
*/
public function offsetSet($offset, $value): void
public function __construct(array $data = [])
{
parent::__construct($data);
}
/**
* @return Traversable<K, T>
*/
public function getIterator(): Traversable
{
return parent::getIterator();
}
/**
* @param K $offset The offset to set
* @param T $value The value to set at the given offset.
*
* @inheritDoc
* @psalm-suppress MoreSpecificImplementedParamType,DocblockTypeContradiction
*/
public function offsetSet(mixed $offset, mixed $value): void
{
if ($offset === null) {
throw new InvalidArgumentException(
@@ -47,18 +69,12 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
$this->data[$offset] = $value;
}
/**
* @inheritDoc
*/
public function containsKey($key): bool
public function containsKey(int | string $key): bool
{
return array_key_exists($key, $this->data);
}
/**
* @inheritDoc
*/
public function containsValue($value): bool
public function containsValue(mixed $value): bool
{
return in_array($value, $this->data, true);
}
@@ -72,21 +88,24 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
}
/**
* @inheritDoc
* @param K $key The key to return from the map.
* @param T | null $defaultValue The default value to use if `$key` is not found.
*
* @return T | null the value or `null` if the key could not be found.
*/
public function get($key, $defaultValue = null)
public function get(int | string $key, mixed $defaultValue = null): mixed
{
if (!$this->containsKey($key)) {
return $defaultValue;
}
return $this[$key];
return $this[$key] ?? $defaultValue;
}
/**
* @inheritDoc
* @param K $key The key to put or replace in the map.
* @param T $value The value to store at `$key`.
*
* @return T | null the previous value associated with key, or `null` if
* there was no mapping for `$key`.
*/
public function put($key, $value)
public function put(int | string $key, mixed $value): mixed
{
$previousValue = $this->get($key);
$this[$key] = $value;
@@ -95,9 +114,13 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
}
/**
* @inheritDoc
* @param K $key The key to put in the map.
* @param T $value The value to store at `$key`.
*
* @return T | null the previous value associated with key, or `null` if
* there was no mapping for `$key`.
*/
public function putIfAbsent($key, $value)
public function putIfAbsent(int | string $key, mixed $value): mixed
{
$currentValue = $this->get($key);
@@ -109,9 +132,12 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
}
/**
* @inheritDoc
* @param K $key The key to remove from the map.
*
* @return T | null the previous value associated with key, or `null` if
* there was no mapping for `$key`.
*/
public function remove($key)
public function remove(int | string $key): mixed
{
$previousValue = $this->get($key);
unset($this[$key]);
@@ -119,10 +145,7 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
return $previousValue;
}
/**
* @inheritDoc
*/
public function removeIf($key, $value): bool
public function removeIf(int | string $key, mixed $value): bool
{
if ($this->get($key) === $value) {
unset($this[$key]);
@@ -134,9 +157,13 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
}
/**
* @inheritDoc
* @param K $key The key to replace.
* @param T $value The value to set at `$key`.
*
* @return T | null the previous value associated with key, or `null` if
* there was no mapping for `$key`.
*/
public function replace($key, $value)
public function replace(int | string $key, mixed $value): mixed
{
$currentValue = $this->get($key);
@@ -147,10 +174,7 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
return $currentValue;
}
/**
* @inheritDoc
*/
public function replaceIf($key, $oldValue, $newValue): bool
public function replaceIf(int | string $key, mixed $oldValue, mixed $newValue): bool
{
if ($this->get($key) === $oldValue) {
$this[$key] = $newValue;
@@ -160,4 +184,20 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
return false;
}
/**
* @return array<K, T>
*/
public function __serialize(): array
{
return parent::__serialize();
}
/**
* @return array<K, T>
*/
public function toArray(): array
{
return parent::toArray();
}
}

View File

@@ -18,16 +18,14 @@ use Ramsey\Collection\Exception\InvalidArgumentException;
use Ramsey\Collection\Tool\TypeTrait;
use Ramsey\Collection\Tool\ValueToStringTrait;
use function var_export;
/**
* This class provides a basic implementation of `TypedMapInterface`, to
* minimize the effort required to implement this interface.
*
* @template K of array-key
* @template T
* @extends AbstractMap<T>
* @implements TypedMapInterface<T>
* @extends AbstractMap<K, T>
* @implements TypedMapInterface<K, T>
*/
abstract class AbstractTypedMap extends AbstractMap implements TypedMapInterface
{
@@ -35,20 +33,14 @@ abstract class AbstractTypedMap extends AbstractMap implements TypedMapInterface
use ValueToStringTrait;
/**
* @param K|null $offset
* @param K $offset
* @param T $value
*
* @inheritDoc
* @psalm-suppress MoreSpecificImplementedParamType
*/
public function offsetSet($offset, $value): void
public function offsetSet(mixed $offset, mixed $value): void
{
if ($offset === null) {
throw new InvalidArgumentException(
'Map elements are key/value pairs; a key must be provided for '
. 'value ' . var_export($value, true),
);
}
if ($this->checkType($this->getKeyType(), $offset) === false) {
throw new InvalidArgumentException(
'Key must be of type ' . $this->getKeyType() . '; key is '

View File

@@ -17,8 +17,7 @@ namespace Ramsey\Collection\Map;
/**
* `AssociativeArrayMap` represents a standard associative array object.
*
* @template T
* @extends AbstractMap<T>
* @extends AbstractMap<string, mixed>
*/
class AssociativeArrayMap extends AbstractMap
{

View File

@@ -21,6 +21,7 @@ use Ramsey\Collection\ArrayInterface;
*
* A map cannot contain duplicate keys; each key can map to at most one value.
*
* @template K of array-key
* @template T
* @extends ArrayInterface<T>
*/
@@ -29,9 +30,9 @@ interface MapInterface extends ArrayInterface
/**
* Returns `true` if this map contains a mapping for the specified key.
*
* @param array-key $key The key to check in the map.
* @param K $key The key to check in the map.
*/
public function containsKey($key): bool;
public function containsKey(int | string $key): bool;
/**
* Returns `true` if this map maps one or more keys to the specified value.
@@ -40,13 +41,12 @@ interface MapInterface extends ArrayInterface
*
* @param T $value The value to check in the map.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function containsValue($value): bool;
public function containsValue(mixed $value): bool;
/**
* Return an array of the keys contained in this map.
*
* @return list<array-key>
* @return list<K>
*/
public function keys(): array;
@@ -55,13 +55,12 @@ interface MapInterface extends ArrayInterface
* map contains no mapping for the key, or (optionally) `$defaultValue` if
* this map contains no mapping for the key.
*
* @param array-key $key The key to return from the map.
* @param T|null $defaultValue The default value to use if `$key` is not found.
* @param K $key The key to return from the map.
* @param T | null $defaultValue The default value to use if `$key` is not found.
*
* @return T|null the value or `null` if the key could not be found.
* @return T | null the value or `null` if the key could not be found.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function get($key, $defaultValue = null);
public function get(int | string $key, mixed $defaultValue = null): mixed;
/**
* Associates the specified value with the specified key in this map.
@@ -69,14 +68,13 @@ interface MapInterface extends ArrayInterface
* If the map previously contained a mapping for the key, the old value is
* replaced by the specified value.
*
* @param array-key $key The key to put or replace in the map.
* @param K $key The key to put or replace in the map.
* @param T $value The value to store at `$key`.
*
* @return T|null the previous value associated with key, or `null` if
* @return T | null the previous value associated with key, or `null` if
* there was no mapping for `$key`.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function put($key, $value);
public function put(int | string $key, mixed $value): mixed;
/**
* Associates the specified value with the specified key in this map only if
@@ -85,25 +83,23 @@ interface MapInterface extends ArrayInterface
* If there is already a value associated with `$key`, this returns that
* value without replacing it.
*
* @param array-key $key The key to put in the map.
* @param K $key The key to put in the map.
* @param T $value The value to store at `$key`.
*
* @return T|null the previous value associated with key, or `null` if
* @return T | null the previous value associated with key, or `null` if
* there was no mapping for `$key`.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function putIfAbsent($key, $value);
public function putIfAbsent(int | string $key, mixed $value): mixed;
/**
* Removes the mapping for a key from this map if it is present.
*
* @param array-key $key The key to remove from the map.
* @param K $key The key to remove from the map.
*
* @return T|null the previous value associated with key, or `null` if
* @return T | null the previous value associated with key, or `null` if
* there was no mapping for `$key`.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function remove($key);
public function remove(int | string $key): mixed;
/**
* Removes the entry for the specified key only if it is currently mapped to
@@ -111,26 +107,24 @@ interface MapInterface extends ArrayInterface
*
* This performs a strict type check on the value.
*
* @param array-key $key The key to remove from the map.
* @param K $key The key to remove from the map.
* @param T $value The value to match.
*
* @return bool true if the value was removed.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function removeIf($key, $value): bool;
public function removeIf(int | string $key, mixed $value): bool;
/**
* Replaces the entry for the specified key only if it is currently mapped
* to some value.
*
* @param array-key $key The key to replace.
* @param K $key The key to replace.
* @param T $value The value to set at `$key`.
*
* @return T|null the previous value associated with key, or `null` if
* @return T | null the previous value associated with key, or `null` if
* there was no mapping for `$key`.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function replace($key, $value);
public function replace(int | string $key, mixed $value): mixed;
/**
* Replaces the entry for the specified key only if currently mapped to the
@@ -138,12 +132,11 @@ interface MapInterface extends ArrayInterface
*
* This performs a strict type check on the value.
*
* @param array-key $key The key to remove from the map.
* @param K $key The key to remove from the map.
* @param T $oldValue The value to match.
* @param T $newValue The value to use as a replacement.
*
* @return bool true if the value was replaced.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function replaceIf($key, $oldValue, $newValue): bool;
public function replaceIf(int | string $key, mixed $oldValue, mixed $newValue): bool;
}

View File

@@ -21,13 +21,12 @@ use Ramsey\Collection\Tool\ValueToStringTrait;
use function array_combine;
use function array_key_exists;
use function is_int;
use function var_export;
/**
* `NamedParameterMap` represents a mapping of values to a set of named keys
* that may optionally be typed
*
* @extends AbstractMap<mixed>
* @extends AbstractMap<string, mixed>
*/
class NamedParameterMap extends AbstractMap
{
@@ -39,13 +38,13 @@ class NamedParameterMap extends AbstractMap
*
* @var array<string, string>
*/
protected array $namedParameters;
private readonly array $namedParameters;
/**
* Constructs a new `NamedParameterMap`.
*
* @param array<array-key, string> $namedParameters The named parameters defined for this map.
* @param array<array-key, mixed> $data An initial set of data to set on this map.
* @param array<string, mixed> $data An initial set of data to set on this map.
*/
public function __construct(array $namedParameters, array $data = [])
{
@@ -63,22 +62,12 @@ class NamedParameterMap extends AbstractMap
return $this->namedParameters;
}
/**
* @inheritDoc
*/
public function offsetSet($offset, $value): void
public function offsetSet(mixed $offset, mixed $value): void
{
if ($offset === null) {
throw new InvalidArgumentException(
'Map elements are key/value pairs; a key must be provided for '
. 'value ' . var_export($value, true),
);
}
if (!array_key_exists($offset, $this->namedParameters)) {
throw new InvalidArgumentException(
'Attempting to set value for unconfigured parameter \''
. $offset . '\'',
. $this->toolValueToString($offset) . '\'',
);
}

View File

@@ -14,8 +14,6 @@ declare(strict_types=1);
namespace Ramsey\Collection\Map;
use Ramsey\Collection\Tool\TypeTrait;
/**
* A `TypedMap` represents a map of elements where key and value are typed.
*
@@ -86,24 +84,6 @@ use Ramsey\Collection\Tool\TypeTrait;
*/
class TypedMap extends AbstractTypedMap
{
use TypeTrait;
/**
* The data type of keys stored in this collection.
*
* A map key's type is immutable once it is set. For this reason, this
* property is set private.
*/
private string $keyType;
/**
* The data type of values stored in this collection.
*
* A map value's type is immutable once it is set. For this reason, this
* property is set private.
*/
private string $valueType;
/**
* Constructs a map object of the specified key and value types,
* optionally with the specified data.
@@ -112,11 +92,11 @@ class TypedMap extends AbstractTypedMap
* @param string $valueType The data type of the map's values.
* @param array<K, T> $data The initial data to set for this map.
*/
public function __construct(string $keyType, string $valueType, array $data = [])
{
$this->keyType = $keyType;
$this->valueType = $valueType;
public function __construct(
private readonly string $keyType,
private readonly string $valueType,
array $data = [],
) {
parent::__construct($data);
}

View File

@@ -18,8 +18,9 @@ namespace Ramsey\Collection\Map;
* A `TypedMapInterface` represents a map of elements where key and value are
* typed.
*
* @template K of array-key
* @template T
* @extends MapInterface<T>
* @extends MapInterface<K, T>
*/
interface TypedMapInterface extends MapInterface
{

View File

@@ -19,6 +19,8 @@ use Ramsey\Collection\Exception\NoSuchElementException;
use Ramsey\Collection\Tool\TypeTrait;
use Ramsey\Collection\Tool\ValueToStringTrait;
use function array_key_first;
/**
* This class provides a basic implementation of `QueueInterface`, to minimize
* the effort required to implement this interface.
@@ -32,29 +34,15 @@ class Queue extends AbstractArray implements QueueInterface
use TypeTrait;
use ValueToStringTrait;
/**
* The type of elements stored in this queue.
*
* A queue's type is immutable once it is set. For this reason, this
* property is set private.
*/
private string $queueType;
/**
* The index of the head of the queue.
*/
protected int $index = 0;
/**
* Constructs a queue object of the specified type, optionally with the
* specified data.
*
* @param string $queueType The type (FQCN) associated with this queue.
* @param array<array-key, T> $data The initial items to store in the collection.
* @param string $queueType The type or class name associated with this queue.
* @param array<array-key, T> $data The initial items to store in the queue.
*/
public function __construct(string $queueType, array $data = [])
public function __construct(private readonly string $queueType, array $data = [])
{
$this->queueType = $queueType;
parent::__construct($data);
}
@@ -65,9 +53,9 @@ class Queue extends AbstractArray implements QueueInterface
* serves only to fulfill the `ArrayAccess` interface requirements. It is
* invoked by other operations when adding values to the queue.
*
* @throws InvalidArgumentException if $value is of the wrong type
* @throws InvalidArgumentException if $value is of the wrong type.
*/
public function offsetSet($offset, $value): void
public function offsetSet(mixed $offset, mixed $value): void
{
if ($this->checkType($this->getType(), $value) === false) {
throw new InvalidArgumentException(
@@ -80,11 +68,9 @@ class Queue extends AbstractArray implements QueueInterface
}
/**
* @throws InvalidArgumentException if $value is of the wrong type
*
* @inheritDoc
* @throws InvalidArgumentException if $value is of the wrong type.
*/
public function add($element): bool
public function add(mixed $element): bool
{
$this[] = $element;
@@ -92,74 +78,67 @@ class Queue extends AbstractArray implements QueueInterface
}
/**
* @inheritDoc
* @return T
*
* @throws NoSuchElementException if this queue is empty.
*/
public function element()
public function element(): mixed
{
$element = $this->peek();
if ($element === null) {
throw new NoSuchElementException(
'Can\'t return element from Queue. Queue is empty.',
);
}
return $element;
return $this->peek() ?? throw new NoSuchElementException(
'Can\'t return element from Queue. Queue is empty.',
);
}
/**
* @inheritDoc
*/
public function offer($element): bool
public function offer(mixed $element): bool
{
try {
return $this->add($element);
} catch (InvalidArgumentException $e) {
} catch (InvalidArgumentException) {
return false;
}
}
/**
* @inheritDoc
* @return T | null
*/
public function peek()
public function peek(): mixed
{
if ($this->count() === 0) {
$index = array_key_first($this->data);
if ($index === null) {
return null;
}
return $this[$this->index];
return $this[$index];
}
/**
* @inheritDoc
* @return T | null
*/
public function poll()
public function poll(): mixed
{
if ($this->count() === 0) {
$index = array_key_first($this->data);
if ($index === null) {
return null;
}
$head = $this[$this->index];
unset($this[$this->index]);
$this->index++;
$head = $this[$index];
unset($this[$index]);
return $head;
}
/**
* @inheritDoc
* @return T
*
* @throws NoSuchElementException if this queue is empty.
*/
public function remove()
public function remove(): mixed
{
$head = $this->poll();
if ($head === null) {
throw new NoSuchElementException('Can\'t return element from Queue. Queue is empty.');
}
return $head;
return $this->poll() ?? throw new NoSuchElementException(
'Can\'t return element from Queue. Queue is empty.',
);
}
public function getType(): string

View File

@@ -129,8 +129,7 @@ interface QueueInterface extends ArrayInterface
* Implementations should use a more-specific exception that extends
* `\RuntimeException`.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function add($element): bool;
public function add(mixed $element): bool;
/**
* Retrieves, but does not remove, the head of this queue.
@@ -144,7 +143,7 @@ interface QueueInterface extends ArrayInterface
*
* @throws NoSuchElementException if this queue is empty.
*/
public function element();
public function element(): mixed;
/**
* Inserts the specified element into this queue if it is possible to do so
@@ -160,8 +159,7 @@ interface QueueInterface extends ArrayInterface
*
* @return bool `true` if the element was added to this queue, else `false`.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function offer($element): bool;
public function offer(mixed $element): bool;
/**
* Retrieves, but does not remove, the head of this queue, or returns `null`
@@ -169,9 +167,9 @@ interface QueueInterface extends ArrayInterface
*
* @see self::element()
*
* @return T|null the head of this queue, or `null` if this queue is empty.
* @return T | null the head of this queue, or `null` if this queue is empty.
*/
public function peek();
public function peek(): mixed;
/**
* Retrieves and removes the head of this queue, or returns `null`
@@ -179,9 +177,9 @@ interface QueueInterface extends ArrayInterface
*
* @see self::remove()
*
* @return T|null the head of this queue, or `null` if this queue is empty.
* @return T | null the head of this queue, or `null` if this queue is empty.
*/
public function poll();
public function poll(): mixed;
/**
* Retrieves and removes the head of this queue.
@@ -195,7 +193,7 @@ interface QueueInterface extends ArrayInterface
*
* @throws NoSuchElementException if this queue is empty.
*/
public function remove();
public function remove(): mixed;
/**
* Returns the type associated with this queue.

View File

@@ -28,7 +28,7 @@ namespace Ramsey\Collection;
* $foo = new \My\Foo();
* $set = new Set(\My\Foo::class);
*
* $set->add($foo); // returns TRUE, the element don't exists
* $set->add($foo); // returns TRUE, the element doesn't exist
* $set->add($foo); // returns FALSE, the element already exists
*
* $bar = new \My\Foo();
@@ -40,23 +40,15 @@ namespace Ramsey\Collection;
*/
class Set extends AbstractSet
{
/**
* The type of elements stored in this set
*
* A set's type is immutable. For this reason, this property is private.
*/
private string $setType;
/**
* Constructs a set object of the specified type, optionally with the
* specified data.
*
* @param string $setType The type (FQCN) associated with this set.
* @param string $setType The type or class name associated with this set.
* @param array<array-key, T> $data The initial items to store in the set.
*/
public function __construct(string $setType, array $data = [])
public function __construct(private readonly string $setType, array $data = [])
{
$this->setType = $setType;
parent::__construct($data);
}

View File

@@ -36,39 +36,22 @@ trait TypeTrait
* @param string $type The type to check the value against.
* @param mixed $value The value to check.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
protected function checkType(string $type, $value): bool
protected function checkType(string $type, mixed $value): bool
{
switch ($type) {
case 'array':
return is_array($value);
case 'bool':
case 'boolean':
return is_bool($value);
case 'callable':
return is_callable($value);
case 'float':
case 'double':
return is_float($value);
case 'int':
case 'integer':
return is_int($value);
case 'null':
return $value === null;
case 'numeric':
return is_numeric($value);
case 'object':
return is_object($value);
case 'resource':
return is_resource($value);
case 'scalar':
return is_scalar($value);
case 'string':
return is_string($value);
case 'mixed':
return true;
default:
return $value instanceof $type;
}
return match ($type) {
'array' => is_array($value),
'bool', 'boolean' => is_bool($value),
'callable' => is_callable($value),
'float', 'double' => is_float($value),
'int', 'integer' => is_int($value),
'null' => $value === null,
'numeric' => is_numeric($value),
'object' => is_object($value),
'resource' => is_resource($value),
'scalar' => is_scalar($value),
'string' => is_string($value),
'mixed' => true,
default => $value instanceof $type,
};
}
}

View File

@@ -14,9 +14,10 @@ declare(strict_types=1);
namespace Ramsey\Collection\Tool;
use Ramsey\Collection\Exception\ValueExtractionException;
use Ramsey\Collection\Exception\InvalidPropertyOrMethod;
use Ramsey\Collection\Exception\UnsupportedOperationException;
use function get_class;
use function is_array;
use function is_object;
use function method_exists;
use function property_exists;
@@ -28,34 +29,53 @@ use function sprintf;
trait ValueExtractorTrait
{
/**
* Extracts the value of the given property or method from the object.
* Extracts the value of the given property, method, or array key from the
* element.
*
* @param mixed $object The object to extract the value from.
* @param string $propertyOrMethod The property or method for which the
* If `$propertyOrMethod` is `null`, we return the element as-is.
*
* @param mixed $element The element to extract the value from.
* @param string | null $propertyOrMethod The property or method for which the
* value should be extracted.
*
* @return mixed the value extracted from the specified property or method.
* @return mixed the value extracted from the specified property, method,
* or array key, or the element itself.
*
* @throws ValueExtractionException if the method or property is not defined.
* @throws InvalidPropertyOrMethod
* @throws UnsupportedOperationException
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
protected function extractValue($object, string $propertyOrMethod)
protected function extractValue(mixed $element, ?string $propertyOrMethod): mixed
{
if (!is_object($object)) {
throw new ValueExtractionException('Unable to extract a value from a non-object');
if ($propertyOrMethod === null) {
return $element;
}
if (property_exists($object, $propertyOrMethod)) {
return $object->$propertyOrMethod;
if (!is_object($element) && !is_array($element)) {
throw new UnsupportedOperationException(sprintf(
'The collection type "%s" does not support the $propertyOrMethod parameter',
$this->getType(),
));
}
if (method_exists($object, $propertyOrMethod)) {
return $object->{$propertyOrMethod}();
if (is_array($element)) {
return $element[$propertyOrMethod] ?? throw new InvalidPropertyOrMethod(sprintf(
'Key or index "%s" not found in collection elements',
$propertyOrMethod,
));
}
throw new ValueExtractionException(
// phpcs:ignore SlevomatCodingStandard.Classes.ModernClassNameReference.ClassNameReferencedViaFunctionCall
sprintf('Method or property "%s" not defined in %s', $propertyOrMethod, get_class($object)),
);
if (property_exists($element, $propertyOrMethod)) {
return $element->$propertyOrMethod;
}
if (method_exists($element, $propertyOrMethod)) {
return $element->{$propertyOrMethod}();
}
throw new InvalidPropertyOrMethod(sprintf(
'Method or property "%s" not defined in %s',
$propertyOrMethod,
$element::class,
));
}
}

View File

@@ -16,7 +16,7 @@ namespace Ramsey\Collection\Tool;
use DateTimeInterface;
use function get_class;
use function assert;
use function get_resource_type;
use function is_array;
use function is_bool;
@@ -24,7 +24,6 @@ use function is_callable;
use function is_object;
use function is_resource;
use function is_scalar;
use function var_export;
/**
* Provides functionality to express a value as string
@@ -46,8 +45,7 @@ trait ValueToStringTrait
*
* @param mixed $value the value to return as a string.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
protected function toolValueToString($value): string
protected function toolValueToString(mixed $value): string
{
// null
if ($value === null) {
@@ -74,12 +72,8 @@ trait ValueToStringTrait
return '(' . get_resource_type($value) . ' resource #' . (int) $value . ')';
}
// If we don't know what it is, use var_export().
if (!is_object($value)) {
return '(' . var_export($value, true) . ')';
}
// From here, $value should be an object.
assert(is_object($value));
// __toString() is implemented
if (is_callable([$value, '__toString'])) {
@@ -92,7 +86,6 @@ trait ValueToStringTrait
}
// unknown type
// phpcs:ignore SlevomatCodingStandard.Classes.ModernClassNameReference.ClassNameReferencedViaFunctionCall
return '(' . get_class($value) . ' Object)';
return '(' . $value::class . ' Object)';
}
}