diff --git a/phpstan.neon b/phpstan.neon index 24bc819..6f3291e 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,7 +5,6 @@ parameters: - tests excludePaths: - tests/Envs/* - checkGenericClassInNonGenericObjectType: false ignoreErrors: # - # message: '#type has no value type specified in iterable type array#' diff --git a/src/EnvMapper.php b/src/EnvMapper.php index 539b9b1..2e8d619 100644 --- a/src/EnvMapper.php +++ b/src/EnvMapper.php @@ -76,11 +76,22 @@ public function map(string $class, bool $requireValues = false, ?array $source = * @return int|float|string|bool * The passed value, but now with the correct type. */ - private function typeNormalize(string $val, \ReflectionProperty $rProp): int|float|string|bool + private function typeNormalize(string $val, \ReflectionProperty $rProp): int|float|string|bool|\BackedEnum { $rType = $rProp->getType(); if ($rType instanceof \ReflectionNamedType) { - return match ($rType->getName()) { + $name = $rType->getName(); + + if (is_a($name, \BackedEnum::class, true)) { + $rEnum = new \ReflectionEnum($name); + $backingType = $rEnum->getBackingType(); + assert($backingType instanceof \ReflectionNamedType); + $isIntBacked = $backingType->getName() === 'int'; + + return $name::from($isIntBacked ? (int) $val : $val); + } + + return match ($name) { 'string' => $val, 'float' => is_numeric($val) ? (float) $val @@ -118,6 +129,9 @@ protected function getDefaultValue(\ReflectionProperty $subject): mixed } /** + * @template T of object + * @param \ReflectionClass $rClass + * * @return array */ protected function getConstructorArgs(\ReflectionClass $rClass): array @@ -126,6 +140,9 @@ protected function getConstructorArgs(\ReflectionClass $rClass): array } /** + * @template T of object + * @param \ReflectionClass $rClass + * * @return array */ protected function makeConstructorArgs(\ReflectionClass $rClass): array diff --git a/tests/EnvMapperTest.php b/tests/EnvMapperTest.php index b713450..f0e189d 100644 --- a/tests/EnvMapperTest.php +++ b/tests/EnvMapperTest.php @@ -7,7 +7,9 @@ use Crell\EnvMapper\Envs\EnvWithDefaults; use Crell\EnvMapper\Envs\EnvWithMissingValue; use Crell\EnvMapper\Envs\EnvWithTypeMismatch; +use Crell\EnvMapper\Envs\IntegerBackedEnum; use Crell\EnvMapper\Envs\SampleEnvironment; +use Crell\EnvMapper\Envs\StringBackedEnum; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; @@ -22,6 +24,8 @@ class EnvMapperTest extends TestCase 'SHLVL' => '1', 'ZIP_CODE' => '01234', 'BOOL' => '1', + 'STRING_BACKED_ENUM' => 'FOO', + 'INTEGER_BACKED_ENUM' => '2', ]; #[Test] @@ -37,6 +41,10 @@ public function mapping_different_types_with_defaults_parses_correctly(): void self::assertNotNull($env->PATH); self::assertNotNull($env->hostname); self::assertNotNull($env->shlvl); + self::assertSame('01234', $env->zipCode); + self::assertSame(true, $env->bool); + self::assertEquals(StringBackedEnum::Foo, $env->stringBackedEnum); + self::assertEquals(IntegerBackedEnum::Bar, $env->integerBackedEnum); self::assertEquals('default', $env->missing); self::assertEquals(false, $env->missingFalse); self::assertEquals('', $env->missingEmptyString); diff --git a/tests/Envs/IntegerBackedEnum.php b/tests/Envs/IntegerBackedEnum.php new file mode 100644 index 0000000..bd673c6 --- /dev/null +++ b/tests/Envs/IntegerBackedEnum.php @@ -0,0 +1,11 @@ +