Add Psalm tooling

This commit is contained in:
Sebastian Meyer 2024-01-26 11:40:26 +01:00
parent 7a8ac2f87a
commit addf2e869d
14 changed files with 509 additions and 104 deletions

View File

@ -39,7 +39,8 @@
"phpstan/phpstan": "^1.10.56", "phpstan/phpstan": "^1.10.56",
"phpstan/phpstan-strict-rules": "^1.5", "phpstan/phpstan-strict-rules": "^1.5",
"friendsofphp/php-cs-fixer": "^3.48", "friendsofphp/php-cs-fixer": "^3.48",
"squizlabs/php_codesniffer": "^3.8" "squizlabs/php_codesniffer": "^3.8",
"vimeo/psalm": "^5.20"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View File

@ -23,6 +23,7 @@
<ignore-tag>extends</ignore-tag> <ignore-tag>extends</ignore-tag>
<ignore-tag>implements</ignore-tag> <ignore-tag>implements</ignore-tag>
<ignore-tag>phpstan-require-implements</ignore-tag> <ignore-tag>phpstan-require-implements</ignore-tag>
<ignore-tag>psalm-suppress</ignore-tag>
</ignore-tags> </ignore-tags>
</api> </api>
<guide> <guide>

22
psalm.xml Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0"?>
<psalm
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorLevel="1"
resolveFromConfigFile="true"
findUnusedBaselineEntry="true"
findUnusedCode="false"
>
<issueHandlers>
<RedundantCastGivenDocblockType errorLevel="suppress"/>
<RedundantConditionGivenDocblockType errorLevel="suppress"/>
<RedundantFunctionCallGivenDocblockType errorLevel="suppress"/>
</issueHandlers>
<projectFiles>
<directory name="src"/>
<ignoreFiles>
<directory name="vendor"/>
</ignoreFiles>
</projectFiles>
</psalm>

View File

