From bf87c2c5df0b6da5745290c1824673236aaa86d4 Mon Sep 17 00:00:00 2001 From: Bram Van der Sype Date: Wed, 15 Oct 2025 20:13:38 +0200 Subject: [PATCH 1/4] Added assertion that ensures zipCode is still a string --- tests/EnvMapperTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/EnvMapperTest.php b/tests/EnvMapperTest.php index b713450..d776b48 100644 --- a/tests/EnvMapperTest.php +++ b/tests/EnvMapperTest.php @@ -37,6 +37,7 @@ 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::assertEquals('default', $env->missing); self::assertEquals(false, $env->missingFalse); self::assertEquals('', $env->missingEmptyString); From 73a725ee4a7cd66da870b8f6476466f560581c39 Mon Sep 17 00:00:00 2001 From: Bram Van der Sype Date: Wed, 15 Oct 2025 20:14:22 +0200 Subject: [PATCH 2/4] Added assertion that ensures bool is a boolean --- tests/EnvMapperTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/EnvMapperTest.php b/tests/EnvMapperTest.php index d776b48..c4e036d 100644 --- a/tests/EnvMapperTest.php +++ b/tests/EnvMapperTest.php @@ -38,6 +38,7 @@ public function mapping_different_types_with_defaults_parses_correctly(): void self::assertNotNull($env->hostname); self::assertNotNull($env->shlvl); self::assertSame('01234', $env->zipCode); + self::assertSame(true, $env->bool); self::assertEquals('default', $env->missing); self::assertEquals(false, $env->missingFalse); self::assertEquals('', $env->missingEmptyString); From 91aee3186ace4816cb85841bb004eb42eb895085 Mon Sep 17 00:00:00 2001 From: Bram Van der Sype Date: Wed, 15 Oct 2025 20:40:20 +0200 Subject: [PATCH 3/4] Added ability to map to backed enums --- src/EnvMapper.php | 15 +++++++++++++-- tests/EnvMapperTest.php | 6 ++++++ tests/Envs/IntegerBackedEnum.php | 11 +++++++++++ tests/Envs/SampleEnvironment.php | 3 +++ tests/Envs/StringBackedEnum.php | 11 +++++++++++ 5 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 tests/Envs/IntegerBackedEnum.php create mode 100644 tests/Envs/StringBackedEnum.php diff --git a/src/EnvMapper.php b/src/EnvMapper.php index 539b9b1..1b212be 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 diff --git a/tests/EnvMapperTest.php b/tests/EnvMapperTest.php index c4e036d..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] @@ -39,6 +43,8 @@ public function mapping_different_types_with_defaults_parses_correctly(): void 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 @@ + Date: Wed, 15 Oct 2025 20:42:43 +0200 Subject: [PATCH 4/4] Removed deprecated phpstan config and added docblocks --- phpstan.neon | 1 - src/EnvMapper.php | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) 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 1b212be..2e8d619 100644 --- a/src/EnvMapper.php +++ b/src/EnvMapper.php @@ -129,6 +129,9 @@ protected function getDefaultValue(\ReflectionProperty $subject): mixed } /** + * @template T of object + * @param \ReflectionClass $rClass + * * @return array */ protected function getConstructorArgs(\ReflectionClass $rClass): array @@ -137,6 +140,9 @@ protected function getConstructorArgs(\ReflectionClass $rClass): array } /** + * @template T of object + * @param \ReflectionClass $rClass + * * @return array */ protected function makeConstructorArgs(\ReflectionClass $rClass): array