Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,10 @@ For this purpose, normalizers of this order are determined:

1) Does the class property have a normalizer as an attribute? Use this.
2) The data type of the property is determined.
1) If it is a collection, use the ArrayNormalizer (recursive).
2) If it is an object, then look for a normalizer as attribute on the class or interfaces and use this.
3) If it is an object, then guess the normalizer based on the object. Fallback to the object normalizer.
1) If it is an array shape, use the ArrayShapeNormalizer (recursive).
2) If it is a collection, use the ArrayNormalizer (recursive).
3) If it is an object, then look for a normalizer as attribute on the class or interfaces and use this.
4) If it is an object, then guess the normalizer based on the object. Fallback to the object normalizer.

The normalizer is only determined once because it is cached in the metadata.
Below you will find the list of all normalizers and how to set them manually or explicitly.
Expand All @@ -155,14 +156,44 @@ use Patchlevel\Hydrator\Normalizer\DateTimeImmutableNormalizer;

final class DTO
{
#[ArrayNormalizer(new DateTimeImmutableNormalizer())]
/**
* @var list<DateTimeImmutable>
*/
#[ArrayNormalizer]
public array $dates;

#[ArrayNormalizer(new DateTimeImmutableNormalizer())]
public array $explicitDates;
}
```

> [!NOTE]
> The keys from the arrays are taken over here.

#### ArrayShape

If you have an array with a specific shape, you can use the `ArrayShapeNormalizer`.

```php
use Patchlevel\Hydrator\Normalizer\ArrayShapeNormalizer;
use Patchlevel\Hydrator\Normalizer\DateTimeImmutableNormalizer;

