Skip to content

Commit fa49a29

Browse files
authored
Merge pull request #9 from Crell/phpstan
Type pedantry fixes.
2 parents 95ad083 + b505c53 commit fa49a29

File tree

3 files changed

+28
-20
lines changed

3 files changed

+28
-20
lines changed

phpstan.neon

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,21 @@ parameters:
66
excludePaths:
77
- tests/Envs/*
88
ignoreErrors:
9-
# -
10-
# message: '#type has no value type specified in iterable type array#'
11-
# path: tests/
12-
# -
13-
# message: '#type has no value type specified in iterable type iterable#'
14-
# path: tests/
9+
-
10+
message: '#type has no value type specified in iterable type array#'
11+
path: tests/
12+
reportUnmatched: false
13+
-
14+
message: '#type has no value type specified in iterable type iterable#'
15+
path: tests/
16+
reportUnmatched: false
1517
# PHPStan is overly aggressive on readonly properties.
16-
- '#Class (.*) has an uninitialized readonly property (.*). Assign it in the constructor.#'
17-
- '#Readonly property (.*) is assigned outside of the constructor.#'
18+
-
19+
identifier: property.uninitializedReadonly
20+
reportUnmatched: false
21+
-
22+
identifier: property.readOnlyAssignNotInConstructor
23+
reportUnmatched: false
1824
# This is wrong, getName() is a working method on ReflectionType. But the stubs are wrong, or something.
1925
-
2026
message: "#^Call to an undefined method ReflectionType\\:\\:getName\\(\\)\\.$#"

src/EnvMapper.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

tests/EnvMapperTest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ public function mapping_different_types_with_defaults_parses_correctly(): void
3636
/** @var SampleEnvironment $env */
3737
$env = $mapper->map(SampleEnvironment::class, source: $this->source);
3838

39-
self::assertNotNull($env->phpVersion);
40-
self::assertNotNull($env->xdebug_mode);
41-
self::assertNotNull($env->PATH);
42-
self::assertNotNull($env->hostname);
43-
self::assertNotNull($env->shlvl);
39+
self::assertEquals($this->source['PHP_VERSION'], $env->phpVersion);
40+
self::assertEquals($this->source['XDEBUG_MODE'], $env->xdebug_mode);
41+
self::assertEquals($this->source['PATH'], $env->PATH);
42+
self::assertEquals($this->source['HOSTNAME'], $env->hostname);
43+
self::assertEquals($this->source['SHLVL'], $env->shlvl);
4444
self::assertSame('01234', $env->zipCode);
4545
self::assertSame(true, $env->bool);
4646
self::assertEquals(StringBackedEnum::Foo, $env->stringBackedEnum);

0 commit comments

Comments
 (0)