Skip to content

Commit 33f4b2c

Browse files
committed
Improve internal type safety
1 parent da2c02c commit 33f4b2c

File tree

11 files changed

+72
-15
lines changed

11 files changed

+72
-15
lines changed

src/Meta/MetaResolver.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,11 @@
3939
use Reflector;
4040
use function array_key_exists;
4141
use function array_merge;
42+
use function assert;
4243
use function get_class;
4344
use function is_a;
45+
use function is_int;
46+
use function is_string;
4447
use const PHP_VERSION_ID;
4548

4649
/**
@@ -487,6 +490,7 @@ private function checkFieldNames(ReflectionClass $rootClass, CompileMeta $meta):
487490
foreach ($fieldMeta->getModifiers() as $modifier) {
488491
if ($modifier->getType() === FieldNameModifier::class) {
489492
$fieldName = $modifier->getArgs()[FieldNameModifier::Name];
493+
assert(is_string($fieldName) || is_int($fieldName));
490494

491495
break;
492496
}

src/Printers/ErrorVisualPrinter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use function get_class;
1717

1818
/**
19-
* @template T of string|array
19+
* @template T of string|array<mixed>
2020
*/
2121
final class ErrorVisualPrinter implements ErrorPrinter, TypePrinter
2222
{
@@ -25,7 +25,7 @@ final class ErrorVisualPrinter implements ErrorPrinter, TypePrinter
2525
private TypeToPrimitiveConverter $converter;
2626

2727
/**
28-
* @param TypeToPrimitiveConverter<T> $converter
28+
* @param TypeToPrimitiveConverter<covariant T> $converter
2929
*/
3030
public function __construct(TypeToPrimitiveConverter $converter)
3131
{

src/Printers/TypeToPrimitiveConverter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use Orisai\ObjectMapper\Types\TypeParameter;
77

88
/**
9-
* @template T of string|array
9+
* @template T of string|array<mixed>
1010
*/
1111
interface TypeToPrimitiveConverter
1212
{

src/Rules/ArrayOfRule.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
use Orisai\ObjectMapper\Processing\Value;
1616
use Orisai\ObjectMapper\Types\GenericArrayType;
1717
use Orisai\Utils\Arrays\ArrayMerger;
18+
use function assert;
1819
use function count;
1920
use function get_debug_type;
2021
use function is_array;
22+
use function is_int;
23+
use function is_string;
2124
use function sprintf;
2225

2326
/**
@@ -159,6 +162,7 @@ public function processValue(
159162
$property,
160163
$dynamic->createClone(),
161164
);
165+
assert(is_int($key) || is_string($key));
162166
} catch (ValueDoesNotMatch | InvalidData $exception) {
163167
$type ??= $this->createType($args, $services, $dynamic);
164168
$type->addInvalidKey($key, $exception);
@@ -221,7 +225,9 @@ public function processValue(
221225
}
222226

223227
if ($args->mergeDefaults && $property->hasDefaultValue()) {
224-
$value = ArrayMerger::merge($property->getDefaultValue(), $value);
228+
$default = $property->getDefaultValue();
229+
assert(is_array($default)); // Rule validates that default is an array
230+
$value = ArrayMerger::merge($default, $value);
225231
}
226232

227233
return $value;

src/Rules/ListOfRule.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Orisai\ObjectMapper\Rules;
44

5+
use Orisai\Exceptions\Logic\InvalidArgument;
56
use Orisai\ObjectMapper\Args\Args;
67
use Orisai\ObjectMapper\Args\ArgsChecker;
78
use Orisai\ObjectMapper\Exception\InvalidData;
@@ -15,9 +16,12 @@
1516
use Orisai\ObjectMapper\Types\GenericArrayType;
1617
use Orisai\ObjectMapper\Types\SimpleValueType;
1718
use Orisai\Utils\Arrays\ArrayMerger;
19+
use function assert;
1820
use function count;
21+
use function get_debug_type;
1922
use function is_array;
2023
use function is_int;
24+
use function sprintf;
2125

2226
/**
2327
* @extends MultiValueRule<MultiValueArgs>
@@ -54,6 +58,20 @@ public function resolveArgs(array $args, MetaFieldContext $context): MultiValueA
5458
$mergeDefaults = $checker->checkBool(self::MergeDefaults);
5559
}
5660

61+
if (
62+
$mergeDefaults
63+
&& $context->hasDefaultValue()
64+
&& !is_array($defaultValue = $context->getDefaultValue())
65+
) {
66+
throw InvalidArgument::create()
67+
->withMessage(sprintf(
68+
'Argument "%s" given to "%s" is set to true but the default value is "%s" insteadof an array.',
69+
self::MergeDefaults,
70+
self::class,
71+
get_debug_type($defaultValue),
72+
));
73+
}
74+
5775
return new MultiValueArgs(
5876
$itemRuleMeta,
5977
$minItems,
@@ -186,7 +204,9 @@ public function processValue(
186204
}
187205

188206
if ($args->mergeDefaults && $property->hasDefaultValue()) {
189-
$value = ArrayMerger::merge($property->getDefaultValue(), $value);
207+
$default = $property->getDefaultValue();
208+
assert(is_array($default)); // Rule validates that default is an array
209+
$value = ArrayMerger::merge($default, $value);
190210
}
191211

192212
return $value;

tests/Doubles/Callbacks/CallbacksVO.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Orisai\ObjectMapper\Rules\MixedValue;
1515
use Orisai\ObjectMapper\Rules\StringValue;
1616
use function array_key_exists;
17+
use function assert;
1718
use function is_array;
1819

1920
/**
@@ -75,6 +76,8 @@ public static function beforeClass($data, ObjectContext $context)
7576
return $data;
7677
}
7778

79+
assert(is_array($data['array']));
80+
$data['array']['beforeClassCallback'] = [];
7881
$data['array']['beforeClassCallback'][] = $context->shouldInitializeObjects();
7982

8083
// Set default value, processor don't know it's going to be structure and thinks value is required
@@ -97,6 +100,8 @@ public static function beforeClass($data, ObjectContext $context)
97100
*/
98101
public static function afterClass(array $data, ObjectContext $context): array
99102
{
103+
assert(is_array($data['array']));
104+
assert(is_array($data['array']['afterClassCallback']));
100105
$data['array']['afterClassCallback'][] = $context->shouldInitializeObjects();
101106

102107
if ($context->shouldInitializeObjects() && !$data['structure'] instanceof MappedObject) {
@@ -118,6 +123,7 @@ public static function afterClass(array $data, ObjectContext $context): array
118123
*/
119124
public static function afterArrayProcessing(array $array, FieldContext $context): array
120125
{
126+
$array['afterArrayProcessingCallback'] = [];
121127
$array['afterArrayProcessingCallback'][] = $context->shouldInitializeObjects();
122128

123129
return $array;
@@ -129,6 +135,7 @@ public static function afterArrayProcessing(array $array, FieldContext $context)
129135
*/
130136
public static function afterArrayInitialization(array $array, FieldContext $context): array
131137
{
138+
$array['afterArrayInitializationCallback'] = [];
132139
$array['afterArrayInitializationCallback'][] = $context->shouldInitializeObjects();
133140

134141
return $array;

tests/Doubles/Inheritance/trait-callback.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Orisai\ObjectMapper\Callbacks\Before;
77
use Orisai\ObjectMapper\MappedObject;
88
use Orisai\ObjectMapper\Rules\StringValue;
9+
use function assert;
910
use function is_array;
1011
use function is_string;
1112

@@ -42,6 +43,7 @@ private function before($data)
4243
*/
4344
private function after(array $data): array
4445
{
46+
assert(is_string($data['string']));
4547
$data['string'] .= '-A::after';
4648

4749
return $data;

tests/Unit/Processing/RawValuesMapTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Orisai\ObjectMapper\Processing\RawValuesMap;
77
use PHPUnit\Framework\TestCase;
88
use Tests\Orisai\ObjectMapper\Doubles\DefaultsVO;
9+
use function assert;
910
use function serialize;
1011
use function unserialize;
1112
use const PHP_VERSION_ID;
@@ -58,6 +59,7 @@ public function testUnset(): void
5859
$data = serialize($object);
5960
unset($object);
6061
$object = unserialize($data);
62+
assert($object instanceof DefaultsVO);
6163

6264
$this->expectException(InvalidState::class);
6365
$map->getRawValues($object);

tests/Unit/Rules/BackedEnumRuleTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Orisai\ObjectMapper\Rules\BackedEnumArgs;
99
use Orisai\ObjectMapper\Rules\BackedEnumRule;
1010
use Orisai\ObjectMapper\Types\EnumType;
11+
use stdClass;
1112
use Tests\Orisai\ObjectMapper\Doubles\Php81\ExampleIntEnum;
1213
use Tests\Orisai\ObjectMapper\Doubles\Php81\ExampleStringEnum;
1314
use Tests\Orisai\ObjectMapper\Toolkit\ProcessingTestCase;
@@ -148,6 +149,18 @@ public function provideInvalidValues(): Generator
148149
new BackedEnumArgs(ExampleIntEnum::class, false),
149150
[0, 1],
150151
];
152+
153+
yield [
154+
['foo', 'bar'],
155+
new BackedEnumArgs(ExampleIntEnum::class, false),
156+
[0, 1],
157+
];
158+
159+
yield [
160+
new stdClass(),
161+
new BackedEnumArgs(ExampleIntEnum::class, false),
162+
[0, 1],
163+
];
151164
}
152165

153166
public function testType(): void

tests/Unit/Rules/DateTimeRuleTest.php

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ public function provideValidValues(): Generator
116116
}
117117

118118
/**
119-
* @param mixed $value
120-
* @param array<int, string> $invalidParameters
119+
* @param mixed $value
120+
* @param list<int|string|array<int|string, mixed>> $invalidParameters
121121
*
122122
* @dataProvider provideInvalidValues
123123
*/
@@ -148,12 +148,9 @@ public function testProcessInvalid(
148148
$parameters = [];
149149
foreach ($type->getParameters() as $parameter) {
150150
if ($parameter->isInvalid()) {
151-
$parameterString = $parameter->getKey();
152-
if ($parameter->hasValue()) {
153-
$parameterString .= ": {$parameter->getValue()}";
154-
}
155-
156-
$parameters[] = $parameterString;
151+
$parameters[] = $parameter->hasValue()
152+
? [$parameter->getKey(), $parameter->getValue()]
153+
: $parameter->getKey();
157154
}
158155
}
159156

@@ -177,7 +174,7 @@ public function provideInvalidValues(): Generator
177174
]];
178175

179176
yield ['whatever', DateTimeInterface::ATOM, [
180-
'format: Y-m-d\TH:i:sP',
177+
['format', 'Y-m-d\TH:i:sP'],
181178
'A four digit year could not be found',
182179
PHP_VERSION_ID < 8_01_07 ? 'Data missing' : 'Not enough data available to satisfy format',
183180
]];
@@ -187,7 +184,7 @@ public function provideInvalidValues(): Generator
187184
], 'timestamp'];
188185

189186
yield ['2013-04-12T16:40:00-04:00', DateTimeInterface::COOKIE, [
190-
'format: l, d-M-Y H:i:s T',
187+
['format', 'l, d-M-Y H:i:s T'],
191188
'A textual day could not be found',
192189
'Unexpected data found.',
193190
'The separation symbol could not be found',

0 commit comments

Comments
 (0)