@ -23,110 +23,223 @@ declare(strict_types=1);
namespace OCC\Basics\DataStructures; namespace OCC\Basics\DataStructures;
use ArrayAccess;
use Countable;
use InvalidArgumentException; use InvalidArgumentException;
use Iterator;
use OutOfRangeException;
use RuntimeException;
use SplDoublyLinkedList; use SplDoublyLinkedList;
use OCC\Basics\Traits\Getter; use OCC\Basics\Traits\Getter;
use Serializable;
/** /**
* A type-sensitive, taversable list. * A type-sensitive, taversable list.
* *
* Extends [\SplDoublyLinkedList](https://www.php.net/spldoublylinkedlist) with * Extends [\SplDoublyLinkedList](https://www.php.net/spldoublylinkedlist) with
* an option to specify the allowed data types for list items. * an option to specify the allowed data types for list values.
* *
* @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com>
* @package Basics\DataStructures * @package Basics\DataStructures
* *
* @property-read string[] $allowedTypes * @property-read string[] $allowedTypes The allowed data types for values.
*
* @api
* *
* @template AllowedType of mixed * @template AllowedType of mixed
* @extends SplDoublyLinkedList<AllowedType> * @extends SplDoublyLinkedList<AllowedType>
* @implements ArrayAccess<int, AllowedType>
* @implements Iterator<int, AllowedType>
*/ */
class StrictList extends SplDoublyLinkedList class StrictList extends SplDoublyLinkedList implements ArrayAccess, Countable, Iterator, Serializable
{ {
use Getter; use Getter;
/** /**
* Defines the allowed data types for items. * Queue style iterator mode (First In, First Out).
*/
public const IT_MODE_FIFO = 0;
/**
* Stack style iterator mode (Last In, First Out).
*/
public const IT_MODE_LIFO = 2;
/**
* Destructive iterator mode (delete values after iteration).
*/
public const IT_MODE_DELETE = 1;
/**
* Preserving iterator mode (keep values after iteration).
*/
public const IT_MODE_KEEP = 0;
/**
* The allowed data types for values.
* *
* @var string[] * @var string[]
*
* @internal
*/ */
protected array $allowedTypes = []; protected array $allowedTypes = [];
/** /**
* Add/insert a new item at the specified index. * Add/insert a new value at the specified offset.
* *
* @param int $index The index where the new item is to be inserted * @param int $offset The offset where the new value is to be inserted
* @param AllowedType $item The new item for the index * @param AllowedType $value The new value for the offset
* *
* @return void * @return void
* *
* @throws InvalidArgumentException * @throws InvalidArgumentException
*
* @api
*/ */
public function add(int $index, mixed $item): void public function add(int $offset, mixed $value): void
{ {
if (!$this->isAllowedType($item)) { if (!$this->isAllowedType($value)) {
throw new InvalidArgumentException( throw new InvalidArgumentException(
sprintf( sprintf(
'Parameter 2 must be an allowed type, %s given.', 'Parameter 2 must be an allowed type, %s given.',
get_debug_type($item) get_debug_type($value)
) )
); );
} }
parent::add($index, $item); parent::add($offset, $value);
} }
/** /**
* Append items at the end of the list. * Append values at the end of the list.
* *
* @param AllowedType ...$items One or more items to append * @param AllowedType ...$values One or more values to append
* *
* @return void * @return void
* *
* @throws InvalidArgumentException * @throws InvalidArgumentException
*
* @api
*/ */
public function append(mixed ...$items): void public function append(mixed ...$values): void
{ {
foreach ($items as $count => $item) { foreach ($values as $count => $value) {
if (!$this->isAllowedType($item)) { if (!$this->isAllowedType($value)) {
throw new InvalidArgumentException( throw new InvalidArgumentException(
sprintf( sprintf(
'Parameter %d must be an allowed type, %s given.', 'Parameter %d must be an allowed type, %s given.',
(int) $count + 1, (int) $count + 1,
get_debug_type($item) get_debug_type($value)
) )
); );
} }
} }
foreach ($items as $item) { foreach ($values as $value) {
parent::push($item); parent::push($value);
} }
} }
/** /**
* Check if the item's data type is allowed on the list. * Peek at the value at the beginning of the list.
* *
* @param AllowedType $item The item to check * @return AllowedType The first value of the list
* *
* @return bool Whether the item's data type is allowed * @throws RuntimeException
*
* @api
*/ */
public function isAllowedType(mixed $item): bool public function bottom(): mixed
{
return parent::bottom();
}
/**
* Count the number of values on the list.
*
* @return int The current number of values
*
* @api
*/
public function count(): int
{
return parent::count();
}
/**
* Get current list value.
*
* @return AllowedType The current value
*
* @api
*/
public function current(): mixed
{
return parent::current();
}
/**
* 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 value's data type is allowed on the list.
*
* @param AllowedType $value The value to check
*
* @return bool Whether the value's data type is allowed
*
* @api
*/
public function isAllowedType(mixed $value): bool
{ {
if (count($this->allowedTypes) === 0) { if (count($this->allowedTypes) === 0) {
return true; return true;
} }
foreach ($this->allowedTypes as $type) { foreach ($this->allowedTypes as $type) {
$function = 'is_' . $type; $function = 'is_' . $type;
if (function_exists($function) && $function($item)) { if (function_exists($function) && $function($value)) {
return true; return true;
} }
/** @var class-string */
$fqcn = ltrim($type, '\\'); $fqcn = ltrim($type, '\\');
if (is_object($item) && is_a($item, $fqcn)) { if (is_object($value) && is_a($value, $fqcn)) {
return true; return true;
} }
} }
return false; return false;
} }
/**
* Check if the list is empty.
*
* @return bool Whether the list is empty
*
* @api
*/
public function isEmpty(): bool
{
return parent::isEmpty();
}
/**
* Get the current value's offset.
*
* @return int The current offset
*
* @api
*/
public function key(): int
{
return parent::key();
}
/** /**
* Magic getter method for $this->allowedTypes. * Magic getter method for $this->allowedTypes.
* *
@ -140,93 +253,275 @@ class StrictList extends SplDoublyLinkedList
} }
/** /**
* Set the item at the specified index. * Move cursor to the next value on the list.
*
* @param ?int $index The index being set or NULL to append
* @param AllowedType $item The new item for the index
* *
* @return void * @return void
* *
* @throws InvalidArgumentException * @api
*/ */
public function offsetSet(mixed $index, mixed $item): void public function next(): void
{ {
if (!$this->isAllowedType($item)) { parent::next();
throw new InvalidArgumentException(
sprintf(
'Parameter 2 must be an allowed type, %s given.',
get_debug_type($item)
)
);
}
parent::offsetSet($index, $item);
} }
/** /**
* Prepend items at the start of the list. * Check if the specified offset exists.
* *
* @param AllowedType ...$items One or more items to prepend * @param int $offset The offset being checked
*
* @return bool Whether the offset exists
*
* @api
*/
public function offsetExists(mixed $offset): bool
{
return parent::offsetExists($offset);
}
/**
* Get the value at the specified offset.
*
* @param int $offset The offset to get
*
* @return ?AllowedType The value at the offset or NULL
*
* @throws OutOfRangeException
*
* @api
*/
public function offsetGet(mixed $offset): mixed
{
return parent::offsetGet($offset);
}
/**
* Set the value at the specified offset.
*
* @param ?int $offset The offset being set or NULL to append
* @param AllowedType $value The new value for the offset
* *
* @return void * @return void
* *
* @throws InvalidArgumentException * @throws InvalidArgumentException
*
* @api
*/ */
public function prepend(mixed ...$items): void public function offsetSet(mixed $offset, mixed $value): void
{ {
foreach ($items as $count => $item) { if (!$this->isAllowedType($value)) {
if (!$this->isAllowedType($item)) { 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 specified offset.
*
* @param int $offset The offset to unset
*
* @return void
*
* @throws OutOfRangeException
*
* @api
*/
public function offsetUnset(mixed $offset): void
{
parent::offsetUnset($offset);
}
/**
* Pops an value from the end of the list.
*
* @return AllowedType The value from the end of the list
*
* @throws RuntimeException
*
* @api
*/
public function pop(): mixed
{
return parent::pop();
}
/**
* Prepend values at the start of the list.
*
* @param AllowedType ...$values One or more values to prepend
*
* @return void
*
* @throws InvalidArgumentException
*
* @api
*/
public function prepend(mixed ...$values): void
{
foreach ($values as $count => $value) {
if (!$this->isAllowedType($value)) {
throw new InvalidArgumentException( throw new InvalidArgumentException(
sprintf( sprintf(
'Parameter %d must be an allowed type, %s given.', 'Parameter %d must be an allowed type, %s given.',
(int) $count + 1, (int) $count + 1,
get_debug_type($item) get_debug_type($value)
) )
); );
} }
} }
foreach ($items as $item) { foreach ($values as $value) {
parent::unshift($item); parent::unshift($value);
} }
} }
/** /**
* Push an item at the end of the list. * Move cursor to the previous value on the list.
* *
* @param AllowedType $item The item to push * @return void
*
* @api
*/
public function prev(): void
{
parent::prev();
}
/**
* Push an value at the end of the list.
*
* @param AllowedType $value The value to push
* *
* @return void * @return void
* *
* @throws InvalidArgumentException * @throws InvalidArgumentException
*
* @api
*/ */
public function push(mixed $item): void public function push(mixed $value): void
{ {
if (!$this->isAllowedType($item)) { if (!$this->isAllowedType($value)) {
throw new InvalidArgumentException( throw new InvalidArgumentException(
sprintf( sprintf(
'Parameter 1 must be an allowed type, %s given.', 'Parameter 1 must be an allowed type, %s given.',
get_debug_type($item) get_debug_type($value)
) )
); );
} }
parent::push($item); parent::push($value);
}
/**
* Move cursor back to the start of the list.
*
* @return void
*
* @api
*/
public function rewind(): void
{
parent::rewind();
} }
/** /**
* Get string representation of $this. * Get string representation of $this.
* *
* @return string The string representation * @return string The string representation
*
* @internal
*/ */
public function serialize(): string public function serialize(): string
{ {
return serialize($this->__serialize()); return serialize($this->__serialize());
} }
/**
* Set allowed data types of list values.
*
* @param string[] $allowedTypes Allowed data types of values
*
* @return void
*
* @throws InvalidArgumentException
*
* @internal
*/
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
*
* @api
*/
public function setIteratorMode(int $mode): int
{
return parent::setIteratorMode($mode);
}
/**
* Shift an value from the beginning of the list.
*
* @return AllowedType The first value of the list
*
* @throws RuntimeException
*
* @api
*/
public function shift(): mixed
{
return parent::shift();
}
/**
* Peek at the value at the end of the list.
*
* @return AllowedType The last value of the list
*
* @throws RuntimeException
*
* @api
*/
public function top(): mixed
{
return parent::top();
}
/** /**
* Restore $this from string representation. * Restore $this from string representation.
* *
* @param string $data The string representation * @param string $data The string representation
* *
* @return void * @return void
*
* @internal
*/ */
public function unserialize($data): void public function unserialize($data): void
{ {
@ -236,31 +531,46 @@ class StrictList extends SplDoublyLinkedList
} }
/** /**
* Prepend the list with an item. * Prepend the list with an value.
* *
* @param AllowedType $item The item to unshift * @param AllowedType $value The value to unshift
* *
* @return void * @return void
* *
* @throws InvalidArgumentException * @throws InvalidArgumentException
*
* @api
*/ */
public function unshift(mixed $item): void public function unshift(mixed $value): void
{ {
if (!$this->isAllowedType($item)) { if (!$this->isAllowedType($value)) {
throw new InvalidArgumentException( throw new InvalidArgumentException(
sprintf( sprintf(
'Parameter 1 must be an allowed type, %s given.', 'Parameter 1 must be an allowed type, %s given.',
get_debug_type($item) get_debug_type($value)
) )
); );
} }
parent::unshift($item); parent::unshift($value);
} }
/** /**
* Create a type-sensitive, traversable list of items. * Check if the list contains any more values.
*
* @return bool Whether the list contains more values
*
* @api
*/
public function valid(): bool
{
return parent::valid();
}
/**
* Create a type-sensitive, traversable list of values.
*
* @param string[] $allowedTypes Allowed data types of values (optional)
* *
* @param string[] $allowedTypes Allowed data types of items (optional)
* If empty, all types are allowed. * If empty, all types are allowed.
* Possible values are: * Possible values are:
* - "array" * - "array"
@ -277,16 +587,13 @@ class StrictList extends SplDoublyLinkedList
* - "scalar" * - "scalar"
* - "string" * - "string"
* *
* @return void
*
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function __construct(array $allowedTypes = []) public function __construct(array $allowedTypes = [])
{ {
if (array_sum(array_map('is_string', $allowedTypes)) !== count($allowedTypes)) { $this->setAllowedTypes($allowedTypes);
throw new InvalidArgumentException(
'Allowed types must be array of strings or empty array.'
);
}
$this->allowedTypes = $allowedTypes;
} }
/** /**
@ -325,15 +632,17 @@ class StrictList extends SplDoublyLinkedList
* @return void * @return void
* *
* @internal * @internal
*
* @psalm-suppress MethodSignatureMismatch
*/ */
public function __unserialize(array $data): void public function __unserialize(array $data): void
{ {
/** @var string[] $allowedTypes */ /** @var string[] $allowedTypes */
$allowedTypes = $data['StrictList::allowedTypes']; $allowedTypes = $data['StrictList::allowedTypes'];
$this->__construct($allowedTypes); $this->setAllowedTypes($allowedTypes);
/** @var iterable<AllowedType> $items */ /** @var array<int, AllowedType> $values */
$items = $data['SplDoublyLinkedList::dllist']; $values = $data['SplDoublyLinkedList::dllist'];
$this->append(...$items); $this->append(...$values);
/** @var int $flags */ /** @var int $flags */
$flags = $data['SplDoublyLinkedList::flags']; $flags = $data['SplDoublyLinkedList::flags'];
$this->setIteratorMode($flags); $this->setIteratorMode($flags);

View File

@ -23,10 +23,14 @@ declare(strict_types=1);
namespace OCC\Basics\DataStructures; namespace OCC\Basics\DataStructures;
use ArrayAccess;
use Countable;
use Iterator;
use RuntimeException; use RuntimeException;
use Serializable;
/** /**
* A type-sensitive, taversable First In, First Out Queue (FIFO). * A type-sensitive, taversable First In, First Out queue (FIFO).
* *
* Extends [\SplQueue](https://www.php.net/splqueue) with an option to specify * Extends [\SplQueue](https://www.php.net/splqueue) with an option to specify
* the allowed data types for list items. * the allowed data types for list items.
@ -34,12 +38,14 @@ use RuntimeException;
* @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com>
* @package Basics\DataStructures * @package Basics\DataStructures
* *
* @property-read string[] $allowedTypes * @api
* *
* @template AllowedType of mixed * @template AllowedType of mixed
* @extends StrictList<AllowedType> * @extends StrictList<AllowedType>
* @implements ArrayAccess<int, AllowedType>
* @implements Iterator<int, AllowedType>
*/ */
class StrictQueue extends StrictList class StrictQueue extends StrictList implements ArrayAccess, Countable, Iterator, Serializable
{ {
/** /**
* Dequeue an item from the queue. * Dequeue an item from the queue.
@ -70,6 +76,17 @@ class StrictQueue extends StrictList
* *
* @param int $mode The new iterator mode (0 or 1) * @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 * @return int The set of flags and modes of iteration
* *
* @throws RuntimeException * @throws RuntimeException
@ -91,6 +108,7 @@ class StrictQueue extends StrictList
* Create a type-sensitive, traversable queue of items. * Create a type-sensitive, traversable queue of items.
* *
* @param string[] $allowedTypes Allowed data types of items (optional) * @param string[] $allowedTypes Allowed data types of items (optional)
*
* If empty, all types are allowed. * If empty, all types are allowed.
* Possible values are: * Possible values are:
* - "array" * - "array"
@ -107,6 +125,8 @@ class StrictQueue extends StrictList
* - "scalar" * - "scalar"
* - "string" * - "string"
* *
* @return void
*
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function __construct(array $allowedTypes = []) public function __construct(array $allowedTypes = [])

View File

@ -23,10 +23,14 @@ declare(strict_types=1);
namespace OCC\Basics\DataStructures; namespace OCC\Basics\DataStructures;
use ArrayAccess;
use Countable;
use Iterator;
use RuntimeException; use RuntimeException;
use Serializable;
/** /**
* A type-sensitive, taversable Last In, First Out Stack (LIFO). * A type-sensitive, taversable Last In, First Out stack (LIFO).
* *
* Extends [\SplStack](https://www.php.net/splstack) with an option to specify * Extends [\SplStack](https://www.php.net/splstack) with an option to specify
* the allowed data types for list items. * the allowed data types for list items.
@ -34,12 +38,14 @@ use RuntimeException;
* @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com>
* @package Basics\DataStructures * @package Basics\DataStructures
* *
* @property-read string[] $allowedTypes * @api
* *
* @template AllowedType of mixed * @template AllowedType of mixed
* @extends StrictList<AllowedType> * @extends StrictList<AllowedType>
* @implements ArrayAccess<int, AllowedType>
* @implements Iterator<int, AllowedType>
*/ */
class StrictStack extends StrictList class StrictStack extends StrictList implements ArrayAccess, Countable, Iterator, Serializable
{ {
/** /**
* Add an item to the stack. * Add an item to the stack.
@ -70,6 +76,17 @@ class StrictStack extends StrictList
* *
* @param int $mode The new iterator mode (2 or 3) * @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 * @return int The set of flags and modes of iteration
* *
* @throws RuntimeException * @throws RuntimeException
@ -91,6 +108,7 @@ class StrictStack extends StrictList
* Create a type-sensitive, traversable stack of items. * Create a type-sensitive, traversable stack of items.
* *
* @param string[] $allowedTypes Allowed data types of items (optional) * @param string[] $allowedTypes Allowed data types of items (optional)
*
* If empty, all types are allowed. * If empty, all types are allowed.
* Possible values are: * Possible values are:
* - "array" * - "array"
@ -107,6 +125,8 @@ class StrictStack extends StrictList
* - "scalar" * - "scalar"
* - "string" * - "string"
* *
* @return void
*
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
*/ */
public function __construct(array $allowedTypes = []) public function __construct(array $allowedTypes = [])

View File

@ -32,6 +32,8 @@ use ErrorException;
* *
* @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com>
* @package Basics\ErrorHandlers * @package Basics\ErrorHandlers
*
* @api
*/ */
class ThrowErrorException class ThrowErrorException
{ {

View File

@ -32,6 +32,8 @@ use Throwable;
* *
* @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com>
* @package Basics\ErrorHandlers * @package Basics\ErrorHandlers
*
* @api
*/ */
class TriggerExceptionError class TriggerExceptionError
{ {

View File

@ -23,25 +23,32 @@ declare(strict_types=1);
namespace OCC\Basics\InterfaceTraits; namespace OCC\Basics\InterfaceTraits;
use ArrayAccess;
/** /**
* A generic implementation of the ArrayAccess interface. * A generic implementation of the ArrayAccess interface.
* *
* @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com>
* @package Basics\InterfaceTraits * @package Basics\InterfaceTraits
* *
* @phpstan-require-implements \ArrayAccess * @template TKey of int|string
* @template TValue of mixed
* @implements ArrayAccess<TKey, TValue>
* @phpstan-require-implements ArrayAccess
*/ */
trait ArrayAccess trait ArrayAccessTrait
{ {
/** /**
* Holds the array-accessible data. * Holds the array-accessible data.
*
* @var array<TKey, TValue>
*/ */
private array $data = []; protected array $data = [];
/** /**
* Whether the specified offset exists. * Check if the specified offset exists.
* *
* @param mixed $offset The offset to check for * @param TKey $offset The offset to check for
* *
* @return bool Whether the offset exists * @return bool Whether the offset exists
*/ */
@ -53,9 +60,9 @@ trait ArrayAccess
/** /**
* Retrieve data at the specified offset. * Retrieve data at the specified offset.
* *
* @param mixed $offset The offset to retrieve at * @param TKey $offset The offset to retrieve at
* *
* @return mixed The value at the offset or NULL * @return ?TValue The value at the offset or NULL
*/ */
public function offsetGet(mixed $offset): mixed public function offsetGet(mixed $offset): mixed
{ {
@ -65,8 +72,8 @@ trait ArrayAccess
/** /**
* Assign a value to the specified offset. * Assign a value to the specified offset.
* *
* @param mixed $offset The offset to assign to or NULL to append * @param ?TKey $offset The offset to assign to or NULL to append
* @param mixed $value The value to set * @param TValue $value The value to set
* *
* @return void * @return void
*/ */
@ -82,7 +89,7 @@ trait ArrayAccess
/** /**
* Unset the specified offset. * Unset the specified offset.
* *
* @param mixed $offset The offset to unset * @param TKey $offset The offset to unset
* *
* @return void * @return void
*/ */

View File

@ -23,20 +23,27 @@ declare(strict_types=1);
namespace OCC\Basics\InterfaceTraits; namespace OCC\Basics\InterfaceTraits;
use Countable;
/** /**
* A generic implementation of the Countable interface. * A generic implementation of the Countable interface.
* *
* @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com>
* @package Basics\InterfaceTraits * @package Basics\InterfaceTraits
* *
* @phpstan-require-implements \Countable * @template TKey of int|string
* @template TValue of mixed
* @implements Countable<TValue>
* @phpstan-require-implements Countable
*/ */
trait Countable trait CountableTrait
{ {
/** /**
* Holds the countable data. * Holds the countable data.
*
* @var array<TKey, TValue>
*/ */
private array $data = []; protected array $data = [];
/** /**
* Count the data items. * Count the data items.

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace OCC\Basics\InterfaceTraits; namespace OCC\Basics\InterfaceTraits;
use ArrayIterator; use ArrayIterator;
use IteratorAggregate;
/** /**
* A generic implementation of the IteratorAggregate interface. * A generic implementation of the IteratorAggregate interface.
@ -31,19 +32,24 @@ use ArrayIterator;
* @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com>
* @package Basics\InterfaceTraits * @package Basics\InterfaceTraits
* *
* @phpstan-require-implements \IteratorAggregate * @template TKey of int|string
* @template TValue of mixed
* @implements IteratorAggregate<TKey, TValue>
* @phpstan-require-implements IteratorAggregate
*/ */
trait IteratorAggregate trait IteratorAggregateTrait
{ {
/** /**
* Holds the iterable data. * Holds the iterable data.
*
* @var array<TKey, TValue>
*/ */
private array $data = []; protected array $data = [];
/** /**
* Retrieve an external iterator. * Retrieve an external iterator.
* *
* @return ArrayIterator * @return ArrayIterator<TKey, TValue> New array iterator for data array
*/ */
public function getIterator(): ArrayIterator public function getIterator(): ArrayIterator
{ {

View File

@ -23,25 +23,32 @@ declare(strict_types=1);
namespace OCC\Basics\InterfaceTraits; namespace OCC\Basics\InterfaceTraits;
use Iterator;
/** /**
* A generic implementation of the Iterator interface. * A generic implementation of the Iterator interface.
* *
* @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com>
* @package Basics\InterfaceTraits * @package Basics\InterfaceTraits
* *
* @phpstan-require-implements \Iterator * @template TKey of int|string
* @template TValue of mixed
* @implements Iterator<TKey, TValue>
* @phpstan-require-implements Iterator
*/ */
trait Iterator trait IteratorTrait
{ {
/** /**
* Holds the iterable data. * Holds the iterable data.
*
* @var array<TKey, TValue>
*/ */
private array $data = []; protected array $data = [];
/** /**
* Return the current item. * Return the current item.
* *
* @return mixed The current item or FALSE if invalid * @return TValue|false The current item or FALSE if invalid
*/ */
public function current(): mixed public function current(): mixed
{ {
@ -51,7 +58,7 @@ trait Iterator
/** /**
* Return the current key. * Return the current key.
* *
* @return mixed The current key or NULL if invalid * @return ?TKey The current key or NULL if invalid
*/ */
public function key(): mixed public function key(): mixed
{ {
@ -79,7 +86,7 @@ trait Iterator
} }
/** /**
* Checks if current position is valid. * Check if current position is valid.
* *
* @return bool Whether the current position is valid * @return bool Whether the current position is valid
*/ */

View File

@ -75,11 +75,12 @@ trait Getter
public function __isset(string $property): bool public function __isset(string $property): bool
{ {
try { try {
/** @var mixed $value */
$value = $this->__get($property); $value = $this->__get($property);
} catch (InvalidArgumentException) { } catch (InvalidArgumentException) {
$value = null; $value = null;
} finally { } finally {
return (bool) $value !== false; return boolval($value ?? null) !== false;
} }
} }
} }

View File

@ -37,7 +37,7 @@ trait Singleton
/** /**
* Holds the singleton instance. * Holds the singleton instance.
* *
* @var array<static> * @var array<string, static>
*/ */
private static array $singleton = []; private static array $singleton = [];