From 20436468f08f89ac49c32d1dda07b82ad85d8a77 Mon Sep 17 00:00:00 2001 From: Tomasz Kowalczyk Date: Tue, 30 Jan 2024 01:25:17 +0100 Subject: [PATCH] introduced Psalm templates to restrict members and values on type level --- psalm.xml | 6 ++++++ src/Enum/AbstractAttributeEnum.php | 4 ++++ src/Enum/AbstractCallbackEnum.php | 4 ++++ src/Enum/AbstractConstantsEnum.php | 4 ++++ src/Enum/AbstractDocblockEnum.php | 4 ++++ src/Enum/AbstractStaticEnum.php | 4 ++++ src/Enum/AttributeEnumTrait.php | 4 ++++ src/Enum/CallbackEnumTrait.php | 4 ++++ src/Enum/ConstantsEnumTrait.php | 4 ++++ src/Enum/DocblockEnumTrait.php | 4 ++++ src/Enum/EnumTrait.php | 31 ++++++++++++++++++++---------- src/Enum/StaticEnumTrait.php | 4 ++++ tests/Psalm/SeasonsExtend.php | 28 +++++++++++++++++++++++++++ tests/Psalm/SeasonsTrait.php | 28 +++++++++++++++++++++++++++ 14 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 tests/Psalm/SeasonsExtend.php create mode 100644 tests/Psalm/SeasonsTrait.php diff --git a/psalm.xml b/psalm.xml index bb4ac1f..8e29c4f 100644 --- a/psalm.xml +++ b/psalm.xml @@ -9,6 +9,7 @@ + @@ -30,6 +31,11 @@ + + + + + diff --git a/src/Enum/AbstractAttributeEnum.php b/src/Enum/AbstractAttributeEnum.php index 15224e6..b2c7893 100644 --- a/src/Enum/AbstractAttributeEnum.php +++ b/src/Enum/AbstractAttributeEnum.php @@ -3,9 +3,13 @@ namespace Thunder\Platenum\Enum; /** + * @template TMember + * @template TValue + * * @author Tomasz Kowalczyk */ abstract class AbstractAttributeEnum implements \JsonSerializable { + /** @use AttributeEnumTrait */ use AttributeEnumTrait; } diff --git a/src/Enum/AbstractCallbackEnum.php b/src/Enum/AbstractCallbackEnum.php index 49d812f..a93946c 100644 --- a/src/Enum/AbstractCallbackEnum.php +++ b/src/Enum/AbstractCallbackEnum.php @@ -3,9 +3,13 @@ namespace Thunder\Platenum\Enum; /** + * @template TMember + * @template TValue + * * @author Tomasz Kowalczyk */ abstract class AbstractCallbackEnum implements \JsonSerializable { + /** @use CallbackEnumTrait */ use CallbackEnumTrait; } diff --git a/src/Enum/AbstractConstantsEnum.php b/src/Enum/AbstractConstantsEnum.php index 696b0d6..03e44a0 100644 --- a/src/Enum/AbstractConstantsEnum.php +++ b/src/Enum/AbstractConstantsEnum.php @@ -3,9 +3,13 @@ namespace Thunder\Platenum\Enum; /** + * @template TMember + * @template TValue + * * @author Tomasz Kowalczyk */ abstract class AbstractConstantsEnum implements \JsonSerializable { + /** @use ConstantsEnumTrait */ use ConstantsEnumTrait; } diff --git a/src/Enum/AbstractDocblockEnum.php b/src/Enum/AbstractDocblockEnum.php index d7d994c..b213584 100644 --- a/src/Enum/AbstractDocblockEnum.php +++ b/src/Enum/AbstractDocblockEnum.php @@ -3,9 +3,13 @@ namespace Thunder\Platenum\Enum; /** + * @template TMember + * @template TValue + * * @author Tomasz Kowalczyk */ abstract class AbstractDocblockEnum implements \JsonSerializable { + /** @use DocblockEnumTrait */ use DocblockEnumTrait; } diff --git a/src/Enum/AbstractStaticEnum.php b/src/Enum/AbstractStaticEnum.php index 6f347fa..a93395a 100644 --- a/src/Enum/AbstractStaticEnum.php +++ b/src/Enum/AbstractStaticEnum.php @@ -3,10 +3,14 @@ namespace Thunder\Platenum\Enum; /** + * @template TMember + * @template TValue + * * @author Tomasz Kowalczyk */ abstract class AbstractStaticEnum implements \JsonSerializable { + /** @use StaticEnumTrait */ use StaticEnumTrait; /** @var array */ diff --git a/src/Enum/AttributeEnumTrait.php b/src/Enum/AttributeEnumTrait.php index 38274be..faf78ae 100644 --- a/src/Enum/AttributeEnumTrait.php +++ b/src/Enum/AttributeEnumTrait.php @@ -3,10 +3,14 @@ namespace Thunder\Platenum\Enum; /** + * @template TMember + * @template TValue + * * @author Tomasz Kowalczyk */ trait AttributeEnumTrait { + /** @use EnumTrait */ use EnumTrait; /** @psalm-suppress UndefinedDocblockClass, UndefinedMethod because there is no ReflectionAttribute on PHP <8.0 */ diff --git a/src/Enum/CallbackEnumTrait.php b/src/Enum/CallbackEnumTrait.php index 6c8b773..2195efc 100644 --- a/src/Enum/CallbackEnumTrait.php +++ b/src/Enum/CallbackEnumTrait.php @@ -5,10 +5,14 @@ use Thunder\Platenum\Exception\PlatenumException; /** + * @template TMember + * @template TValue + * * @author Tomasz Kowalczyk */ trait CallbackEnumTrait { + /** @use EnumTrait */ use EnumTrait; /** @var non-empty-array> */ diff --git a/src/Enum/ConstantsEnumTrait.php b/src/Enum/ConstantsEnumTrait.php index 4a1aea5..9258892 100644 --- a/src/Enum/ConstantsEnumTrait.php +++ b/src/Enum/ConstantsEnumTrait.php @@ -3,10 +3,14 @@ namespace Thunder\Platenum\Enum; /** + * @template TMember + * @template TValue + * * @author Tomasz Kowalczyk */ trait ConstantsEnumTrait { + /** @use EnumTrait */ use EnumTrait; private static function resolve(): array diff --git a/src/Enum/DocblockEnumTrait.php b/src/Enum/DocblockEnumTrait.php index 0784a7d..9ff7557 100644 --- a/src/Enum/DocblockEnumTrait.php +++ b/src/Enum/DocblockEnumTrait.php @@ -5,10 +5,14 @@ use Thunder\Platenum\Exception\PlatenumException; /** + * @template TMember + * @template TValue + * * @author Tomasz Kowalczyk */ trait DocblockEnumTrait { + /** @use EnumTrait */ use EnumTrait; private static function resolve(): array diff --git a/src/Enum/EnumTrait.php b/src/Enum/EnumTrait.php index 9f5816f..652d3bd 100644 --- a/src/Enum/EnumTrait.php +++ b/src/Enum/EnumTrait.php @@ -5,13 +5,16 @@ use Thunder\Platenum\Exception\PlatenumException; /** + * @template TMember + * @template TValue + * * @author Tomasz Kowalczyk */ trait EnumTrait { - /** @var string */ + /** @var TMember */ private $member; - /** @var int|string */ + /** @var TValue */ private $value; /** @var non-empty-array> */ @@ -19,7 +22,11 @@ trait EnumTrait /** @var array> */ protected static $instances = []; - /** @param int|string $value */ + + /** + * @param TMember $member + * @param TValue $value + */ /* final */ private function __construct(string $member, $value) { $this->member = $member; @@ -51,7 +58,7 @@ final public static function fromMember(string $member): self static::throwDefaultInvalidMemberException($member); } - /** @psalm-suppress UnsafeInstantiation */ + /** @psalm-suppress UnsafeInstantiation,ArgumentTypeCoercion,PropertyTypeCoercion */ return static::$instances[$class][$member] = new static($member, static::$members[$class][$member]); } @@ -91,6 +98,7 @@ final public static function fromEnum($enum): self } /** + * @psalm-suppress ReferenceConstraintViolation * @param static $enum * @param-out AbstractConstantsEnum|AbstractDocblockEnum|AbstractStaticEnum|AbstractCallbackEnum|AbstractAttributeEnum $enum */ @@ -149,17 +157,19 @@ final public function equals($other): bool /* --- TRANSFORM --- */ + /** @return TMember */ final public function getMember(): string { return $this->member; } - /** @return int|string */ + /** @return TValue */ final public function getValue() { return $this->value; } + /** @psalm-suppress MissingReturnType */ #[\ReturnTypeWillChange] final public function jsonSerialize() { @@ -204,24 +214,25 @@ final public static function valuesExist(array $values): bool return [] !== array_intersect(static::$members[static::class], $values); } - final public function hasMember(string $members): bool + /** @param TMember $member */ + final public function hasMember(string $member): bool { - return $members === $this->member; + return $member === $this->member; } - /** @param int|string $value */ + /** @param TValue $value */ final public function hasValue($value): bool { return $value === $this->value; } - /** @param list $members */ + /** @param list $members */ final public function hasMemberIn(array $members): bool { return in_array($this->member, $members, true); } - /** @param list $values */ + /** @param list $values */ final public function hasValueIn(array $values): bool { return in_array($this->value, $values, true); diff --git a/src/Enum/StaticEnumTrait.php b/src/Enum/StaticEnumTrait.php index 738cee2..fe10e1a 100644 --- a/src/Enum/StaticEnumTrait.php +++ b/src/Enum/StaticEnumTrait.php @@ -5,10 +5,14 @@ use Thunder\Platenum\Exception\PlatenumException; /** + * @template TMember + * @template TValue + * * @author Tomasz Kowalczyk */ trait StaticEnumTrait { + /** @use EnumTrait */ use EnumTrait; private static function resolve(): array diff --git a/tests/Psalm/SeasonsExtend.php b/tests/Psalm/SeasonsExtend.php new file mode 100644 index 0000000..f089a2c --- /dev/null +++ b/tests/Psalm/SeasonsExtend.php @@ -0,0 +1,28 @@ + + * @psalm-suppress PropertyNotSetInConstructor + */ +final class SeasonsExtend extends AbstractConstantsEnum +{ + private const SPRING = 1; + private const SUMMER = 2; + private const AUTUMN = 3; + private const WINTER = 4; +} + +$spring = SeasonsExtend::SPRING(); +/** @psalm-suppress InvalidArgument */ +$spring->hasMember('INVALID'); +/** @psalm-suppress InvalidArgument */ +$spring->hasValue(9); diff --git a/tests/Psalm/SeasonsTrait.php b/tests/Psalm/SeasonsTrait.php new file mode 100644 index 0000000..7a2b46f --- /dev/null +++ b/tests/Psalm/SeasonsTrait.php @@ -0,0 +1,28 @@ + */ + use ConstantsEnumTrait; +} + +$spring = SeasonsTrait::SPRING(); +/** @psalm-suppress InvalidArgument */ +$spring->hasMember('INVALID'); +/** @psalm-suppress InvalidArgument */ +$spring->hasValue(9);