Skip to content

Commit

Permalink
introduced Psalm templates to restrict members and values on type level
Browse files Browse the repository at this point in the history
  • Loading branch information
thunderer committed Feb 3, 2024
1 parent fad79b0 commit 2043646
Show file tree
Hide file tree
Showing 14 changed files with 123 additions and 10 deletions.
6 changes: 6 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

<projectFiles>
<directory name="src" />
<directory name="tests/Psalm" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
Expand All @@ -30,6 +31,11 @@
<file name="src/Doctrine/PlatenumDoctrineType.php" />
</errorLevel>
</DeprecatedMethod>
<DocblockTypeContradiction errorLevel="error">
<errorLevel type="suppress">
<file name="src/Enum/EnumTrait.php" /> <!-- PHP 7.2 only? -->
</errorLevel>
</DocblockTypeContradiction>
</issueHandlers>

</psalm>
4 changes: 4 additions & 0 deletions src/Enum/AbstractAttributeEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
namespace Thunder\Platenum\Enum;

/**
* @template TMember
* @template TValue
*
* @author Tomasz Kowalczyk <[email protected]>
*/
abstract class AbstractAttributeEnum implements \JsonSerializable
{
/** @use AttributeEnumTrait<TMember,TValue> */
use AttributeEnumTrait;
}
4 changes: 4 additions & 0 deletions src/Enum/AbstractCallbackEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
namespace Thunder\Platenum\Enum;

/**
* @template TMember
* @template TValue
*
* @author Tomasz Kowalczyk <[email protected]>
*/
abstract class AbstractCallbackEnum implements \JsonSerializable
{
/** @use CallbackEnumTrait<TMember,TValue> */
use CallbackEnumTrait;
}
4 changes: 4 additions & 0 deletions src/Enum/AbstractConstantsEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
namespace Thunder\Platenum\Enum;

/**
* @template TMember
* @template TValue
*
* @author Tomasz Kowalczyk <[email protected]>
*/
abstract class AbstractConstantsEnum implements \JsonSerializable
{
/** @use ConstantsEnumTrait<TMember,TValue> */
use ConstantsEnumTrait;
}
4 changes: 4 additions & 0 deletions src/Enum/AbstractDocblockEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
namespace Thunder\Platenum\Enum;

/**
* @template TMember
* @template TValue
*
* @author Tomasz Kowalczyk <[email protected]>
*/
abstract class AbstractDocblockEnum implements \JsonSerializable
{
/** @use DocblockEnumTrait<TMember,TValue> */
use DocblockEnumTrait;
}
4 changes: 4 additions & 0 deletions src/Enum/AbstractStaticEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
namespace Thunder\Platenum\Enum;

/**
* @template TMember
* @template TValue
*
* @author Tomasz Kowalczyk <[email protected]>
*/
abstract class AbstractStaticEnum implements \JsonSerializable
{
/** @use StaticEnumTrait<TMember,TValue> */
use StaticEnumTrait;

/** @var array */
Expand Down
4 changes: 4 additions & 0 deletions src/Enum/AttributeEnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
namespace Thunder\Platenum\Enum;

/**
* @template TMember
* @template TValue
*
* @author Tomasz Kowalczyk <[email protected]>
*/
trait AttributeEnumTrait
{
/** @use EnumTrait<TMember,TValue> */
use EnumTrait;

/** @psalm-suppress UndefinedDocblockClass, UndefinedMethod because there is no ReflectionAttribute on PHP <8.0 */
Expand Down
4 changes: 4 additions & 0 deletions src/Enum/CallbackEnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
use Thunder\Platenum\Exception\PlatenumException;

/**
* @template TMember
* @template TValue
*
* @author Tomasz Kowalczyk <[email protected]>
*/
trait CallbackEnumTrait
{
/** @use EnumTrait<TMember,TValue> */
use EnumTrait;

/** @var non-empty-array<class-string,callable():array<string,int|string>> */
Expand Down
4 changes: 4 additions & 0 deletions src/Enum/ConstantsEnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
namespace Thunder\Platenum\Enum;

/**
* @template TMember
* @template TValue
*
* @author Tomasz Kowalczyk <[email protected]>
*/
trait ConstantsEnumTrait
{
/** @use EnumTrait<TMember,TValue> */
use EnumTrait;

private static function resolve(): array
Expand Down
4 changes: 4 additions & 0 deletions src/Enum/DocblockEnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
use Thunder\Platenum\Exception\PlatenumException;

/**
* @template TMember
* @template TValue
*
* @author Tomasz Kowalczyk <[email protected]>
*/
trait DocblockEnumTrait
{
/** @use EnumTrait<TMember,TValue> */
use EnumTrait;

private static function resolve(): array
Expand Down
31 changes: 21 additions & 10 deletions src/Enum/EnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,28 @@
use Thunder\Platenum\Exception\PlatenumException;

/**
* @template TMember
* @template TValue
*
* @author Tomasz Kowalczyk <[email protected]>
*/
trait EnumTrait
{
/** @var string */
/** @var TMember */
private $member;
/** @var int|string */
/** @var TValue */
private $value;

/** @var non-empty-array<string,non-empty-array<string,int|string>> */
protected static $members = [];
/** @var array<string,array<string,static>> */
protected static $instances = [];

/** @param int|string $value */

/**
* @param TMember $member
* @param TValue $value
*/
/* final */ private function __construct(string $member, $value)
{
$this->member = $member;
Expand Down Expand Up @@ -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]);
}

Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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<string> $members */
/** @param list<TMember> $members */
final public function hasMemberIn(array $members): bool
{
return in_array($this->member, $members, true);
}

/** @param list<int|string> $values */
/** @param list<TValue> $values */
final public function hasValueIn(array $values): bool
{
return in_array($this->value, $values, true);
Expand Down
4 changes: 4 additions & 0 deletions src/Enum/StaticEnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
use Thunder\Platenum\Exception\PlatenumException;

/**
* @template TMember
* @template TValue
*
* @author Tomasz Kowalczyk <[email protected]>
*/
trait StaticEnumTrait
{
/** @use EnumTrait<TMember,TValue> */
use EnumTrait;

private static function resolve(): array
Expand Down
28 changes: 28 additions & 0 deletions tests/Psalm/SeasonsExtend.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Thunder\Platenum\Tests\Psalm;

use Thunder\Platenum\Enum\AbstractConstantsEnum;

/**
* @method static static SPRING()
* @method static static SUMMER()
* @method static static AUTUMN()
* @method static static WINTER()
*
* @extends AbstractConstantsEnum<'SPRING'|'SUMMER'|'AUTUMN'|'WINTER',SeasonsExtend::*>
* @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);
28 changes: 28 additions & 0 deletions tests/Psalm/SeasonsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Thunder\Platenum\Tests\Psalm;

use Thunder\Platenum\Enum\ConstantsEnumTrait;

/**
* @method static static SPRING()
* @method static static SUMMER()
* @method static static AUTUMN()
* @method static static WINTER()
*/
final class SeasonsTrait
{
private const SPRING = 1;
private const SUMMER = 2;
private const AUTUMN = 3;
private const WINTER = 4;

/** @use ConstantsEnumTrait<'SPRING'|'SUMMER'|'AUTUMN'|'WINTER',SeasonsTrait::*> */
use ConstantsEnumTrait;
}

$spring = SeasonsTrait::SPRING();
/** @psalm-suppress InvalidArgument */
$spring->hasMember('INVALID');
/** @psalm-suppress InvalidArgument */
$spring->hasValue(9);

0 comments on commit 2043646

Please sign in to comment.