• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

azjezz / psl / 17432557268

03 Sep 2025 11:52AM UTC coverage: 98.446% (-0.07%) from 98.513%
17432557268

push

github

web-flow
chore: migrate from `psalm` to `mago` (#527)

232 of 241 new or added lines in 81 files covered. (96.27%)

14 existing lines in 12 files now uncovered.

5510 of 5597 relevant lines covered (98.45%)

52.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.67
/src/Psl/Type/Internal/BackedEnumValueType.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Psl\Type\Internal;
6

7
use BackedEnum;
8
use Override;
9
use Psl\Exception\InvariantViolationException;
10
use Psl\Exception\RuntimeException;
11
use Psl\Type\Exception\AssertException;
12
use Psl\Type\Exception\CoercionException;
13
use Psl\Type\Type;
14
use ReflectionEnum;
15
use ReflectionException;
16
use ReflectionNamedType;
17

18
use function is_a;
19
use function is_int;
20
use function is_string;
21
use function Psl\invariant;
22
use function Psl\Type\int;
23
use function Psl\Type\string;
24

25
/**
26
 * @template T of BackedEnum
27
 *
28
 * @extends Type<value-of<T>>
29
 *
30
 * @internal
31
 */
32
final readonly class BackedEnumValueType extends Type
33
{
34
    private bool $isStringBacked;
35

36
    /**
37
     * @psalm-mutation-free
38
     *
39
     * @param enum-string<T> $enum
40
     *
41
     * @throws RuntimeException If reflection fails.
42
     * @throws InvariantViolationException If the given value is not enum-string<BackedEnum>.
43
     */
44
    public function __construct(
45
        private string $enum,
46
    ) {
47
        $this->isStringBacked = $this->hasStringBackingType($this->enum);
75✔
48
    }
49

50
    /**
51
     * @param enum-string<T> $enum
52
     *
53
     * @throws RuntimeException If reflection fails.
54
     * @throws InvariantViolationException If the given value is not enum-string<BackedEnum>.
55
     */
56
    private function hasStringBackingType(string $enum): bool
57
    {
58
        invariant(is_a($enum, BackedEnum::class, true), 'A BackedEnum enum-string is required');
75✔
59

60
        // If the enum has any cases, detect its type by inspecting the first case found
61
        $case = $enum::cases()[0] ?? null;
74✔
62
        if ($case !== null) {
74✔
63
            return is_string($case->value);
72✔
64
        }
65

66
        // Fallback to reflection to detect the backing type:
67
        try {
68
            $reflection = new ReflectionEnum($enum);
2✔
69
            $type = $reflection->getBackingType();
2✔
70
            invariant($type instanceof ReflectionNamedType, 'Unexpected type');
2✔
71
            return $type->getName() === 'string';
2✔
72
        } catch (ReflectionException $e) {
×
NEW
73
            throw new RuntimeException('Failed to reflect an enum enum-string', 0, $e);
×
74
        }
75
    }
76

77
    /**
78
     * @psalm-assert-if-true value-of<T> $value
79
     */
80
    #[Override]
81
    public function matches(mixed $value): bool
82
    {
83
        return match ($this->isStringBacked) {
62✔
84
            true => is_string($value) && $this->enum::tryFrom($value) !== null,
62✔
85
            false => is_int($value) && $this->enum::tryFrom($value) !== null,
62✔
86
        };
62✔
87
    }
88

89
    /**
90
     * @throws CoercionException
91
     *
92
     * @return value-of<T>
93
     *
94
     * @mago-expect lint:no-empty-catch-clause
95
     */
96
    #[Override]
97
    public function coerce(mixed $value): string|int
98
    {
99
        try {
100
            $case = $this->isStringBacked ? string()->coerce($value) : int()->coerce($value);
20✔
101

102
            if ($this->matches($case)) {
12✔
103
                return $case;
12✔
104
            }
105
        } catch (CoercionException) {
8✔
106
        }
107

108
        throw CoercionException::withValue($value, $this->toString());
11✔
109
    }
110

111
    /**
112
     * @throws AssertException
113
     *
114
     * @return value-of<T>
115
     *
116
     * @psalm-assert value-of<T> $value
117
     */
118
    #[Override]
119
    public function assert(mixed $value): string|int
120
    {
121
        if ($this->matches($value)) {
25✔
122
            return $value;
9✔
123
        }
124

125
        throw AssertException::withValue($value, $this->toString());
16✔
126
    }
127

128
    #[Override]
129
    public function toString(): string
130
    {
131
        return 'value-of<' . $this->enum . '>';
29✔
132
    }
133
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc