diff --git a/.phpdoc/guide/changelog.rst b/.phpdoc/guide/changelog.rst
index ab8d7be..d67317d 100644
--- a/.phpdoc/guide/changelog.rst
+++ b/.phpdoc/guide/changelog.rst
@@ -3,7 +3,8 @@
Changelog
#########
-.. contents::
+.. sidebar:: Table of Contents
+ .. contents::
v2.0.0
======
@@ -39,7 +40,9 @@ v2.0.0
* Added new error handler :php:class:`OCC\Basics\ErrorHandlers\TriggerExceptionError`
* Added new trait :php:trait:`OCC\Basics\Traits\OverloadingGetter`
* Added new trait :php:trait:`OCC\Basics\Traits\OverloadingSetter`
-* Extended API for all datastructures
+* Added new trait :php:trait:`OCC\Basics\Traits\TypeChecker`
+* Extended API for all datastructures (see :php:trait:`OCC\Basics\DataStructures\Traits\StrictSplDatastructureTrait`)
+* Introduced :php:class:`OCC\Basics\DataStructures\Exceptions\InvalidDataTypeException` for strict datastructures
* Extended `documentation `_
v1.1.0
diff --git a/.phpdoc/guide/index.rst b/.phpdoc/guide/index.rst
index c2a08f9..15f3f49 100644
--- a/.phpdoc/guide/index.rst
+++ b/.phpdoc/guide/index.rst
@@ -3,11 +3,8 @@
Documentation
#############
-.. meta::
- :layout: landingpage
-
.. toctree::
- :hidden:
+ :maxdepth: 2
overview/index
usage/index
diff --git a/.phpdoc/guide/overview/datastructures.rst b/.phpdoc/guide/overview/datastructures.rst
new file mode 100644
index 0000000..a8b1b05
--- /dev/null
+++ b/.phpdoc/guide/overview/datastructures.rst
@@ -0,0 +1,79 @@
+.. title:: Datastructures
+
+Typed Datastructures
+####################
+
+.. sidebar:: Table of Contents
+ .. contents::
+
+All datastructures in this package have in common that you can control the types of items they can hold.
+
+To restrict allowed data types for items, provide the constructor with an array of atomic types or fully qualified
+class names you want to allow as item types. Available atomic types are `array`, `bool`, `callable`, `countable`,
+`float` / `double`, `int` / `integer` / `long`, `iterable`, `null`, `numeric`, `object`, `resource`, `scalar` and
+`string`.
+
+Trying to add an item with a data type not on the list of allowed types to a strict datastructure will result in an
+:php:class:`OCC\Basics\DataStructures\Exceptions\InvalidDataTypeException`.
+
+ Examples:
+
+ .. code-block:: php
+ // create a collection of strings
+ $stringCollection = new StrictCollection(['string']);
+
+ // create a queue of PSR-15 middlewares
+ $middlewareQueue = new StrictQueue(['Psr\Http\Server\MiddlewareInterface']);
+
+StrictCollection
+================
+
+.. sidebar:: API Documentation
+ :php:class:`OCC\Basics\DataStructures\StrictCollection`
+
+*A type-sensitive, unsorted collection of items.*
+
+Holds items as key/value pairs where keys identify the items and have to be valid array keys while values can be of any
+controlled type.
+
+A `StrictCollection` can be accessed like an array, but not traversed because it has no particular order. Technically
+speaking, `StrictCollection` implements `\ArrayAccess `_, `\Countable
+`_ and `\Serializable `_, but no `\Traversable
+`_ interface.
+
+.. note::
+ Internally it holds the items in the `$_data` array, the same as most :php:namespace:`OCC\Basics\Interfaces` and
+ :php:namespace:`OCC\Basics\Traits` of this package.
+
+StrictList
+==========
+
+.. sidebar:: API Documentation
+ :php:class:`OCC\Basics\DataStructures\StrictList`
+
+*A type-sensitive, taversable list of items.*
+
+Extends `\SplDoublyLinkedList `_ with an option to restrict the allowed data
+types for list items.
+
+StrictQueue
+===========
+
+.. sidebar:: API Documentation
+ :php:class:`OCC\Basics\DataStructures\StrictQueue`
+
+*A type-sensitive, taversable queue (FIFO) of items.*
+
+Extends `\SplDoublyLinkedList `_ with an option to restrict the allowed data
+types for list items.
+
+StrictStack
+===========
+
+.. sidebar:: API Documentation
+ :php:class:`OCC\Basics\DataStructures\StrictStack`
+
+*A type-sensitive, taversable stack (LIFO) of items.*
+
+Extends `\SplDoublyLinkedList `_ with an option to restrict the allowed data
+types for list items.
diff --git a/.phpdoc/guide/overview/errorhandlers.rst b/.phpdoc/guide/overview/errorhandlers.rst
new file mode 100644
index 0000000..eb82d53
--- /dev/null
+++ b/.phpdoc/guide/overview/errorhandlers.rst
@@ -0,0 +1,27 @@
+.. title:: Error Handlers
+
+Error and Exception Handlers
+############################
+
+.. sidebar:: Table of Contents
+ .. contents::
+
+ThrowErrorException
+===================
+
+Throws internal errors as exceptions.
+
+If registered as error handler, this converts an internal PHP error into an
+`ErrorException`. It respects the `error_reporting` directive.
+
+> Usage: `set_error_handler(new ThrowErrorException());`
+
+TriggerExceptionError
+=====================
+
+Triggers errors for uncaught exceptions.
+
+If registered as exception handler, this catches an uncaught exception and
+converts it into an internal PHP error of severity `E_USER_ERROR`.
+
+> Usage: `set_exception_handler(new TriggerExceptionError());`
diff --git a/.phpdoc/guide/overview/index.rst b/.phpdoc/guide/overview/index.rst
index abf0d2b..8dc9e06 100644
--- a/.phpdoc/guide/overview/index.rst
+++ b/.phpdoc/guide/overview/index.rst
@@ -2,3 +2,17 @@
Overview
########
+
+The package currently contains classes for :doc:`datastructures`, :doc:`errorhandlers`, multiple :doc:`interfaces`, and
+more generic :doc:`traits` for common use cases. They share the same design principles like property and method naming
+schema, the highest coding standards of `PHPStan `_ and `Psalm `_, and full
+`PSR-12 `_ compliance to make sure they can be combined and easily re-used in
+other projects.
+
+.. toctree::
+ :maxdepth: 2
+
+ datastructures
+ errorhandlers
+ interfaces
+ traits
diff --git a/.phpdoc/guide/overview/interfaces.rst b/.phpdoc/guide/overview/interfaces.rst
new file mode 100644
index 0000000..dcd8357
--- /dev/null
+++ b/.phpdoc/guide/overview/interfaces.rst
@@ -0,0 +1,35 @@
+.. title:: Interfaces
+
+Interface Traits
+################
+
+.. sidebar:: Table of Contents
+ .. contents::
+
+ArrayAccessTrait
+================
+
+A generic implementation of the ArrayAccess interface.
+
+Internally it accesses the protected `$_data` array.
+
+CountableTrait
+==============
+
+A generic implementation of the Countable interface.
+
+Internally it counts the values of the protected `$_data` array.
+
+IteratorAggregateTrait
+======================
+
+A generic implementation of the IteratorAggregate interface.
+
+Internally it iterates over the protected `$_data` array.
+
+IteratorTrait
+=============
+
+A generic implementation of the Iterator interface.
+
+Internally it iterates over the protected `$_data` array.
diff --git a/.phpdoc/guide/overview/traits.rst b/.phpdoc/guide/overview/traits.rst
new file mode 100644
index 0000000..462083d
--- /dev/null
+++ b/.phpdoc/guide/overview/traits.rst
@@ -0,0 +1,78 @@
+.. title:: Traits
+
+Traits
+######
+
+.. sidebar:: Table of Contents
+ .. contents::
+
+Getter
+======
+
+Reads data from inaccessible properties by using magic methods.
+
+To make a `protected` or `private` property readable, provide a method named
+`_magicGet{Property}()` which handles the reading. Replace `{Property}` in
+the method's name with the name of the actual property (with an uppercase
+first letter).
+
+> Example: If the property is named `$fooBar`, the "magic" method has to be
+> `_magicGetFooBar()`. This method is then called when `$fooBar` is read in
+> a context where it normally would not be accessible.
+
+OverloadingGetter
+=================
+
+Overloads a class with readable magic properties.
+
+Internally it reads the protected `$_data` array whose keys are interpreted
+as property names.
+
+> Example: Reading `Foo->bar` will return the value of `$_data['bar']`.
+
+OverloadingSetter
+=================
+
+Overloads a class with writable magic properties.
+
+Internally it writes the protected `$_data` array whose keys are interpreted
+as property names.
+
+> Example: `Foo->bar = 42;` will set `$_data['bar']` to `42`.
+
+Setter
+======
+
+Writes data to inaccessible properties by using magic methods.
+
+To make a `protected` or `private` property writable, provide a method named
+`_magicSet{Property}()` which handles the writing. Replace `{Property}` in
+the method's name with the name of the actual property (with an uppercase
+first letter).
+
+> Example: If the property is named `$fooBar`, the "magic" method has to be
+> `_magicSetFooBar()`. This method is then called when `$fooBar` is written
+> to in a context where it normally would not be accessible.
+
+Singleton
+=========
+
+Allows just a single instance of the class using this trait.
+
+Get the singleton by calling the static method `getInstance()`.
+
+If there is no object yet, the constructor is called with the same arguments
+as `getInstance()`. Any later call will just return the already instantiated
+object (irrespective of the given arguments).
+
+In order for this to work as expected, the constructor has to be implemented
+as `private` to prevent direct instantiation of the class.
+
+TypeChecker
+===========
+
+A generic data type checker.
+
+This allows to set a list of allowed atomic data types and fully qualified
+class names. It also provides a method to check if a value's data type matches
+at least one of these types.
diff --git a/.phpdoc/guide/usage/index.rst b/.phpdoc/guide/usage/index.rst
index 1ce6e10..831e95d 100644
--- a/.phpdoc/guide/usage/index.rst
+++ b/.phpdoc/guide/usage/index.rst
@@ -3,8 +3,12 @@
User Guide
##########
-.. toctree::
- :maxdepth: 2
+The *PHP Basics* are a library package, not a stand-alone application. The following documentation of requirements and
+installation procedures describes how to make use of the classes and traits within your own application. For a detailed
+description of the package's contents have a look at the :doc:`../overview/index` page.
- requirements
- installation
+.. toctree::
+ :maxdepth: 2
+
+ requirements
+ installation
diff --git a/.phpdoc/guide/usage/installation.rst b/.phpdoc/guide/usage/installation.rst
index ad96dc2..9ccec9a 100644
--- a/.phpdoc/guide/usage/installation.rst
+++ b/.phpdoc/guide/usage/installation.rst
@@ -3,6 +3,9 @@
Installation
############
+.. sidebar:: Table of Contents
+ .. contents::
+
Composer
========
diff --git a/.phpdoc/template/css/custom.css.twig b/.phpdoc/template/css/custom.css.twig
index 5dc7e98..e6320d0 100644
--- a/.phpdoc/template/css/custom.css.twig
+++ b/.phpdoc/template/css/custom.css.twig
@@ -40,6 +40,8 @@ aside.phpdocumentor-sidebar
padding: 0 var(--spacing-md) !important;
}
+p > code,
+li > code,
code.prettyprint
{
background-color: var(--code-background-color);
@@ -50,6 +52,38 @@ code.prettyprint
padding: var(--spacing-xxxs);
}
+@media (max-width: 999px) {
+div.admonition-wrapper
+{
+ display: none;
+}
+}
+
+@media (min-width: 1000px) {
+div.admonition-wrapper
+{
+ display: block;
+ float: right;
+ padding: var(--spacing-md);
+ margin: 0 0 var(--spacing-md) var(--spacing-md);
+ width: auto;
+ font-size: var(--text-sm);
+ border: 1px solid var(--primary-color-lighten);
+}
+}
+
+div.admonition-wrapper
+ p.sidebar-title
+{
+ font-weight: bold;
+}
+
+div.contents
+ ul.phpdocumentor-list
+{
+ margin-bottom: 0;
+}
+
div.phpdocumentor-content
div.section
ul
diff --git a/.phpdoc/template/index.html.twig b/.phpdoc/template/index.html.twig
index 3c519ac..01d4ee9 100644
--- a/.phpdoc/template/index.html.twig
+++ b/.phpdoc/template/index.html.twig
@@ -10,10 +10,10 @@
A collection of generic classes and useful traits for PHP projects.
Extends \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.
+
Extends \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.
The common interface of all type-sensitive, SPL-based datastructures.
+
+
This extends all methods of the common interface of the Standard PHP Library
+Doubly Linked List Datastructures
+by type-checking to only allow specified data types on the list.
Extends \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.
+
Extends \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.
The common interface of all type-sensitive, SPL-based datastructures.
+
+
This extends all methods of the common interface of the Standard PHP Library
+Doubly Linked List Datastructures
+by type-checking to only allow specified data types on the list.
This allows to set a list of allowed atomic data types and fully qualified
+class names. It also provides a method to check if a value's data type
+matches at least one of these types.
This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
You should have received a copy of the GNU General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
You should have received a copy of the GNU General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
You should have received a copy of the GNU General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
You should have received a copy of the GNU General Public License
+along with this program. If not, see http://www.gnu.org/licenses/.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/files/src/DataStructures/Exceptions/InvalidDataTypeException.php.txt b/doc/files/src/DataStructures/Exceptions/InvalidDataTypeException.php.txt
new file mode 100644
index 0000000..784d43e
--- /dev/null
+++ b/doc/files/src/DataStructures/Exceptions/InvalidDataTypeException.php.txt
@@ -0,0 +1,39 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+declare(strict_types=1);
+
+namespace OCC\Basics\DataStructures\Exceptions;
+
+use DomainException;
+
+/**
+ * Common exception for type-sensitive datastructures.
+ *
+ * Exception thrown if a value does not adhere to the defined list of valid
+ * data types.
+ *
+ * @author Sebastian Meyer
+ * @package Basics\DataStructures
+ */
+class InvalidDataTypeException extends DomainException
+{
+}
diff --git a/doc/files/src/DataStructures/StrictCollection.php.txt b/doc/files/src/DataStructures/StrictCollection.php.txt
index d814faa..78f6883 100644
--- a/doc/files/src/DataStructures/StrictCollection.php.txt
+++ b/doc/files/src/DataStructures/StrictCollection.php.txt
@@ -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/doc/files/src/DataStructures/StrictList.php.txt b/doc/files/src/DataStructures/StrictList.php.txt
index db1ec77..be8f407 100644
--- a/doc/files/src/DataStructures/StrictList.php.txt
+++ b/doc/files/src/DataStructures/StrictList.php.txt
@@ -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/doc/files/src/DataStructures/StrictQueue.php.txt b/doc/files/src/DataStructures/StrictQueue.php.txt
index f132fd7..266fa7c 100644
--- a/doc/files/src/DataStructures/StrictQueue.php.txt
+++ b/doc/files/src/DataStructures/StrictQueue.php.txt
@@ -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/doc/files/src/DataStructures/StrictSplDatastructureTrait.php.txt b/doc/files/src/DataStructures/StrictSplDatastructureTrait.php.txt
new file mode 100644
index 0000000..8608abc
--- /dev/null
+++ b/doc/files/src/DataStructures/StrictSplDatastructureTrait.php.txt
@@ -0,0 +1,420 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+declare(strict_types=1);
+
+namespace OCC\Basics\DataStructures;
+
+use InvalidArgumentException;
+use OCC\Basics\Traits\TypeChecker;
+use OutOfRangeException;
+
+use function get_debug_type;
+use function iterator_to_array;
+use function serialize;
+use function sprintf;
+use function unserialize;
+
+/**
+ * The common interface of all type-sensitive, SPL-based datastructures.
+ *
+ * This extends all methods of the common interface of the Standard PHP Library
+ * [Doubly Linked List Datastructures](https://www.php.net/spl.datastructures)
+ * by type-checking to only allow specified data types on the list.
+ *
+ * @author Sebastian Meyer
+ * @package Basics\DataStructures
+ *
+ * @template AllowedType of mixed
+ */
+trait StrictSplDatastructureTrait
+{
+ use TypeChecker {
+ setAllowedTypes as protected;
+ }
+
+ /**
+ * 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->hasAllowedType($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);
+ }
+ }
+
+ /**
+ * Clear the list of any items.
+ *
+ * @return void
+ *
+ * @api
+ */
+ public function clear(): void
+ {
+ while (!$this->isEmpty()) {
+ $this->pop();
+ }
+ $this->rewind();
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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->hasAllowedType($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);
+ }
+
+ /**
+ * 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->hasAllowedType($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);
+ }
+ }
+
+ /**
+ * 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->hasAllowedType($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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Return array representation of list.
+ *
+ * @return array 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->getAllowedTypes());
+ foreach ($this->toArray() as $offset => $value) {
+ $strictCollection[$offset] = $value;
+ }
+ return $strictCollection;
+ }
+
+ /**
+ * 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->hasAllowedType($value)) {
+ throw new InvalidArgumentException(
+ sprintf(
+ 'Parameter 1 must be an allowed type, %s given.',
+ get_debug_type($value)
+ )
+ );
+ }
+ parent::unshift($value);
+ }
+
+ /**
+ * 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 [
+ 'StrictSplDatastructure::allowedTypes' => $this->getAllowedTypes(),
+ 'StrictSplDatastructure::dllist' => $this->toArray(),
+ 'StrictSplDatastructure::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['StrictSplDatastructure::allowedTypes'];
+ $this->setAllowedTypes($allowedTypes);
+ /** @var array $values */
+ $values = $data['StrictSplDatastructure::dllist'];
+ $this->append(...$values);
+ /** @var int $flags */
+ $flags = $data['StrictSplDatastructure::flags'];
+ $this->setIteratorMode($flags);
+ }
+}
diff --git a/doc/files/src/DataStructures/StrictStack.php.txt b/doc/files/src/DataStructures/StrictStack.php.txt
index 357897e..8ec5cda 100644
--- a/doc/files/src/DataStructures/StrictStack.php.txt
+++ b/doc/files/src/DataStructures/StrictStack.php.txt
@@ -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();
}
}
diff --git a/doc/files/src/DataStructures/Traits/StrictSplDatastructureTrait.php.txt b/doc/files/src/DataStructures/Traits/StrictSplDatastructureTrait.php.txt
new file mode 100644
index 0000000..982223a
--- /dev/null
+++ b/doc/files/src/DataStructures/Traits/StrictSplDatastructureTrait.php.txt
@@ -0,0 +1,422 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+declare(strict_types=1);
+
+namespace OCC\Basics\DataStructures\Traits;
+
+use InvalidArgumentException;
+use OCC\Basics\DataStructures\Exceptions\InvalidDataTypeException;
+use OCC\Basics\DataStructures\StrictCollection;
+use OCC\Basics\Traits\TypeChecker;
+use OutOfRangeException;
+
+use function get_debug_type;
+use function iterator_to_array;
+use function serialize;
+use function sprintf;
+use function unserialize;
+
+/**
+ * The common interface of all type-sensitive, SPL-based datastructures.
+ *
+ * This extends all methods of the common interface of the Standard PHP Library
+ * [Doubly Linked List Datastructures](https://www.php.net/spl.datastructures)
+ * by type-checking to only allow specified data types on the list.
+ *
+ * @author Sebastian Meyer
+ * @package Basics\DataStructures
+ *
+ * @template AllowedType of mixed
+ */
+trait StrictSplDatastructureTrait
+{
+ use TypeChecker {
+ setAllowedTypes as protected;
+ }
+
+ /**
+ * 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 InvalidDataTypeException 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 InvalidDataTypeException 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->hasAllowedType($value)) {
+ throw new InvalidDataTypeException(
+ sprintf(
+ 'Parameter %d must be an allowed type, %s given.',
+ $count + 1,
+ get_debug_type($value)
+ )
+ );
+ }
+ }
+ foreach ($values as $value) {
+ parent::push($value);
+ }
+ }
+
+ /**
+ * Clear the list of any items.
+ *
+ * @return void
+ *
+ * @api
+ */
+ public function clear(): void
+ {
+ while (!$this->isEmpty()) {
+ $this->pop();
+ }
+ $this->rewind();
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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 InvalidDataTypeException 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->hasAllowedType($value)) {
+ throw new InvalidDataTypeException(
+ sprintf(
+ 'Parameter 2 must be an allowed type, %s given.',
+ get_debug_type($value)
+ )
+ );
+ }
+ /** @psalm-suppress PossiblyNullArgument */
+ parent::offsetSet($offset, $value);
+ }
+
+ /**
+ * Prepend items at the start of the list.
+ *
+ * @param AllowedType ...$values One or more items to prepend
+ *
+ * @return void
+ *
+ * @throws InvalidDataTypeException if `$value` is not of allowed type
+ *
+ * @api
+ */
+ public function prepend(mixed ...$values): void
+ {
+ /** @var array $values */
+ foreach ($values as $count => $value) {
+ if (!$this->hasAllowedType($value)) {
+ throw new InvalidDataTypeException(
+ sprintf(
+ 'Parameter %d must be an allowed type, %s given.',
+ $count + 1,
+ get_debug_type($value)
+ )
+ );
+ }
+ }
+ foreach ($values as $value) {
+ parent::unshift($value);
+ }
+ }
+
+ /**
+ * Push an item at the end of the list.
+ *
+ * @param AllowedType $value The item to push
+ *
+ * @return void
+ *
+ * @throws InvalidDataTypeException if `$value` is not of allowed type
+ *
+ * @api
+ */
+ public function push(mixed $value): void
+ {
+ if (!$this->hasAllowedType($value)) {
+ throw new InvalidDataTypeException(
+ 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);
+ }
+
+ /**
+ * 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 InvalidDataTypeException if `$value` is not of allowed type
+ *
+ * @api
+ */
+ public function set(int $offset, mixed $value): void
+ {
+ $this->offsetSet($offset, $value);
+ }
+
+ /**
+ * Return array representation of list.
+ *
+ * @return array 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->getAllowedTypes());
+ foreach ($this->toArray() as $offset => $value) {
+ $strictCollection[$offset] = $value;
+ }
+ return $strictCollection;
+ }
+
+ /**
+ * 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 InvalidDataTypeException if `$value` is not of allowed type
+ *
+ * @api
+ */
+ public function unshift(mixed $value): void
+ {
+ if (!$this->hasAllowedType($value)) {
+ throw new InvalidDataTypeException(
+ sprintf(
+ 'Parameter 1 must be an allowed type, %s given.',
+ get_debug_type($value)
+ )
+ );
+ }
+ parent::unshift($value);
+ }
+
+ /**
+ * 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 [
+ 'StrictSplDatastructure::allowedTypes' => $this->getAllowedTypes(),
+ 'StrictSplDatastructure::dllist' => $this->toArray(),
+ 'StrictSplDatastructure::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['StrictSplDatastructure::allowedTypes'];
+ $this->setAllowedTypes($allowedTypes);
+ /** @var array $values */
+ $values = $data['StrictSplDatastructure::dllist'];
+ $this->append(...$values);
+ /** @var int $flags */
+ $flags = $data['StrictSplDatastructure::flags'];
+ $this->setIteratorMode($flags);
+ }
+}
diff --git a/doc/files/src/Traits/TypeChecker.php.txt b/doc/files/src/Traits/TypeChecker.php.txt
new file mode 100644
index 0000000..9a2042a
--- /dev/null
+++ b/doc/files/src/Traits/TypeChecker.php.txt
@@ -0,0 +1,150 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+declare(strict_types=1);
+
+namespace OCC\Basics\Traits;
+
+use InvalidArgumentException;
+
+use function array_map;
+use function array_sum;
+use function array_values;
+use function count;
+use function function_exists;
+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 ltrim;
+
+/**
+ * A generic data type checker.
+ *
+ * This allows to set a list of allowed atomic data types and fully qualified
+ * class names. It also provides a method to check if a value's data type
+ * matches at least one of these types.
+ *
+ * @author Sebastian Meyer
+ * @package Basics\Traits
+ *
+ * @api
+ */
+trait TypeChecker
+{
+ /**
+ * The allowed data types.
+ *
+ * @var string[]
+ *
+ * @internal
+ */
+ protected array $_allowedTypes = [];
+
+ /**
+ * Get allowed data types.
+ *
+ * @return string[] The list of allowed data types
+ *
+ * @api
+ */
+ public function getAllowedTypes(): array
+ {
+ return $this->_allowedTypes;
+ }
+
+ /**
+ * Check if a value's data type is allowed.
+ *
+ * @param mixed $value The value to check
+ *
+ * @return bool Whether the value's data type is allowed
+ *
+ * @api
+ */
+ public function hasAllowedType(mixed $value): bool
+ {
+ if (count($this->getAllowedTypes()) === 0) {
+ return true;
+ }
+ foreach ($this->getAllowedTypes() 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 a data type is allowed.
+ *
+ * @param string $type The type to check
+ *
+ * @return bool Whether the data type is allowed
+ *
+ * @api
+ */
+ public function isAllowedType(string $type): bool
+ {
+ return in_array($type, $this->getAllowedTypes(), true);
+ }
+
+ /**
+ * Set allowed data types.
+ *
+ * @param string[] $allowedTypes Allowed data types
+ *
+ * @return void
+ *
+ * @throws InvalidArgumentException if any value of `$allowedTypes` is not a string
+ *
+ * @api
+ */
+ public 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 = array_values($allowedTypes);
+ }
+}
diff --git a/doc/graphs/classes.html b/doc/graphs/classes.html
index ec4f91d..8cb5de0 100644
--- a/doc/graphs/classes.html
+++ b/doc/graphs/classes.html
@@ -64,6 +64,24 @@
All datastructures in this package have in common that you can control the types of items they can hold.
+
To restrict allowed data types for items, provide the constructor with an array of atomic types or fully qualified
+class names you want to allow as item types. Available atomic types are array, bool, callable, countable,
+float / double, int / integer / long, iterable, null, numeric, object, resource, scalar and
+string.
+
Trying to add an item with a data type not on the list of allowed types to a strict datastructure will result in an
+InvalidDataTypeException
+.
+
Examples:
// create a collection of strings
+$stringCollection = new StrictCollection(['string']);
+
+// create a queue of PSR-15 middlewares
+$middlewareQueue = new StrictQueue(['Psr\Http\Server\MiddlewareInterface']);
Holds items as key/value pairs where keys identify the items and have to be valid array keys while values can be of any
+controlled type.
+
A StrictCollection can be accessed like an array, but not traversed because it has no particular order. Technically
+speaking, StrictCollection implements \ArrayAccess, \Countable and \Serializable, but no \Traversable interface.
+
+
+
+
Internally it holds the items in the $_data array, the same as most Interfaces
+ and
+Traits
+ of this package.
The package currently contains classes for Typed Datastructures
+, Error and Exception Handlers
+, multiple Interface Traits
+, and
+more generic Traits
+ for common use cases. They share the same design principles like property and method naming
+schema, the highest coding standards of PHPStan and Psalm, and full
+PSR-12 compliance to make sure they can be combined and easily re-used in
+other projects.
Reads data from inaccessible properties by using magic methods.
+
To make a protected or private property readable, provide a method named
+_magicGet{Property}() which handles the reading. Replace {Property} in
+the method's name with the name of the actual property (with an uppercase
+first letter).
+
> Example: If the property is named $fooBar, the "magic" method has to be
+> _magicGetFooBar(). This method is then called when $fooBar is read in
+> a context where it normally would not be accessible.
+
+
+
+
OverloadingGetter
+
+
Overloads a class with readable magic properties.
+
Internally it reads the protected $_data array whose keys are interpreted
+as property names.
+
> Example: Reading Foo->bar will return the value of $_data['bar'].
+
+
+
+
OverloadingSetter
+
+
Overloads a class with writable magic properties.
+
Internally it writes the protected $_data array whose keys are interpreted
+as property names.
+
> Example: Foo->bar = 42; will set $_data['bar'] to 42.
+
+
+
+
Setter
+
+
Writes data to inaccessible properties by using magic methods.
+
To make a protected or private property writable, provide a method named
+_magicSet{Property}() which handles the writing. Replace {Property} in
+the method's name with the name of the actual property (with an uppercase
+first letter).
+
> Example: If the property is named $fooBar, the "magic" method has to be
+> _magicSetFooBar(). This method is then called when $fooBar is written
+> to in a context where it normally would not be accessible.
+
+
+
+
Singleton
+
+
Allows just a single instance of the class using this trait.
+
Get the singleton by calling the static method getInstance().
+
If there is no object yet, the constructor is called with the same arguments
+as getInstance(). Any later call will just return the already instantiated
+object (irrespective of the given arguments).
+
In order for this to work as expected, the constructor has to be implemented
+as private to prevent direct instantiation of the class.
+
+
+
+
TypeChecker
+
+
A generic data type checker.
+
This allows to set a list of allowed atomic data types and fully qualified
+class names. It also provides a method to check if a value's data type matches
+at least one of these types.
The PHP Basics are a library package, not a stand-alone application. The following documentation of requirements and
+installation procedures describes how to make use of the classes and traits within your own application. For a detailed
+description of the package's contents have a look at the Overview
+ page.