diff --git a/.gitignore b/.gitignore index 70c2328..f49bfd5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .idea/ vendor/ - .php_cs.cache .phpunit.cache/ +.php-cs-fixer.cache \ No newline at end of file diff --git a/README.md b/README.md index c13905b..b269b93 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,21 @@ Very simple typed map class. + Includes IntCharMap and CharMap as examples. It is best to extend `Olivebbs\Map\GenericMap `for your uses. ### Example ```php use Olivebbs\Map\GenericMap; +use Olivebbs\Map\Enum\ValueType; +use Olivebbs\Map\Enum\KeyType; final class MyStringMap extends GenericMap { public function __construct(array $values) { - parent::__construct(GenericMap::STRING, GenericMap::STRING); + parent::__construct(KeyType::STRING, ValueType::STRING); $this->map->putAll($values); } } @@ -23,8 +26,10 @@ or ```php use Olivebbs\Map\GenericMap; +use Olivebbs\Map\Enum\ValueType; +use Olivebbs\Map\Enum\KeyType; -$myIntMap = new GenericMap(GenericMap::INT, GenericMap::INT); +$myIntMap = new GenericMap(KeyType::INT, ValueType::INT); ``` Then use it. ```php diff --git a/src/Olivebbs/Map/CharMap.php b/src/Olivebbs/Map/CharMap.php index a6f961c..f4546da 100644 --- a/src/Olivebbs/Map/CharMap.php +++ b/src/Olivebbs/Map/CharMap.php @@ -28,13 +28,15 @@ namespace Olivebbs\Map; use function array_combine; +use Olivebbs\Map\Enum\KeyType; +use Olivebbs\Map\Enum\ValueType; use function str_split; class CharMap extends GenericMap { public function __construct(array $initialValues = []) { - parent::__construct(keyType: self::CHAR, valueType: self::CHAR); + parent::__construct(keyType: KeyType::CHAR, valueType: ValueType::CHAR); $this->map->putAll(values: $initialValues); } diff --git a/src/Olivebbs/Map/Enum/KeyType.php b/src/Olivebbs/Map/Enum/KeyType.php new file mode 100644 index 0000000..e24673d --- /dev/null +++ b/src/Olivebbs/Map/Enum/KeyType.php @@ -0,0 +1,37 @@ +keyType = strtolower(string: $keyType); - - $this->valueType = class_exists(class: $valueType) ? $valueType : strtolower(string: $valueType); - - if (!$this->isValidKeyType(type: $this->keyType)) { - throw new InvalidArgumentException(message: sprintf('Invalid key type (%s)', $this->keyType)); - } + $this->keyTypeValue = $this->keyType->value; + $this->valueTypeValue = is_string(value: $this->valueType) ? $this->valueType : $this->valueType->value; if (!$this->isValidValueType(type: $this->valueType)) { - throw new InvalidArgumentException(message: sprintf('Invalid value type (%s)', $this->valueType)); + throw new InvalidArgumentException(message: sprintf('Invalid value type (%s)', $this->valueTypeValue)); } $this->map = new Map(); } - public function getKeyType(): string + public function getKeyType(): KeyType { return $this->keyType; } - public function getValueType(): string + public function getValueType(): object|string { return $this->valueType; } @@ -115,7 +101,7 @@ class GenericMap implements ArrayAccess, Countable */ public function offsetSet(mixed $offset, mixed $value): void { - $this->checkTypes($offset, $value); + $this->checkTypes(offset: $offset, value: $value); $this->map->offsetSet(offset: $offset, value: $value); } @@ -152,8 +138,8 @@ class GenericMap implements ArrayAccess, Countable protected function assertInitialValues(array $initialValues): bool { foreach ($initialValues as $key => $value) { - if (!$this->checkType(type: $this->keyType, var: $key) || !$this->checkType(type: $this->valueType, var: $value)) { - throw new InvalidArgumentException(message: sprintf('Invalid types for map [%s => %s], they should be [%s => %s]', get_debug_type(value: $key), get_debug_type(value: $value), $this->keyType, $this->valueType)); + if (!$this->checkType($this->keyType, $key) || !$this->checkType($this->valueType, $value)) { + throw new InvalidArgumentException(message: sprintf('Invalid types for map [%s => %s], they should be [%s => %s]', get_debug_type(value: $key), get_debug_type(value: $value), $this->keyType->value, is_string($this->valueType) ? $this->valueType : $this->valueType->value)); } } @@ -167,63 +153,42 @@ class GenericMap implements ArrayAccess, Countable } if (!$this->checkType(type: $this->keyType, var: $offset)) { - throw new TypeError(message: sprintf('Key should be of value %s.', $this->keyType)); + throw new InvalidArgumentException(message: sprintf('Key should be of value %s.', $this->keyTypeValue)); } if (null !== $value && !$this->checkType(type: $this->valueType, var: $value)) { - throw new ValueError(message: sprintf('Value should be of type %s.', $this->valueType)); + throw new InvalidArgumentException(message: sprintf('Value should be of type %s.', $this->valueTypeValue)); } } /** * @codeCoverageIgnore - * @param string $type - * @param mixed $var + * @param KeyType|ValueType|string $type + * @param mixed $var * * @return bool */ - private function checkType(string $type, mixed $var): bool + private function checkType(KeyType|ValueType|string $type, mixed $var): bool { return match ($type) { - self::OBJECT => is_object(value: $var), - self::CALLABLE => is_callable(value: $var), - self::ARRAY => is_array(value: $var), - self::INT, self::INTEGER => is_int(value: $var), - self::STRING => is_string(value: $var), - self::CHAR => is_string(value: $var) && (function_exists('mb_strlen') ? mb_strlen(string: $var) === 1 : strlen(string: $var) === 1), - self::ENUM => is_object(value: $var) && enum_exists(enum: $var::class), - self::ANY => true, + ValueType::OBJECT => is_object(value: $var), + ValueType::CALLABLE => is_callable(value: $var), + ValueType::ARRAY => is_array(value: $var), + KeyType::INT, KeyType::INTEGER, ValueType::INT, ValueType::INTEGER => is_int(value: $var), + KeyType::STRING, ValueType::STRING => is_string(value: $var), + KeyType::CHAR, ValueType::CHAR => is_string(value: $var) && (function_exists('mb_strlen') ? mb_strlen(string: $var) === 1 : strlen(string: $var) === 1), + ValueType::ENUM => is_object(value: $var) && enum_exists(enum: $var::class), + KeyType::ANY, ValueType::ANY => true, default => $var instanceof $type, }; } - private function isValidKeyType(string $type): bool + private function isValidValueType(object|string $type): bool { - return in_array(needle: $type, haystack: [ - self::INT, - self::INTEGER, - self::STRING, - self::CHAR, - self::ANY, - ], strict: true); - } - - private function isValidValueType(string $type): bool - { - if (class_exists(class: $type)) { + if (is_string(value: $type) && class_exists(class: $type)) { return true; } - return in_array(needle: $type, haystack: [ - self::OBJECT, - self::CALLABLE, - self::ARRAY, - self::INT, - self::INTEGER, - self::STRING, - self::CHAR, - self::ENUM, - self::ANY, - ], strict: true); + return $type instanceof ValueType; } } diff --git a/src/Olivebbs/Map/ImmutableMap.php b/src/Olivebbs/Map/ImmutableMap.php index 9e5aebd..06a259a 100644 --- a/src/Olivebbs/Map/ImmutableMap.php +++ b/src/Olivebbs/Map/ImmutableMap.php @@ -27,13 +27,15 @@ namespace Olivebbs\Map; +use Olivebbs\Map\Enum\KeyType; +use Olivebbs\Map\Enum\ValueType; use Olivebbs\Map\Exception\ImmutableMapException; class ImmutableMap extends GenericMap { private function __construct( - string $keyType, - string $valueType, + KeyType $keyType, + object|string $valueType, array $values ) { parent::__construct(keyType: $keyType, valueType: $valueType); @@ -52,7 +54,7 @@ class ImmutableMap extends GenericMap throw new ImmutableMapException(message: sprintf('Cannot unset values in %s', __CLASS__)); } - public static function create(string $keyType, string $valueType, array $values): static + public static function create(KeyType $keyType, ValueType $valueType, array $values): static { return new static(keyType: $keyType, valueType: $valueType, values: $values); } diff --git a/src/Olivebbs/Map/IntCharMap.php b/src/Olivebbs/Map/IntCharMap.php index 86bcb18..24ba548 100644 --- a/src/Olivebbs/Map/IntCharMap.php +++ b/src/Olivebbs/Map/IntCharMap.php @@ -27,13 +27,15 @@ namespace Olivebbs\Map; +use Olivebbs\Map\Enum\KeyType; +use Olivebbs\Map\Enum\ValueType; use function str_split; class IntCharMap extends GenericMap { public function __construct(array $initialValues = []) { - parent::__construct(keyType: self::INT, valueType: self::CHAR); + parent::__construct(keyType:KeyType::INT, valueType: ValueType::CHAR); $this->map->putAll(values: $initialValues); } diff --git a/tests/GenericMapTest.php b/tests/GenericMapTest.php index 99e6768..0dac638 100644 --- a/tests/GenericMapTest.php +++ b/tests/GenericMapTest.php @@ -27,10 +27,13 @@ namespace Olivebbs\Tests\Map; +use Olivebbs\Map\Enum\KeyType; +use Olivebbs\Map\Enum\ValueType; use Olivebbs\Map\Exception\InvalidArgumentException; use Olivebbs\Map\GenericMap; use PHPUnit\Framework\TestCase; -use ValueError; +use SplObjectStorage; +use stdClass; class GenericMapTest extends TestCase { @@ -46,13 +49,13 @@ class GenericMapTest extends TestCase public function testHashMap(): void { - $genericHashMap = new GenericMap(GenericMap::INT, GenericMap::INT); + $genericHashMap = new GenericMap(KeyType::INT, ValueType::INT); foreach (range(0, 9) as $range) { $genericHashMap[$range] = $range; } - self::assertSame(GenericMap::INT, $this->genericMap->getKeyType()); - self::assertSame(GenericMap::INT, $this->genericMap->getValueType()); + self::assertSame(KeyType::INT, $this->genericMap->getKeyType()); + self::assertSame(ValueType::INT, $this->genericMap->getValueType()); self::assertCount(10, $this->genericMap); self::assertEquals(10, $this->genericMap->count()); $array = array_combine(range(0, 9), range(0, 9)); @@ -64,15 +67,15 @@ class GenericMapTest extends TestCase unset($this->genericMap[1]); self::assertNull($this->genericMap[1]); - $this->expectException(\TypeError::class); + $this->expectException(InvalidArgumentException::class); $this->genericMap['H'] = 1; } public function testInvalidArgumentExceptionThrowsIsset(): void { - $genericHashMap = new GenericMap(GenericMap::ANY, GenericMap::ANY); + $genericHashMap = new GenericMap(KeyType::ANY, ValueType::ANY); $this->expectException(InvalidArgumentException::class); - $obj = new \stdClass(); + $obj = new stdClass(); /** @noinspection PhpExpressionResultUnusedInspection */ isset($genericHashMap[$obj]); } @@ -80,23 +83,17 @@ class GenericMapTest extends TestCase public function testTypeErrorThrowsIsset(): void { $genericMap = $this->getGenericMap(); - $this->expectException(\TypeError::class); + $this->expectException(InvalidArgumentException::class); /** @noinspection PhpExpressionResultUnusedInspection */ isset($genericMap['C']); } - public function testInvalidArgumentExceptionThrowsOnArrayOrObject(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid key type (array)'); - $genericMap = new GenericMap(GenericMap::ARRAY, GenericMap::ARRAY); - } public function testOffsetGetThrowsInvalidArgumentException(): void { $genericMap = $this->getGenericMap(); $this->expectException(InvalidArgumentException::class); - $obj = new \stdClass(); + $obj = new stdClass(); $test = $genericMap[$obj]; } @@ -104,7 +101,7 @@ class GenericMapTest extends TestCase { $genericMap = $this->getGenericMap(); $this->expectException(InvalidArgumentException::class); - $obj = new \stdClass(); + $obj = new stdClass(); $genericMap[$obj] = 1; } @@ -112,14 +109,14 @@ class GenericMapTest extends TestCase { $genericMap = $this->getGenericMap(); $this->expectException(InvalidArgumentException::class); - $obj = new \stdClass(); + $obj = new stdClass(); unset($genericMap[$obj]); } public function testOffsetSetThrowsValueErrorException(): void { $genericMap = $this->getGenericMap(); - $this->expectException(ValueError::class); + $this->expectException(InvalidArgumentException::class); $genericMap[0] = 'string'; } @@ -128,7 +125,7 @@ class GenericMapTest extends TestCase { $this->resetGenericMap(); - $this->expectException(\TypeError::class); + $this->expectException(InvalidArgumentException::class); unset($this->genericMap['C']); } @@ -136,7 +133,7 @@ class GenericMapTest extends TestCase public function testOffsetGetThrowsTypeErrorException(): void { $this->resetGenericMap(); - $this->expectException(\TypeError::class); + $this->expectException(InvalidArgumentException::class); $test = $this->genericMap['C']; } @@ -144,14 +141,14 @@ class GenericMapTest extends TestCase public function testThrowInvalidArgumentExceptionOnConstructValue(): void { $this->expectException(InvalidArgumentException::class); - $genericMap = new GenericMap(GenericMap::CHAR, 'test'); + $genericMap = new GenericMap(KeyType::CHAR, 'test'); } public function testUsingClassAsValue(): void { - $genericMap = new GenericMap(GenericMap::INT, \SplObjectStorage::class); - $splObject = new \SplObjectStorage(); - $stdClass = new \stdClass(); + $genericMap = new GenericMap(KeyType::INT, SplObjectStorage::class); + $splObject = new SplObjectStorage(); + $stdClass = new stdClass(); $splObject->attach($stdClass); $genericMap[0] = $splObject; self::assertSame($splObject, $genericMap[0]); @@ -160,8 +157,8 @@ class GenericMapTest extends TestCase public function testObjectCantBeUsedAsKeyWithAny(): void { - $genericMap = new GenericMap(GenericMap::ANY, GenericMap::ANY); - $object = new \SplObjectStorage(); + $genericMap = new GenericMap(KeyType::ANY, ValueType::ANY); + $object = new SplObjectStorage(); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Map keys cannot be objects or arrays'); $genericMap[$object] = 1; @@ -172,7 +169,7 @@ class GenericMapTest extends TestCase $enumMap = new class(TestEnum::cases()) extends GenericMap { public function __construct(array $values = []) { - parent::__construct(self::INT, self::ENUM); + parent::__construct(KeyType::INT, ValueType::ENUM); $this->assertInitialValues($values); $this->map->putAll($values); } @@ -190,6 +187,6 @@ class GenericMapTest extends TestCase private function getGenericMap(): GenericMap { - return new GenericMap(GenericMap::INT, GenericMap::INT); + return new GenericMap(KeyType::INT, ValueType::INT); } } diff --git a/tests/ImmutableMapTest.php b/tests/ImmutableMapTest.php index 5fa69d5..d95f99f 100644 --- a/tests/ImmutableMapTest.php +++ b/tests/ImmutableMapTest.php @@ -27,9 +27,10 @@ namespace Olivebbs\Tests\Map; +use Olivebbs\Map\Enum\KeyType; +use Olivebbs\Map\Enum\ValueType; use Olivebbs\Map\Exception\ImmutableMapException; use Olivebbs\Map\Exception\InvalidArgumentException; -use Olivebbs\Map\GenericMap; use Olivebbs\Map\ImmutableMap; use PHPUnit\Framework\TestCase; @@ -37,14 +38,14 @@ class ImmutableMapTest extends TestCase { public function testOffsetUnset(): void { - $immutableMap = ImmutableMap::create(GenericMap::INTEGER, GenericMap::CHAR, range('A', 'Z')); + $immutableMap = ImmutableMap::create(KeyType::INTEGER, ValueType::CHAR, range('A', 'Z')); $this->expectException(ImmutableMapException::class); unset($immutableMap[0]); } public function testOffsetSet(): void { - $immutableMap = ImmutableMap::create(GenericMap::INTEGER, GenericMap::CHAR, range('A', 'Z')); + $immutableMap = ImmutableMap::create(KeyType::INTEGER, ValueType::CHAR, range('A', 'Z')); $this->expectException(ImmutableMapException::class); $immutableMap[0] = 1; } @@ -58,6 +59,6 @@ class ImmutableMapTest extends TestCase ]; $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid types for map [string => int], they should be [int => string]'); - $immutableMap = ImmutableMap::create(GenericMap::INT, GenericMap::STRING, $values); + $immutableMap = ImmutableMap::create(KeyType::INT, ValueType::STRING, $values); } }