@@ -20,7 +20,7 @@ class EnvMapper
2020 * @param bool $requireValues
2121 * If true, any unmatched properties will result in an exception. If false, unmatched properties
2222 * will be ignored, which in most cases means they will be uninitialized.
23- * @param array<string, mixed >|null $source
23+ * @param array<string, string|int|float >|null $source
2424 * The array to map from. If not specified, $_ENV will be used. Note that because the
2525 * primary use case is environment variables, the input array MUST have keys that are UPPER_CASE
2626 * strings.
@@ -39,7 +39,9 @@ public function map(string $class, bool $requireValues = false, ?array $source =
3939 $ propName = $ rProp ->getName ();
4040 $ envName = $ this ->normalizeName ($ propName );
4141 if (isset ($ source [$ envName ])) {
42- $ toSet [$ propName ] = $ this ->typeNormalize ($ source [$ envName ], $ rProp );
42+ /** @var string|int|float $val */
43+ $ val = $ source [$ envName ];
44+ $ toSet [$ propName ] = $ this ->typeNormalize ($ val , $ rProp );
4345 } elseif (PropValue::None !== $ default = $ this ->getDefaultValue ($ rProp )) {
4446 $ toSet [$ propName ] = $ default ;
4547 } elseif ($ requireValues ) {
@@ -71,12 +73,12 @@ public function map(string $class, bool $requireValues = false, ?array $source =
7173 * push them into well-typed numeric fields we need to cast them
7274 * appropriately.
7375 *
74- * @param string $val
76+ * @param string|int|float $val
7577 * The value to normalize.
7678 * @return int|float|string|bool
7779 * The passed value, but now with the correct type.
7880 */
79- private function typeNormalize (string $ val , \ReflectionProperty $ rProp ): int |float |string |bool |\BackedEnum
81+ private function typeNormalize (string | int | float $ val , \ReflectionProperty $ rProp ): int |float |string |bool |\BackedEnum
8082 {
8183 $ rType = $ rProp ->getType ();
8284 if ($ rType instanceof \ReflectionNamedType) {
@@ -90,18 +92,18 @@ private function typeNormalize(string $val, \ReflectionProperty $rProp): int|flo
9092 assert ($ backingType instanceof \ReflectionNamedType);
9193 $ isIntBacked = $ backingType ->getName () === 'int ' ;
9294
93- return $ name ::from ($ isIntBacked ? (int ) $ val : $ val );
95+ return $ name ::from ($ isIntBacked ? (int ) $ val : ( string ) $ val );
9496 }
9597
9698 return match ($ name ) {
9799 'string ' => $ val ,
98100 'float ' => is_numeric ($ val )
99101 ? (float ) $ val
100102 : throw TypeMismatch::create ($ rProp ->getDeclaringClass ()->getName (), $ rProp ->getName (), $ val ),
101- 'int ' => (is_numeric ($ val ) && floor (( float ) $ val) === ( float ) $ val )
103+ 'int ' => (is_numeric ($ val ) && is_int ( $ val + 0 ) )
102104 ? (int ) $ val
103105 : throw TypeMismatch::create ($ rProp ->getDeclaringClass ()->getName (), $ rProp ->getName (), $ val ),
104- 'bool ' => in_array (strtolower ($ val ), [1 , '1 ' , 'true ' , 'yes ' , 'on ' ], false ),
106+ 'bool ' => in_array (strtolower (( string ) $ val ), [1 , '1 ' , 'true ' , 'yes ' , 'on ' ], false ),
105107 default => throw TypeMismatch::create ($ rProp ->getDeclaringClass ()->getName (), $ rProp ->getName (), $ val ),
106108 };
107109 }
0 commit comments