Skip to content

Commit 7e9c8e8

Browse files
committed
add support for array shapes
1 parent dc33c9d commit 7e9c8e8

File tree

10 files changed

+800
-280
lines changed

10 files changed

+800
-280
lines changed

README.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,44 @@ use Patchlevel\Hydrator\Normalizer\DateTimeImmutableNormalizer;
155155

156156
final class DTO
157157
{
158-
#[ArrayNormalizer(new DateTimeImmutableNormalizer())]
158+
/**
159+
* @var list<DateTimeImmutable>
160+
*/
161+
#[ArrayNormalizer]
159162
public array $dates;
163+
164+
#[ArrayNormalizer(new DateTimeImmutableNormalizer())]
165+
public array $explicitDates;
160166
}
161167
```
162168

163169
> [!NOTE]
164170
> The keys from the arrays are taken over here.
165171
172+
#### ArrayShape
173+
174+
If you have an array with a specific shape, you can use the `ArrayShapeNormalizer`.
175+
176+
```php
177+
use Patchlevel\Hydrator\Normalizer\ArrayShapeNormalizer;
178+
use Patchlevel\Hydrator\Normalizer\DateTimeImmutableNormalizer;
179+
180+
final class DTO
181+
{
182+
/**
183+
* @var array{
184+
* date: DateTimeImmutable,
185+
* otherField: string
186+
* }
187+
*/
188+
#[ArrayShapeNormalizer]
189+
public array $meta;
190+
191+
#[ArrayShapeNormalizer(['date' => new DateTimeImmutableNormalizer()])]
192+
public array $explicitMeta;
193+
}
194+
```
195+
166196
#### DateTimeImmutable
167197

168198
With the `DateTimeImmutable` Normalizer, as the name suggests,

baseline.xml

Lines changed: 133 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<files psalm-version="6.9.1@81c8a77c0793d450fee40265cfe68891df11d505">
2+
<files psalm-version="6.12.0@cf420941d061a57050b6c468ef2c778faf40aee2">
3+
<file src="src/Attribute/PersonalData.php">
4+
<MixedPropertyTypeCoercion>
5+
<code><![CDATA[$fallbackCallable]]></code>
6+
</MixedPropertyTypeCoercion>
7+
</file>
38
<file src="src/Cryptography/Cipher/OpensslCipherKeyFactory.php">
49
<ArgumentTypeCoercion>
510
<code><![CDATA[openssl_random_pseudo_bytes($this->ivLength)]]></code>
@@ -25,27 +30,106 @@
2530
</MixedAssignment>
2631
</file>
2732
<file src="src/Metadata/AttributeMetadataFactory.php">
28-
<MixedAssignment>
29-
<code><![CDATA[$personalDataFallback]]></code>
30-
</MixedAssignment>
33+
<InvalidReturnStatement>
34+
<code><![CDATA[[false, null]]]></code>
35+
</InvalidReturnStatement>
36+
<InvalidReturnType>
37+
<code><![CDATA[array{bool, mixed, (callable(string, mixed):mixed)|null}]]></code>
38+
</InvalidReturnType>
39+
<PossiblyNullReference>
40+
<code><![CDATA[guess]]></code>
41+
</PossiblyNullReference>
3142
</file>
3243
<file src="src/Metadata/ClassMetadata.php">
3344
<InvalidPropertyAssignmentValue>
3445
<code><![CDATA[new ReflectionClass($data['className'])]]></code>
3546
</InvalidPropertyAssignmentValue>
3647
</file>
48+
<file src="src/Metadata/PropertyMetadata.php">
49+
<RiskyTruthyFalsyComparison>
50+
<code><![CDATA[$this->personalDataFallbackCallable]]></code>
51+
</RiskyTruthyFalsyComparison>
52+
</file>
53+
<file src="src/MetadataHydrator.php">
54+
<InvalidOperand>
55+
<code><![CDATA[$guessers]]></code>
56+
</InvalidOperand>
57+
<PossiblyInvalidArgument>
58+
<code><![CDATA[[
59+
...$guessers,
60+
$guesser,
61+
]]]></code>
62+
</PossiblyInvalidArgument>
63+
</file>
64+
<file src="src/Normalizer/ArrayShapeNormalizer.php">
65+
<MixedAssignment>
66+
<code><![CDATA[$result[$field]]]></code>
67+
<code><![CDATA[$result[$field]]]></code>
68+
</MixedAssignment>
69+
</file>
70+
<file src="src/Normalizer/EnumNormalizer.php">
71+
<DeprecatedClass>
72+
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf($reflectionType, BackedEnum::class)]]></code>
73+
</DeprecatedClass>
74+
<DeprecatedInterface>
75+
<code><![CDATA[EnumNormalizer]]></code>
76+
</DeprecatedInterface>
77+
</file>
3778
<file src="src/Normalizer/ObjectNormalizer.php">
79+
<DeprecatedClass>
80+
<code><![CDATA[ReflectionTypeUtil::classString($reflectionType)]]></code>
81+
</DeprecatedClass>
82+
<DeprecatedInterface>
83+
<code><![CDATA[ObjectNormalizer]]></code>
84+
</DeprecatedInterface>
3885
<MixedArgument>
3986
<code><![CDATA[$value]]></code>
4087
</MixedArgument>
4188
<MixedArgumentTypeCoercion>
4289
<code><![CDATA[$value]]></code>
4390
</MixedArgumentTypeCoercion>
4491
</file>
92+
<file src="tests/Benchmark/tideways.php">
93+
<ClassMustBeFinal>
94+
<code><![CDATA[CompiledMetadataHydrator]]></code>
95+
</ClassMustBeFinal>
96+
<MixedMethodCall>
97+
<code><![CDATA[normalize]]></code>
98+
<code><![CDATA[normalize]]></code>
99+
</MixedMethodCall>
100+
<MixedReturnTypeCoercion>
101+
<code><![CDATA[array]]></code>
102+
<code><![CDATA[match (true) {
103+
$object instanceof ProfileCreated => $this->extractProfileCreated($object),
104+
$object instanceof Skill => $this->extractSkill($object),
105+
default => throw new InvalidArgumentException('Unknown object type'),
106+
}]]></code>
107+
</MixedReturnTypeCoercion>
108+
</file>
45109
<file src="tests/Unit/Cryptography/Cipher/OpensslCipherTest.php">
46110
<MixedAssignment>
47111
<code><![CDATA[$return]]></code>
48112
</MixedAssignment>
113+
<MixedReturnStatement>
114+
<code><![CDATA[Generator]]></code>
115+
</MixedReturnStatement>
116+
</file>
117+
<file src="tests/Unit/Cryptography/CryptographySubscriberTest.php">
118+
<InvalidArgument>
119+
<code><![CDATA[$metadata]]></code>
120+
<code><![CDATA[$metadata]]></code>
121+
</InvalidArgument>
122+
</file>
123+
<file src="tests/Unit/Fixture/IdNormalizer.php">
124+
<DeprecatedClass>
125+
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf(
126+
$reflectionType,
127+
Id::class,
128+
)]]></code>
129+
</DeprecatedClass>
130+
<DeprecatedInterface>
131+
<code><![CDATA[IdNormalizer]]></code>
132+
</DeprecatedInterface>
49133
</file>
50134
<file src="tests/Unit/Metadata/AttributeMetadataFactoryTest.php">
51135
<ArgumentTypeCoercion>
@@ -59,8 +143,53 @@
59143
<ArgumentTypeCoercion>
60144
<code><![CDATA['Unknown']]></code>
61145
</ArgumentTypeCoercion>
146+
<InvalidArgument>
147+
<code><![CDATA[$metadataFactory->metadata(ProfileCreated::class)]]></code>
148+
<code><![CDATA[$metadataFactory->metadata(ProfileCreated::class)]]></code>
149+
<code><![CDATA[$metadataFactory->metadata(ProfileCreated::class)]]></code>
150+
<code><![CDATA[$metadataFactory->metadata(ProfileCreated::class)]]></code>
151+
</InvalidArgument>
62152
<UndefinedClass>
63153
<code><![CDATA['Unknown']]></code>
64154
</UndefinedClass>
65155
</file>
156+
<file src="tests/Unit/Normalizer/ArrayNormalizerTest.php">
157+
<InvalidArgument>
158+
<code><![CDATA[$normalizer]]></code>
159+
</InvalidArgument>
160+
</file>
161+
<file src="tests/Unit/Normalizer/ArrayShapeNormalizerTest.php">
162+
<InvalidArgument>
163+
<code><![CDATA[['foo' => $normalizer]]]></code>
164+
</InvalidArgument>
165+
</file>
166+
<file src="tests/Unit/Normalizer/ReflectionTypeUtilTest.php">
167+
<DeprecatedClass>
168+
<code><![CDATA[ReflectionTypeUtil::classString($this->reflectionType($object, 'notAObject'))]]></code>
169+
<code><![CDATA[ReflectionTypeUtil::classString($this->reflectionType($object, 'object'))]]></code>
170+
<code><![CDATA[ReflectionTypeUtil::classString($this->reflectionType($object, 'objectNullable'))]]></code>
171+
<code><![CDATA[ReflectionTypeUtil::classString($this->reflectionType($object, 'objectUnionNullable'))]]></code>
172+
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf(
173+
$this->reflectionType($object, 'object'),
174+
ProfileCreated::class,
175+
)]]></code>
176+
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf(
177+
$this->reflectionType($object, 'objectNullable'),
178+
ProfileCreated::class,
179+
)]]></code>
180+
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf(
181+
$this->reflectionType($object, 'objectUnionNullable'),
182+
ProfileCreated::class,
183+
)]]></code>
184+
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf(
185+
$this->reflectionType($object, 'object'),
186+
ChildDto::class,
187+
)]]></code>
188+
<code><![CDATA[ReflectionTypeUtil::type($this->reflectionType($object, 'intersection'))]]></code>
189+
<code><![CDATA[ReflectionTypeUtil::type($this->reflectionType($object, 'nullableString'))]]></code>
190+
<code><![CDATA[ReflectionTypeUtil::type($this->reflectionType($object, 'string'))]]></code>
191+
<code><![CDATA[ReflectionTypeUtil::type($this->reflectionType($object, 'union'))]]></code>
192+
<code><![CDATA[ReflectionTypeUtil::type($this->reflectionType($object, 'unionNullableString'))]]></code>
193+
</DeprecatedClass>
194+
</file>
66195
</files>

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"psr/cache": "^2.0.0 || ^3.0.0",
2525
"psr/simple-cache": "^2.0.0 || ^3.0.0",
2626
"symfony/event-dispatcher": "^5.4.29 || ^6.4.0 || ^7.0.0",
27-
"symfony/type-info": "^7.2.4"
27+
"symfony/type-info": "^7.3.0"
2828
},
2929
"require-dev": {
3030
"infection/infection": "^0.29.10",

0 commit comments

Comments
 (0)