diff --git a/src/DataStructures/StrictCollection.php b/src/DataStructures/StrictCollection.php index d814faa..78f6883 100644 --- a/src/DataStructures/StrictCollection.php +++ b/src/DataStructures/StrictCollection.php @@ -27,35 +27,17 @@ use ArrayAccess; use Countable; use DomainException; use InvalidArgumentException; +use OCC\Basics\DataStructures\Exceptions\InvalidDataTypeException; use OCC\Basics\Interfaces\ArrayAccessTrait; use OCC\Basics\Interfaces\CountableTrait; -use OCC\Basics\Traits\Getter; +use OCC\Basics\Traits\TypeChecker; +use OutOfRangeException; use Serializable; use function array_is_list; -use function array_map; -use function array_sum; -use function count; -use function function_exists; use function get_debug_type; -use function is_a; -use function is_array; -use function is_bool; -use function is_callable; -use function is_countable; -use function is_double; -use function is_float; -use function is_int; use function is_integer; -use function is_iterable; -use function is_long; -use function is_null; -use function is_numeric; -use function is_resource; -use function is_scalar; use function is_string; -use function is_object; -use function ltrim; use function serialize; use function sprintf; use function unserialize; @@ -73,8 +55,6 @@ use function unserialize; * @author Sebastian Meyer * @package Basics\DataStructures * - * @property-read string[] $allowedTypes The allowed data types for items. - * * @api * * @template AllowedType of mixed @@ -86,35 +66,28 @@ class StrictCollection implements ArrayAccess, Countable, Serializable use ArrayAccessTrait; /** @use CountableTrait */ use CountableTrait; - use Getter; - - /** - * The allowed data types for collection items. - * - * @var string[] - * - * @internal - */ - protected array $allowedTypes = []; + use TypeChecker { + setAllowedTypes as protected; + } /** * Holds the collection's items. * - * @var AllowedType[] + * @var array * * @internal */ protected array $_data = []; /** - * Add/insert a new item at the specified index. + * Add/insert a item at the specified index. * - * @param array-key $offset The new item's index - * @param AllowedType $value The new item + * @param array-key $offset The item's index + * @param AllowedType $value The item * * @return void * - * @throws InvalidArgumentException if `$offset` is not of allowed type + * @throws InvalidDataTypeException if `$value` is not of allowed type * * @api */ @@ -140,53 +113,25 @@ class StrictCollection implements ArrayAccess, Countable, Serializable * * @param array-key $offset The item's index * - * @return ?AllowedType The item or NULL if key is invalid + * @return AllowedType The item + * + * @throws OutOfRangeException when `$offset` is out of bounds * * @api */ public function get(int|string $offset): mixed { - return $this->offsetGet($offset); - } - - /** - * Get allowed data types for collection items. - * - * @return string[] The list of allowed data types - * - * @api - */ - public function getAllowedTypes(): array - { - return $this->allowedTypes; - } - - /** - * Check if the item's data type is allowed in the collection. - * - * @param AllowedType $value The item to check - * - * @return bool Whether the item's data type is allowed - * - * @api - */ - public function isAllowedType(mixed $value): bool - { - if (count($this->allowedTypes) === 0) { - return true; + if (!$this->offsetExists($offset)) { + throw new OutOfRangeException( + sprintf( + 'Offset %s is not a valid index key for this collection.', + $offset + ) + ); } - foreach ($this->allowedTypes as $type) { - $function = 'is_' . $type; - if (function_exists($function) && $function($value)) { - return true; - } - /** @var class-string $fqcn */ - $fqcn = ltrim($type, '\\'); - if (is_object($value) && is_a($value, $fqcn)) { - return true; - } - } - return false; + /** @var AllowedType $value */ + $value = $this->offsetGet($offset); + return $value; } /** @@ -226,7 +171,8 @@ class StrictCollection implements ArrayAccess, Countable, Serializable * * @return void * - * @throws InvalidArgumentException if `$offset` or `$value` is not of allowed type + * @throws InvalidDataTypeException if `$value` is not of allowed type + * @throws InvalidArgumentException if `$offset` is not a valid array key * * @api */ @@ -240,8 +186,8 @@ class StrictCollection implements ArrayAccess, Countable, Serializable ) ); } - if (!$this->isAllowedType($value)) { - throw new InvalidArgumentException( + if (!$this->hasAllowedType($value)) { + throw new InvalidDataTypeException( sprintf( 'Parameter 2 must be an allowed type, %s given.', get_debug_type($value) @@ -258,10 +204,20 @@ class StrictCollection implements ArrayAccess, Countable, Serializable * * @return void * + * @throws OutOfRangeException when `$offset` is out of bounds + * * @api */ public function remove(int|string $offset): void { + if (!$this->offsetExists($offset)) { + throw new OutOfRangeException( + sprintf( + 'Offset %s is not a valid index key for this collection.', + $offset + ) + ); + } $this->offsetUnset($offset); } @@ -283,7 +239,7 @@ class StrictCollection implements ArrayAccess, Countable, Serializable * * @return void * - * @throws InvalidArgumentException if `$value` is not of allowed type + * @throws InvalidDataTypeException if `$value` is not of allowed type * * @api */ @@ -292,29 +248,10 @@ class StrictCollection implements ArrayAccess, Countable, Serializable $this->offsetSet($offset, $value); } - /** - * Set allowed data types of collection items. - * - * @param string[] $allowedTypes Allowed data types of items - * - * @return void - * - * @throws InvalidArgumentException if any value of `$allowedTypes` is not a string - */ - protected function setAllowedTypes(array $allowedTypes = []): void - { - if (array_sum(array_map('is_string', $allowedTypes)) !== count($allowedTypes)) { - throw new InvalidArgumentException( - 'Allowed types must be array of strings or empty array.' - ); - } - $this->allowedTypes = $allowedTypes; - } - /** * Return array representation of collection. * - * @return AllowedType[] Array of collection items + * @return array Array of collection items * * @api */ @@ -338,12 +275,11 @@ class StrictCollection implements ArrayAccess, Countable, Serializable { if (!$this->isList()) { throw new DomainException( - 'Cannot convert into list: collection contains non-integer and/or non-consecutive keys.' + 'Cannot convert into StrictList: collection contains non-integer and/or non-consecutive keys.' ); } - $strictList = new StrictList($this->allowedTypes); - $items = $this->toArray(); - $strictList->append(...$items); + $strictList = new StrictList($this->getAllowedTypes()); + $strictList->append(...$this->toArray()); return $strictList; } @@ -361,18 +297,6 @@ class StrictCollection implements ArrayAccess, Countable, Serializable $this->__unserialize($dataArray); } - /** - * Magic getter method for $this->allowedTypes. - * - * @return string[] The list of allowed data types - * - * @internal - */ - protected function _magicGetAllowedTypes(): array - { - return $this->getAllowedTypes(); - } - /** * Create a type-sensitive collection of items. * @@ -425,8 +349,8 @@ class StrictCollection implements ArrayAccess, Countable, Serializable public function __serialize(): array { return [ - 'StrictCollection::allowedTypes' => $this->allowedTypes, - 'StrictCollection::items' => $this->_data + 'StrictCollection::allowedTypes' => $this->getAllowedTypes(), + 'StrictCollection::items' => $this->toArray() ]; } @@ -446,7 +370,7 @@ class StrictCollection implements ArrayAccess, Countable, Serializable /** @var string[] $allowedTypes */ $allowedTypes = $data['StrictCollection::allowedTypes']; $this->setAllowedTypes($allowedTypes); - /** @var AllowedType[] $items */ + /** @var array $items */ $items = $data['StrictCollection::items']; foreach ($items as $offset => $value) { $this->offsetSet($offset, $value); diff --git a/src/DataStructures/StrictList.php b/src/DataStructures/StrictList.php index db1ec77..be8f407 100644 --- a/src/DataStructures/StrictList.php +++ b/src/DataStructures/StrictList.php @@ -23,46 +23,8 @@ declare(strict_types=1); namespace OCC\Basics\DataStructures; -use ArrayAccess; -use Countable; -use InvalidArgumentException; -use Iterator; -use OutOfRangeException; -use RangeException; -use RuntimeException; +use OCC\Basics\DataStructures\Traits\StrictSplDatastructureTrait; use SplDoublyLinkedList; -use OCC\Basics\Traits\Getter; -use Serializable; - -use function array_map; -use function array_sum; -use function count; -use function function_exists; -use function get_debug_type; -use function in_array; -use function is_a; -use function is_array; -use function is_bool; -use function is_callable; -use function is_countable; -use function is_double; -use function is_float; -use function is_int; -use function is_integer; -use function is_iterable; -use function is_long; -use function is_null; -use function is_numeric; -use function is_resource; -use function is_scalar; -use function is_string; -use function is_object; -use function iterator_to_array; -use function ltrim; -use function range; -use function serialize; -use function sprintf; -use function unserialize; /** * A type-sensitive, taversable list. @@ -74,709 +36,13 @@ use function unserialize; * @author Sebastian Meyer * @package Basics\DataStructures * - * @property-read string[] $allowedTypes The allowed data types for values. - * * @api * * @template AllowedType of mixed * @extends SplDoublyLinkedList - * @implements ArrayAccess - * @implements Iterator */ -class StrictList extends SplDoublyLinkedList implements ArrayAccess, Countable, Iterator, Serializable +class StrictList extends SplDoublyLinkedList { - use Getter; - - /** - * The allowed data types for list items. - * - * @var string[] - * - * @internal - */ - protected array $allowedTypes = []; - - /** - * Add/insert a new item at the specified offset. - * - * @param int $offset The offset where the new item is to be inserted - * @param AllowedType $value The new item for the offset - * - * @return void - * - * @throws InvalidArgumentException if `$value` is not of allowed type - * @throws OutOfRangeException when `$offset` is out of bounds - * - * @api - */ - public function add(int $offset, mixed $value): void - { - $this->offsetSet($offset, $value); - } - - /** - * Append items at the end of the list. - * - * @param AllowedType ...$values One or more items to append - * - * @return void - * - * @throws InvalidArgumentException if any `$values` is not of allowed type - * - * @api - */ - public function append(mixed ...$values): void - { - /** @var array $values */ - foreach ($values as $count => $value) { - if (!$this->isAllowedType($value)) { - throw new InvalidArgumentException( - sprintf( - 'Parameter %d must be an allowed type, %s given.', - $count + 1, - get_debug_type($value) - ) - ); - } - } - foreach ($values as $value) { - parent::push($value); - } - } - - /** - * Peek at the item at the beginning of the list. - * - * @return AllowedType The first item of the list - * - * @throws RuntimeException if the list is empty - * - * @api - */ - public function bottom(): mixed - { - return parent::bottom(); - } - - /** - * Clear the list of any items. - * - * @return void - * - * @api - */ - public function clear(): void - { - while (!$this->isEmpty()) { - $this->pop(); - } - $this->rewind(); - } - - /** - * Get the number of items on the list. - * - * @return int The number of items on the list - * - * @api - */ - public function count(): int - { - return parent::count(); - } - - /** - * Get the current list item. - * - * @return AllowedType The current item - * - * @api - */ - public function current(): mixed - { - return parent::current(); - } - - /** - * Get the item at the specified index. - * - * @param int $offset The item's index - * - * @return AllowedType The item - * - * @throws OutOfRangeException when `$offset` is out of bounds - * - * @api - */ - public function get(int $offset): mixed - { - return $this->offsetGet($offset); - } - - /** - * Get allowed data types for list items. - * - * @return string[] The list of allowed data types - * - * @api - */ - public function getAllowedTypes(): array - { - return $this->allowedTypes; - } - - /** - * Get the mode of iteration. - * - * @return int The set of flags and modes of iteration - * - * @api - */ - public function getIteratorMode(): int - { - return parent::getIteratorMode(); - } - - /** - * Check if the item's data type is allowed on the list. - * - * @param AllowedType $value The item to check - * - * @return bool Whether the item's data type is allowed - * - * @api - */ - public function isAllowedType(mixed $value): bool - { - if (count($this->allowedTypes) === 0) { - return true; - } - foreach ($this->allowedTypes as $type) { - $function = 'is_' . $type; - if (function_exists($function) && $function($value)) { - return true; - } - /** @var class-string $fqcn */ - $fqcn = ltrim($type, '\\'); - if (is_object($value) && is_a($value, $fqcn)) { - return true; - } - } - return false; - } - - /** - * Check if list is empty. - * - * @return bool Whether the list contains no items - * - * @api - */ - public function isEmpty(): bool - { - return parent::isEmpty(); - } - - /** - * Check if this can be considered a list. - * - * @return true Always TRUE (this exists only for compatibility reasons) - * - * @api - */ - public function isList(): bool - { - return true; - } - - /** - * Get the current list index. - * - * @return int The current list index - * - * @api - */ - public function key(): int - { - return parent::key(); - } - - /** - * Move the cursor to the next list index. - * - * @return void - * - * @api - */ - public function next(): void - { - parent::next(); - } - - /** - * Check if the specified index exists and is not empty. - * - * @param int $offset The index to check - * - * @return bool Whether the index exists and is not empty - * - * @api - */ - public function offsetExists(mixed $offset): bool - { - return parent::offsetExists($offset); - } - - /** - * Get the item from the specified index. - * - * @param int $offset The item's index - * - * @return AllowedType The item - * - * @throws OutOfRangeException when `$offset` is out of bounds - * - * @api - */ - public function offsetGet(mixed $offset): mixed - { - return parent::offsetGet($offset); - } - - /** - * Set the item at the specified offset. - * - * @param ?int $offset The offset being set or NULL to append - * @param AllowedType $value The new item for the offset - * - * @return void - * - * @throws InvalidArgumentException if `$value` is not of allowed type - * @throws OutOfRangeException when `$offset` is out of bounds - * - * @api - */ - public function offsetSet(mixed $offset, mixed $value): void - { - if (!$this->isAllowedType($value)) { - throw new InvalidArgumentException( - sprintf( - 'Parameter 2 must be an allowed type, %s given.', - get_debug_type($value) - ) - ); - } - /** @psalm-suppress PossiblyNullArgument */ - parent::offsetSet($offset, $value); - } - - /** - * Unset the item at the specified index. - * - * @param int $offset The item's index - * - * @return void - * - * @throws OutOfRangeException when `$offset` is out of bounds - * - * @api - */ - public function offsetUnset(mixed $offset): void - { - parent::offsetUnset($offset); - } - - /** - * Pops an item from the end of the list. - * - * @return AllowedType The item from the end of the list - * - * @throws RuntimeException if the list is empty - * - * @api - */ - public function pop(): mixed - { - return parent::pop(); - } - - /** - * Prepend items at the start of the list. - * - * @param AllowedType ...$values One or more items to prepend - * - * @return void - * - * @throws InvalidArgumentException if `$value` is not of allowed type - * - * @api - */ - public function prepend(mixed ...$values): void - { - /** @var array $values */ - foreach ($values as $count => $value) { - if (!$this->isAllowedType($value)) { - throw new InvalidArgumentException( - sprintf( - 'Parameter %d must be an allowed type, %s given.', - $count + 1, - get_debug_type($value) - ) - ); - } - } - foreach ($values as $value) { - parent::unshift($value); - } - } - - /** - * Move the cursor to the previous list index. - * - * @return void - * - * @api - */ - public function prev(): void - { - parent::prev(); - } - - /** - * Push an item at the end of the list. - * - * @param AllowedType $value The item to push - * - * @return void - * - * @throws InvalidArgumentException if `$value` is not of allowed type - * - * @api - */ - public function push(mixed $value): void - { - if (!$this->isAllowedType($value)) { - throw new InvalidArgumentException( - sprintf( - 'Parameter 1 must be an allowed type, %s given.', - get_debug_type($value) - ) - ); - } - parent::push($value); - } - - /** - * Remove an item from the list. - * - * @param int $offset The item's index - * - * @return void - * - * @throws OutOfRangeException when `$offset` is out of bounds - * - * @api - */ - public function remove(int $offset): void - { - $this->offsetUnset($offset); - } - - /** - * Rewind the iterator's cursor. - * - * @return void - * - * @api - */ - public function rewind(): void - { - parent::rewind(); - } - - /** - * Get string representation of $this. - * - * @return string The string representation - * - * @internal - */ - public function serialize(): string - { - return serialize($this->__serialize()); - } - - /** - * Set an item at the specified index. - * - * @param int $offset The item's index - * @param AllowedType $value The item - * - * @return void - * - * @throws InvalidArgumentException if `$value` is not of allowed type - * - * @api - */ - public function set(int $offset, mixed $value): void - { - $this->offsetSet($offset, $value); - } - - /** - * Set allowed data types of list items. - * - * @param string[] $allowedTypes Allowed data types of items - * - * @return void - * - * @throws InvalidArgumentException if any value of `$allowedTypes` is not a string - */ - protected function setAllowedTypes(array $allowedTypes = []): void - { - if (array_sum(array_map('is_string', $allowedTypes)) !== count($allowedTypes)) { - throw new InvalidArgumentException( - 'Allowed types must be array of strings or empty array.' - ); - } - $this->allowedTypes = $allowedTypes; - } - - /** - * Set the mode of iteration. - * - * @param int $mode The new iterator mode (0, 1, 2 or 3) - * - * There are two orthogonal sets of modes that can be set. - * - * The direction of iteration (either one or the other): - * - StrictList::IT_MODE_FIFO (queue style) - * - StrictList::IT_MODE_LIFO (stack style) - * - * The behavior of the iterator (either one or the other): - * - StrictList::IT_MODE_DELETE (delete items) - * - StrictList::IT_MODE_KEEP (keep items) - * - * The default mode is: IT_MODE_FIFO | IT_MODE_KEEP - * - * @return int The set of flags and modes of iteration - * - * @throws RangeException if an invalid `$mode` is given - * - * @api - */ - public function setIteratorMode(int $mode): int - { - if (!in_array($mode, range(0, 3), true)) { - throw new RangeException( - sprintf( - 'Iterator mode must be an integer in range [0..3], %d given.', - $mode - ) - ); - } - return parent::setIteratorMode($mode); - } - - /** - * Shift an item from the beginning of the list. - * - * @return AllowedType The first item of the list - * - * @throws RuntimeException if the list is empty - * - * @api - */ - public function shift(): mixed - { - return parent::shift(); - } - - /** - * Return array representation of list. - * - * @return AllowedType[] Array of list items - * - * @api - */ - public function toArray(): array - { - return iterator_to_array($this, true); - } - - /** - * Turn list into a type-sensitive collection. - * - * @return StrictCollection A type-sensitive collection of the list's items - * - * @api - */ - public function toStrictCollection(): StrictCollection - { - $strictCollection = new StrictCollection($this->allowedTypes); - foreach ($this->toArray() as $offset => $value) { - $strictCollection[$offset] = $value; - } - return $strictCollection; - } - - /** - * Peek at the item at the end of the list. - * - * @return AllowedType The last item of the list - * - * @throws RuntimeException if the list is empty - * - * @api - */ - public function top(): mixed - { - return parent::top(); - } - - /** - * Restore $this from string representation. - * - * @param string $data The string representation - * - * @return void - * - * @internal - */ - public function unserialize($data): void - { - /** @var mixed[] $dataArray */ - $dataArray = unserialize($data); - $this->__unserialize($dataArray); - } - - /** - * Prepend the list with an item. - * - * @param AllowedType $value The item to unshift - * - * @return void - * - * @throws InvalidArgumentException if `$value` is not of allowed type - * - * @api - */ - public function unshift(mixed $value): void - { - if (!$this->isAllowedType($value)) { - throw new InvalidArgumentException( - sprintf( - 'Parameter 1 must be an allowed type, %s given.', - get_debug_type($value) - ) - ); - } - parent::unshift($value); - } - - /** - * Check if current cursor position is valid. - * - * @return bool Whether the current cursor position is valid - * - * @api - */ - public function valid(): bool - { - return parent::valid(); - } - - /** - * Magic getter method for $this->allowedTypes. - * - * @return string[] The list of allowed data types - * - * @internal - */ - protected function _magicGetAllowedTypes(): array - { - return $this->getAllowedTypes(); - } - - /** - * Create a type-sensitive, traversable list of items. - * - * @param string[] $allowedTypes Allowed data types of items (optional) - * - * If empty, all types are allowed. - * Possible values are: - * - "array" - * - "bool" - * - "callable" - * - "countable" - * - "float" or "double" - * - "int" or "integer" or "long" - * - "iterable" - * - "null" - * - "numeric" - * - "object" or FQCN - * - "resource" - * - "scalar" - * - "string" - * - * @return void - * - * @throws InvalidArgumentException if any value of `$allowedTypes` is not a string - */ - public function __construct(array $allowedTypes = []) - { - $this->setAllowedTypes($allowedTypes); - } - - /** - * Get debug information for $this. - * - * @return mixed[] The debug information - * - * @internal - */ - public function __debugInfo(): array - { - return $this->__serialize(); - } - - /** - * Get array representation of $this. - * - * @return mixed[] The array representation - * - * @internal - */ - public function __serialize(): array - { - return [ - 'StrictList::allowedTypes' => $this->allowedTypes, - 'SplDoublyLinkedList::dllist' => iterator_to_array($this), - 'SplDoublyLinkedList::flags' => $this->getIteratorMode() - ]; - } - - /** - * Restore $this from array representation. - * - * @param mixed[] $data The array representation - * - * @return void - * - * @internal - * - * @psalm-suppress MethodSignatureMismatch - */ - public function __unserialize(array $data): void - { - /** @var string[] $allowedTypes */ - $allowedTypes = $data['StrictList::allowedTypes']; - $this->setAllowedTypes($allowedTypes); - /** @var array $values */ - $values = $data['SplDoublyLinkedList::dllist']; - $this->append(...$values); - /** @var int $flags */ - $flags = $data['SplDoublyLinkedList::flags']; - $this->setIteratorMode($flags); - } + /** @use StrictSplDatastructureTrait */ + use StrictSplDatastructureTrait; } diff --git a/src/DataStructures/StrictQueue.php b/src/DataStructures/StrictQueue.php index f132fd7..266fa7c 100644 --- a/src/DataStructures/StrictQueue.php +++ b/src/DataStructures/StrictQueue.php @@ -23,24 +23,16 @@ declare(strict_types=1); namespace OCC\Basics\DataStructures; -use ArrayAccess; -use Countable; -use InvalidArgumentException; -use Iterator; -use RangeException; -use RuntimeException; -use Serializable; - -use function sprintf; +use OCC\Basics\DataStructures\Exceptions\InvalidDataTypeException; +use OCC\Basics\DataStructures\Traits\StrictSplDatastructureTrait; +use SplQueue; /** * A type-sensitive, taversable queue (FIFO). * - * Extends [\SplDoublyLinkedList](https://www.php.net/spldoublylinkedlist) with - * an option to restrict the allowed data types for list items by providing the - * constructor with an array of atomic types or fully qualified class names. It - * also restricts the iterator direction to first-in, first-out (FIFO) exactly - * like [\SplQueue](https://www.php.net/splqueue). + * Extends [\SplQueue](https://www.php.net/splqueue) with an option to restrict + * the allowed data types for list items by providing the constructor with an + * array of atomic types or fully qualified class names. * * @author Sebastian Meyer * @package Basics\DataStructures @@ -48,23 +40,12 @@ use function sprintf; * @api * * @template AllowedType of mixed - * @extends StrictList - * @implements ArrayAccess - * @implements Iterator + * @extends SplQueue */ -class StrictQueue extends StrictList implements ArrayAccess, Countable, Iterator, Serializable +class StrictQueue extends SplQueue { - /** - * Dequeue an item from the queue. - * - * @return AllowedType The dequeued item - * - * @api - */ - public function dequeue(): mixed - { - return parent::shift(); - } + /** @use StrictSplDatastructureTrait */ + use StrictSplDatastructureTrait; /** * Add an item to the queue. @@ -73,79 +54,12 @@ class StrictQueue extends StrictList implements ArrayAccess, Countable, Iterator * * @return void * - * @throws InvalidArgumentException if `$value` is not of allowed type + * @throws InvalidDataTypeException if `$value` is not of allowed type * * @api */ public function enqueue(mixed $value): void { - parent::push($value); - } - - /** - * Set the mode of iteration. - * - * @param int $mode The new iterator mode (0 or 1) - * - * There are two orthogonal sets of modes that can be set. - * - * The direction of iteration (fixed for StrictQueue): - * - StrictQueue::IT_MODE_FIFO (queue style) - * - * The behavior of the iterator (either one or the other): - * - StrictQueue::IT_MODE_DELETE (delete items) - * - StrictQueue::IT_MODE_KEEP (keep items) - * - * The default mode is: IT_MODE_FIFO | IT_MODE_KEEP - * - * @return int The set of flags and modes of iteration - * - * @throws RangeException if an invalid `$mode` is given - * @throws RuntimeException if trying to change iterator direction - * - * @api - */ - final public function setIteratorMode(int $mode): int - { - if ($mode > 1) { - throw new RuntimeException( - sprintf( - 'Changing the iterator direction of %s is prohibited.', - static::class - ) - ); - } - return parent::setIteratorMode($mode); - } - - /** - * Create a type-sensitive, traversable queue of items. - * - * @param string[] $allowedTypes Allowed data types of items (optional) - * - * If empty, all types are allowed. - * Possible values are: - * - "array" - * - "bool" - * - "callable" - * - "countable" - * - "float" or "double" - * - "int" or "integer" or "long" - * - "iterable" - * - "null" - * - "numeric" - * - "object" or FQCN - * - "resource" - * - "scalar" - * - "string" - * - * @return void - * - * @throws InvalidArgumentException if any value of `$allowedTypes` is not a string - */ - public function __construct(array $allowedTypes = []) - { - parent::__construct($allowedTypes); - $this->setIteratorMode(0); + $this->push($value); } } diff --git a/src/DataStructures/StrictStack.php b/src/DataStructures/StrictStack.php index 357897e..8ec5cda 100644 --- a/src/DataStructures/StrictStack.php +++ b/src/DataStructures/StrictStack.php @@ -23,24 +23,17 @@ declare(strict_types=1); namespace OCC\Basics\DataStructures; -use ArrayAccess; -use Countable; -use InvalidArgumentException; -use Iterator; -use RangeException; +use OCC\Basics\DataStructures\Exceptions\InvalidDataTypeException; +use OCC\Basics\DataStructures\Traits\StrictSplDatastructureTrait; use RuntimeException; -use Serializable; - -use function sprintf; +use SplStack; /** * A type-sensitive, taversable stack (LIFO). * - * Extends [\SplDoublyLinkedList](https://www.php.net/spldoublylinkedlist) with - * an option to restrict the allowed data types for list items by providing the - * constructor with an array of atomic types or fully qualified class names. It - * also restricts the iterator direction to last-in, first-out (LIFO) exactly - * like [\SplStack](https://www.php.net/splstack). + * Extends [\SplStack](https://www.php.net/splstack) with an option to restrict + * the allowed data types for list items by providing the constructor with an + * array of atomic types or fully qualified class names. * * @author Sebastian Meyer * @package Basics\DataStructures @@ -48,12 +41,13 @@ use function sprintf; * @api * * @template AllowedType of mixed - * @extends StrictList - * @implements ArrayAccess - * @implements Iterator + * @extends SplStack */ -class StrictStack extends StrictList implements ArrayAccess, Countable, Iterator, Serializable +class StrictStack extends SplStack { + /** @use StrictSplDatastructureTrait */ + use StrictSplDatastructureTrait; + /** * Add an item to the stack. * @@ -61,13 +55,13 @@ class StrictStack extends StrictList implements ArrayAccess, Countable, Iterator * * @return void * - * @throws InvalidArgumentException if `$value` is not of allowed type + * @throws InvalidDataTypeException if `$value` is not of allowed type * * @api */ public function stack(mixed $value): void { - parent::push($value); + $this->push($value); } /** @@ -75,77 +69,12 @@ class StrictStack extends StrictList implements ArrayAccess, Countable, Iterator * * @return AllowedType The unstacked item * + * @throws RuntimeException if the list is empty + * * @api */ public function unstack(): mixed { - return parent::pop(); - } - - /** - * Set the mode of iteration. - * - * @param int $mode The new iterator mode (2 or 3) - * - * There are two orthogonal sets of modes that can be set. - * - * The direction of iteration (fixed for StrictStack): - * - StrictStack::IT_MODE_LIFO (stack style) - * - * The behavior of the iterator (either one or the other): - * - StrictStack::IT_MODE_DELETE (delete items) - * - StrictStack::IT_MODE_KEEP (keep items) - * - * The default mode is: IT_MODE_LIFO | IT_MODE_KEEP - * - * @return int The set of flags and modes of iteration - * - * @throws RangeException if an invalid `$mode` is given - * @throws RuntimeException if trying to change iterator direction - * - * @api - */ - final public function setIteratorMode(int $mode): int - { - if ($mode < 2) { - throw new RuntimeException( - sprintf( - 'Changing the iterator direction of %s is prohibited.', - static::class - ) - ); - } - return parent::setIteratorMode($mode); - } - - /** - * Create a type-sensitive, traversable stack of items. - * - * @param string[] $allowedTypes Allowed data types of items (optional) - * - * If empty, all types are allowed. - * Possible values are: - * - "array" - * - "bool" - * - "callable" - * - "countable" - * - "float" or "double" - * - "int" or "integer" or "long" - * - "iterable" - * - "null" - * - "numeric" - * - "object" or FQCN - * - "resource" - * - "scalar" - * - "string" - * - * @return void - * - * @throws InvalidArgumentException if any value of `$allowedTypes` is not a string - */ - public function __construct(array $allowedTypes = []) - { - parent::__construct($allowedTypes); - $this->setIteratorMode(2); + return $this->pop(); } }