137 lines
3.9 KiB
PHP
137 lines
3.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KTXF\Utile\Collection;
|
|
|
|
/**
|
|
* @template T
|
|
* @extends \ArrayObject<int, T>
|
|
*/
|
|
class CollectionAbstract extends \ArrayObject {
|
|
|
|
protected const TYPE_STRING = 'string';
|
|
protected const TYPE_INT = 'int';
|
|
protected const TYPE_FLOAT = 'float';
|
|
protected const TYPE_BOOL = 'bool';
|
|
protected const TYPE_ARRAY = 'array';
|
|
protected const TYPE_DATE = 'date';
|
|
|
|
protected bool $associative = false;
|
|
protected string $typeKey = 'int';
|
|
protected string $typeValue = 'string';
|
|
|
|
/**
|
|
* @param array<T> $data
|
|
* @param class-string<T>|string|null $typeValue
|
|
* @param class-string<TKey>|string|null $typeKey
|
|
*/
|
|
public function __construct(array $data = [], string $typeValue, string|null $typeKey = null) {
|
|
// Ensure that all data entries are of the specified type
|
|
$this->typeValue = $typeValue;
|
|
if ($typeKey !== null) {
|
|
$this->typeKey = $typeKey;
|
|
$this->associative = true;
|
|
}
|
|
|
|
foreach ($data as $key => $value) {
|
|
if ($this->associative && !$this->validateKey($key)) {
|
|
throw new \InvalidArgumentException('Type error: element key ' . $key . ' is not of type ' . $this->typeKey);
|
|
}
|
|
if (!$this->validateValue($value)) {
|
|
throw new \InvalidArgumentException('Type error: element value at index ' . $key . ' is not of type ' . $this->typeValue);
|
|
}
|
|
}
|
|
|
|
if (!$this->associative) {
|
|
parent::__construct(array_values($data));
|
|
} else {
|
|
parent::__construct($data);
|
|
}
|
|
}
|
|
|
|
private function validateValue($value): bool {
|
|
// Check if the value is of the specified type
|
|
return match ($this->typeValue) {
|
|
self::TYPE_STRING => is_string($value),
|
|
self::TYPE_INT, 'integer' => is_int($value),
|
|
self::TYPE_FLOAT => is_float($value),
|
|
self::TYPE_BOOL, 'boolean' => is_bool($value),
|
|
self::TYPE_ARRAY => is_array($value),
|
|
self::TYPE_DATE => $value instanceof \DateTimeInterface,
|
|
default => $value instanceof $this->typeValue
|
|
};
|
|
}
|
|
|
|
protected function validateKey($key): bool {
|
|
// Check if the key is of the specified type
|
|
return match ($this->typeKey) {
|
|
self::TYPE_STRING => is_string($key),
|
|
default => is_int($key),
|
|
};
|
|
}
|
|
|
|
public function add($value, string|int|null $key = null): void {
|
|
$this->offsetSet($key, $value);
|
|
}
|
|
|
|
public function remove(string|int $key): void {
|
|
$this->offsetUnset($key);
|
|
}
|
|
|
|
public function extant(string|int $key): bool {
|
|
return $this->offsetExists($key);
|
|
}
|
|
|
|
#[\Override]
|
|
public function append(mixed $value): void {
|
|
if ($this->associative) {
|
|
throw new \LogicException('Cannot append to an associative collection. Use add() or offsetSet() instead.');
|
|
}
|
|
// ensure that the value is of the specified type before appending
|
|
if (!$this->validateValue($value)) {
|
|
throw new \InvalidArgumentException('Type error: value is not of type ' . $this->typeValue);
|
|
}
|
|
parent::append($value);
|
|
}
|
|
|
|
#[\Override]
|
|
public function offsetSet(mixed $key, mixed $value): void {
|
|
if ($this->associative) {
|
|
if ($key === null) {
|
|
throw new \LogicException('Logic error: Key cannot be null for associative collections');
|
|
}
|
|
if (!$this->validateKey($key)) {
|
|
throw new \InvalidArgumentException('Type error: key is not of type ' . $this->typeKey);
|
|
}
|
|
} else {
|
|
if ($key !== null) {
|
|
throw new \LogicException('Logic error: Key must be null for non-associative collections');
|
|
}
|
|
}
|
|
if (!$this->validateValue($value)) {
|
|
throw new \InvalidArgumentException('Type error: value is not of type ' . $this->typeValue);
|
|
}
|
|
parent::offsetSet($key, $value);
|
|
}
|
|
|
|
#[\Override]
|
|
public function offsetUnset(mixed $key): void
|
|
{
|
|
if (!$this->validateKey($key)) {
|
|
throw new \InvalidArgumentException('Type error: key is not of type ' . $this->typeKey);
|
|
}
|
|
parent::offsetUnset($key);
|
|
}
|
|
|
|
#[\Override]
|
|
public function offsetExists(mixed $key): bool
|
|
{
|
|
if (!$this->validateKey($key)) {
|
|
throw new \InvalidArgumentException('Type error: key is not of type ' . $this->typeKey);
|
|
}
|
|
return parent::offsetExists($key);
|
|
}
|
|
|
|
}
|