Skip to content

Commit

Permalink
WIP introduced Psalm templates with ability to restrict possible valu…
Browse files Browse the repository at this point in the history
…es on type level
  • Loading branch information
thunderer committed Jun 6, 2022
1 parent 89c4ceb commit 035e427
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 10 deletions.
1 change: 1 addition & 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 Down
5 changes: 4 additions & 1 deletion src/Doctrine/PlatenumDoctrineType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
use Doctrine\DBAL\Types\Type;
use Thunder\Platenum\Enum\EnumTrait;

/** @psalm-suppress PropertyNotSetInConstructor, MissingConstructor */
/**
* @psalm-suppress PropertyNotSetInConstructor, MissingConstructor
* @psalm-external-mutation-free
*/
final class PlatenumDoctrineType extends Type
{
/** @var class-string */
Expand Down
3 changes: 3 additions & 0 deletions src/Enum/AbstractCallbackEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

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

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

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

/**
* @author Tomasz Kowalczyk <[email protected]>
* @psalm-template T
* @psalm-immutable
*/
abstract class AbstractStaticEnum implements \JsonSerializable
{
/** @use StaticEnumTrait<T> */
use StaticEnumTrait;

/** @var array */
Expand Down
3 changes: 3 additions & 0 deletions src/Enum/CallbackEnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@

/**
* @author Tomasz Kowalczyk <[email protected]>
* @psalm-template T
* @psalm-immutable
*/
trait CallbackEnumTrait
{
/** @use EnumTrait<T> */
use EnumTrait;

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

/**
* @author Tomasz Kowalczyk <[email protected]>
* @psalm-template T
* @psalm-immutable
*/
trait ConstantsEnumTrait
{
/** @use EnumTrait<T> */
use EnumTrait;

private static function resolve(): array
Expand Down
3 changes: 3 additions & 0 deletions src/Enum/DocblockEnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@

/**
* @author Tomasz Kowalczyk <[email protected]>
* @psalm-template T
* @psalm-immutable
*/
trait DocblockEnumTrait
{
/** @use EnumTrait<T> */
use EnumTrait;

private static function resolve(): array
Expand Down
30 changes: 21 additions & 9 deletions src/Enum/EnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@

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

/** @var non-empty-array<string,non-empty-array<string,int|string>> */
/** @psalm-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 */
/** @psalm-param T $value */
final private function __construct(string $member, $value)
{
$this->member = $member;
Expand Down Expand Up @@ -51,8 +53,17 @@ final public static function fromMember(string $member): self
static::throwDefaultInvalidMemberException($member);
}

return static::$instances[$class][$member] = static::fromMemberAndValue($member, static::$members[$class][$member]);
}

/**
* @psalm-param int|string $value
* @psalm-return static
*/
final private static function fromMemberAndValue(string $member, $value): self
{
/** @psalm-suppress UnsafeInstantiation */
return static::$instances[$class][$member] = new static($member, static::$members[$class][$member]);
return new static($member, $value);
}

/**
Expand Down Expand Up @@ -91,8 +102,8 @@ final public static function fromEnum($enum): self
}

/**
* @param static $enum
* @param-out AbstractConstantsEnum|AbstractDocblockEnum|AbstractStaticEnum|AbstractCallbackEnum|AbstractAttributeEnum $enum
* @param self &$enum
* @param-out self $enum
*/
final public function fromInstance(&$enum): void
{
Expand Down Expand Up @@ -140,12 +151,13 @@ final public function getMember(): string
return $this->member;
}

/** @return int|string */
/** @psalm-return T */
final public function getValue()
{
return $this->value;
}

/** @psalm-return T */
#[\ReturnTypeWillChange]
final public function jsonSerialize()
{
Expand Down Expand Up @@ -179,7 +191,7 @@ final public function hasMember(string $members): bool
return $members === $this->member;
}

/** @param int|string $value */
/** @psalm-param T $value */
final public function hasValue($value): bool
{
return $value === $this->value;
Expand Down Expand Up @@ -245,7 +257,7 @@ private static function resolveMembers(): void
// reflection instead of method_exists because of PHP 7.4 bug #78632
// @see https://bugs.php.net/bug.php?id=78632
$hasResolve = (new \ReflectionClass($class))->hasMethod('resolve');
/** @var array<string,int|string> $members */
/** @psalm-var array<string,T> $members */
$members = $hasResolve ? static::resolve() : $throwMissingResolve($class);
if(empty($members)) {
throw PlatenumException::fromEmptyMembers($class);
Expand Down
3 changes: 3 additions & 0 deletions src/Enum/StaticEnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@

/**
* @author Tomasz Kowalczyk <[email protected]>
* @psalm-template T
* @psalm-immutable
*/
trait StaticEnumTrait
{
/** @use EnumTrait<T> */
use EnumTrait;

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

use Thunder\Platenum\Enum\AbstractConstantsEnum;
use Thunder\Platenum\Enum\ConstantsEnumTrait;

final class PsalmEnum
{
}

/**
* @method static static FIRST()
* @method static static SECOND()
* @psalm-template T of self::*
* @psalm-immutable
*/
final class PsalmConstantsTraitEnum
{
/** @use ConstantsEnumTrait<T> */
use ConstantsEnumTrait;

public const FIRST = 1;
public const SECOND = 2;
}

PsalmConstantsTraitEnum::FIRST();
PsalmConstantsTraitEnum::SECOND();
PsalmConstantsTraitEnum::fromMember('THIRD');
PsalmConstantsTraitEnum::fromValue(4);

/**
* @method static static FIRST()
* @method static static SECOND()
* @psalm-template T of self::*
* @psalm-immutable
*/
final class PsalmConstantsExtendsEnum extends AbstractConstantsEnum
{
public const FIRST = 1;
public const SECOND = 2;
}

PsalmConstantsExtendsEnum::FIRST();
PsalmConstantsExtendsEnum::SECOND();
PsalmConstantsExtendsEnum::fromMember('THIRD');
PsalmConstantsExtendsEnum::fromValue(4);

0 comments on commit 035e427

Please sign in to comment.