final class DTO
{
/**
* @var array{
* date: DateTimeImmutable,
* otherField: string
* }
*/
#[ArrayShapeNormalizer]
public array $meta;

#[ArrayShapeNormalizer(['date' => new DateTimeImmutableNormalizer()])]
public array $explicitMeta;
}
```

#### DateTimeImmutable

With the `DateTimeImmutable` Normalizer, as the name suggests,
Expand Down
137 changes: 133 additions & 4 deletions baseline.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="6.9.1@81c8a77c0793d450fee40265cfe68891df11d505">
<files psalm-version="6.12.0@cf420941d061a57050b6c468ef2c778faf40aee2">
<file src="src/Attribute/PersonalData.php">
<MixedPropertyTypeCoercion>
<code><![CDATA[$fallbackCallable]]></code>
</MixedPropertyTypeCoercion>
</file>
<file src="src/Cryptography/Cipher/OpensslCipherKeyFactory.php">
<ArgumentTypeCoercion>
<code><![CDATA[openssl_random_pseudo_bytes($this->ivLength)]]></code>
Expand All @@ -25,27 +30,106 @@
</MixedAssignment>
</file>
<file src="src/Metadata/AttributeMetadataFactory.php">
<MixedAssignment>
<code><![CDATA[$personalDataFallback]]></code>
</MixedAssignment>
<InvalidReturnStatement>
<code><![CDATA[[false, null]]]></code>
</InvalidReturnStatement>
<InvalidReturnType>
<code><![CDATA[array{bool, mixed, (callable(string, mixed):mixed)|null}]]></code>
</InvalidReturnType>
<PossiblyNullReference>
<code><![CDATA[guess]]></code>
</PossiblyNullReference>
</file>
<file src="src/Metadata/ClassMetadata.php">
<InvalidPropertyAssignmentValue>
<code><![CDATA[new ReflectionClass($data['className'])]]></code>
</InvalidPropertyAssignmentValue>
</file>
<file src="src/Metadata/PropertyMetadata.php">
<RiskyTruthyFalsyComparison>
<code><![CDATA[$this->personalDataFallbackCallable]]></code>
</RiskyTruthyFalsyComparison>
</file>
<file src="src/MetadataHydrator.php">
<InvalidOperand>
<code><![CDATA[$guessers]]></code>
</InvalidOperand>
<PossiblyInvalidArgument>
<code><![CDATA[[
...$guessers,
$guesser,
]]]></code>
</PossiblyInvalidArgument>
</file>
<file src="src/Normalizer/ArrayShapeNormalizer.php">
<MixedAssignment>
<code><![CDATA[$result[$field]]]></code>
<code><![CDATA[$result[$field]]]></code>
</MixedAssignment>
</file>
<file src="src/Normalizer/EnumNormalizer.php">
<DeprecatedClass>
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf($reflectionType, BackedEnum::class)]]></code>
</DeprecatedClass>
<DeprecatedInterface>
<code><![CDATA[EnumNormalizer]]></code>
</DeprecatedInterface>
</file>
<file src="src/Normalizer/ObjectNormalizer.php">
<DeprecatedClass>
<code><![CDATA[ReflectionTypeUtil::classString($reflectionType)]]></code>
</DeprecatedClass>
<DeprecatedInterface>
<code><![CDATA[ObjectNormalizer]]></code>
</DeprecatedInterface>
<MixedArgument>
<code><![CDATA[$value]]></code>
</MixedArgument>
<MixedArgumentTypeCoercion>
<code><![CDATA[$value]]></code>
</MixedArgumentTypeCoercion>
</file>
<file src="tests/Benchmark/tideways.php">
<ClassMustBeFinal>
<code><![CDATA[CompiledMetadataHydrator]]></code>
</ClassMustBeFinal>
<MixedMethodCall>
<code><![CDATA[normalize]]></code>
<code><![CDATA[normalize]]></code>
</MixedMethodCall>
<MixedReturnTypeCoercion>
<code><![CDATA[array]]></code>
<code><![CDATA[match (true) {
$object instanceof ProfileCreated => $this->extractProfileCreated($object),
$object instanceof Skill => $this->extractSkill($object),
default => throw new InvalidArgumentException('Unknown object type'),
}]]></code>
</MixedReturnTypeCoercion>
</file>
<file src="tests/Unit/Cryptography/Cipher/OpensslCipherTest.php">
<MixedAssignment>
<code><![CDATA[$return]]></code>
</MixedAssignment>
<MixedReturnStatement>
<code><![CDATA[Generator]]></code>
</MixedReturnStatement>
</file>
<file src="tests/Unit/Cryptography/CryptographySubscriberTest.php">
<InvalidArgument>
<code><![CDATA[$metadata]]></code>
<code><![CDATA[$metadata]]></code>
</InvalidArgument>
</file>
<file src="tests/Unit/Fixture/IdNormalizer.php">
<DeprecatedClass>
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf(
$reflectionType,
Id::class,
)]]></code>
</DeprecatedClass>
<DeprecatedInterface>
<code><![CDATA[IdNormalizer]]></code>
</DeprecatedInterface>
</file>
<file src="tests/Unit/Metadata/AttributeMetadataFactoryTest.php">
<ArgumentTypeCoercion>
Expand All @@ -59,8 +143,53 @@
<ArgumentTypeCoercion>
<code><![CDATA['Unknown']]></code>
</ArgumentTypeCoercion>
<InvalidArgument>
<code><![CDATA[$metadataFactory->metadata(ProfileCreated::class)]]></code>
<code><![CDATA[$metadataFactory->metadata(ProfileCreated::class)]]></code>
<code><![CDATA[$metadataFactory->metadata(ProfileCreated::class)]]></code>
<code><![CDATA[$metadataFactory->metadata(ProfileCreated::class)]]></code>
</InvalidArgument>
<UndefinedClass>
<code><![CDATA['Unknown']]></code>
</UndefinedClass>
</file>
<file src="tests/Unit/Normalizer/ArrayNormalizerTest.php">
<InvalidArgument>
<code><![CDATA[$normalizer]]></code>
</InvalidArgument>
</file>
<file src="tests/Unit/Normalizer/ArrayShapeNormalizerTest.php">
<InvalidArgument>
<code><![CDATA[['foo' => $normalizer]]]></code>
</InvalidArgument>
</file>
<file src="tests/Unit/Normalizer/ReflectionTypeUtilTest.php">
<DeprecatedClass>
<code><![CDATA[ReflectionTypeUtil::classString($this->reflectionType($object, 'notAObject'))]]></code>
<code><![CDATA[ReflectionTypeUtil::classString($this->reflectionType($object, 'object'))]]></code>
<code><![CDATA[ReflectionTypeUtil::classString($this->reflectionType($object, 'objectNullable'))]]></code>
<code><![CDATA[ReflectionTypeUtil::classString($this->reflectionType($object, 'objectUnionNullable'))]]></code>
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf(
$this->reflectionType($object, 'object'),
ProfileCreated::class,
)]]></code>
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf(
$this->reflectionType($object, 'objectNullable'),
ProfileCreated::class,
)]]></code>
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf(
$this->reflectionType($object, 'objectUnionNullable'),
ProfileCreated::class,
)]]></code>
<code><![CDATA[ReflectionTypeUtil::classStringInstanceOf(
$this->reflectionType($object, 'object'),
ChildDto::class,
)]]></code>
<code><![CDATA[ReflectionTypeUtil::type($this->reflectionType($object, 'intersection'))]]></code>
<code><![CDATA[ReflectionTypeUtil::type($this->reflectionType($object, 'nullableString'))]]></code>
<code><![CDATA[ReflectionTypeUtil::type($this->reflectionType($object, 'string'))]]></code>
<code><![CDATA[ReflectionTypeUtil::type($this->reflectionType($object, 'union'))]]></code>
<code><![CDATA[ReflectionTypeUtil::type($this->reflectionType($object, 'unionNullableString'))]]></code>
</DeprecatedClass>
</file>
</files>
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"psr/cache": "^2.0.0 || ^3.0.0",
"psr/simple-cache": "^2.0.0 || ^3.0.0",
"symfony/event-dispatcher": "^5.4.29 || ^6.4.0 || ^7.0.0",
"symfony/type-info": "^7.2.4"
"symfony/type-info": "^7.3.0"
},
"require-dev": {
"infection/infection": "^0.29.10",
Expand Down
Loading
Loading