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
38 changes: 21 additions & 17 deletions baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@
<InvalidOperand>
<code><![CDATA[$guessers]]></code>
</InvalidOperand>
<MixedReturnStatement>
<code><![CDATA[(new ReflectionClass($class))->newLazyProxy(
function () use ($metadata, $data): object {
return $this->doHydrate($metadata, $data);
},
)]]></code>
</MixedReturnStatement>
<PossiblyInvalidArgument>
<code><![CDATA[[
...$guessers,
Expand Down Expand Up @@ -89,23 +96,6 @@
<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>
Expand Down Expand Up @@ -163,6 +153,20 @@
<code><![CDATA[['foo' => $normalizer]]]></code>
</InvalidArgument>
</file>
<file src="tests/Unit/Normalizer/EnumNormalizerTest.php">
<DeprecatedMethod>
<code><![CDATA[handleReflectionType]]></code>
<code><![CDATA[handleReflectionType]]></code>
<code><![CDATA[handleReflectionType]]></code>
</DeprecatedMethod>
</file>
<file src="tests/Unit/Normalizer/ObjectNormalizerTest.php">
<DeprecatedMethod>
<code><![CDATA[handleReflectionType]]></code>
<code><![CDATA[handleReflectionType]]></code>
<code><![CDATA[handleReflectionType]]></code>
</DeprecatedMethod>
</file>
<file src="tests/Unit/Normalizer/ReflectionTypeUtilTest.php">
<DeprecatedClass>
<code><![CDATA[ReflectionTypeUtil::classString($this->reflectionType($object, 'notAObject'))]]></code>
Expand Down
10 changes: 10 additions & 0 deletions src/Metadata/AttributeMetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\Type\ArrayShapeType;
use Symfony\Component\TypeInfo\Type\CollectionType;
use Symfony\Component\TypeInfo\Type\GenericType;
use Symfony\Component\TypeInfo\Type\NullableType;
use Symfony\Component\TypeInfo\Type\ObjectType;
use Symfony\Component\TypeInfo\Type\TemplateType;
use Symfony\Component\TypeInfo\TypeResolver\TypeResolver;

use function array_key_exists;
Expand Down Expand Up @@ -391,6 +393,14 @@ private function inferNormalizerByType(Type $type): Normalizer|null
$type = $type->getWrappedType();
}

if ($type instanceof TemplateType) {
$type = $type->getWrappedType();
}

if ($type instanceof GenericType) {
$type = $type->getWrappedType();
}

if ($type instanceof ObjectType) {
$normalizer = $this->findNormalizerOnClass($type->getClassName());

Expand Down
1 change: 1 addition & 0 deletions src/Normalizer/EnumNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public function denormalize(mixed $value): BackedEnum|null
}
}

/** @deprecated use `handleType()` instead */
public function handleReflectionType(ReflectionType|null $reflectionType): void
{
if ($this->enum !== null || $reflectionType === null) {
Expand Down
11 changes: 11 additions & 0 deletions src/Normalizer/ObjectNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
use Patchlevel\Hydrator\Hydrator;
use ReflectionType;
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\Type\GenericType;
use Symfony\Component\TypeInfo\Type\NullableType;
use Symfony\Component\TypeInfo\Type\ObjectType;
use Symfony\Component\TypeInfo\Type\TemplateType;

use function is_array;

Expand Down Expand Up @@ -68,6 +70,7 @@ public function setHydrator(Hydrator $hydrator): void
$this->hydrator = $hydrator;
}

/** @deprecated use handleType instead */
public function handleReflectionType(ReflectionType|null $reflectionType): void
{
if ($this->className !== null || $reflectionType === null) {
Expand All @@ -87,6 +90,14 @@ public function handleType(Type|null $type): void
$type = $type->getWrappedType();
}

if ($type instanceof GenericType) {
$type = $type->getWrappedType();
}

if ($type instanceof TemplateType) {
$type = $type->getWrappedType();
}

if (!$type instanceof ObjectType) {
return;
}
Expand Down
16 changes: 16 additions & 0 deletions tests/Unit/Fixture/ProfileCreatedWithGeneric.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Patchlevel\Hydrator\Tests\Unit\Fixture;

final class ProfileCreatedWithGeneric
{
/** @param Wrapper<Email> $email */
public function __construct(
#[IdNormalizer]
public ProfileId $profileId,
public Wrapper $email,
) {
}
}
24 changes: 24 additions & 0 deletions tests/Unit/Fixture/Wrapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Patchlevel\Hydrator\Tests\Unit\Fixture;

use Patchlevel\Hydrator\Normalizer\ObjectNormalizer;

/** @template T */
#[ObjectNormalizer]
final class Wrapper
{
/**
* @param T $value
* @param Wrapper<Email> $object
* @param Wrapper<string>|null $scalar
*/
public function __construct(
public mixed $value,
public Wrapper $object,
public Wrapper|null $scalar = null,
Comment thread
DavidBadura marked this conversation as resolved.
) {
}
}
39 changes: 35 additions & 4 deletions tests/Unit/Metadata/AttributeMetadataFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Patchlevel\Hydrator\Metadata\PropertyMetadataNotFound;
use Patchlevel\Hydrator\Metadata\SubjectIdAndPersonalDataConflict;
use Patchlevel\Hydrator\Normalizer\EnumNormalizer;
use Patchlevel\Hydrator\Normalizer\ObjectNormalizer;
use Patchlevel\Hydrator\Tests\Unit\Fixture\BrokenParentDto;
use Patchlevel\Hydrator\Tests\Unit\Fixture\DistributionCreated;
use Patchlevel\Hydrator\Tests\Unit\Fixture\DuplicateFieldNameDto;
Expand All @@ -29,8 +30,10 @@
use Patchlevel\Hydrator\Tests\Unit\Fixture\MissingSubjectIdDto;
use Patchlevel\Hydrator\Tests\Unit\Fixture\ParentDto;
use Patchlevel\Hydrator\Tests\Unit\Fixture\ParentWithPersonalDataDto;
use Patchlevel\Hydrator\Tests\Unit\Fixture\ProfileCreatedWithGeneric;
use Patchlevel\Hydrator\Tests\Unit\Fixture\ProfileId;
use Patchlevel\Hydrator\Tests\Unit\Fixture\Status;
use Patchlevel\Hydrator\Tests\Unit\Fixture\Wrapper;
use PHPUnit\Framework\TestCase;

final class AttributeMetadataFactoryTest extends TestCase
Expand Down Expand Up @@ -136,7 +139,7 @@ public function __construct(
self::assertNull($propertyMetadata->normalizer());
}

public function testEventWithFieldName(): void
public function testNormalizedName(): void
{
$object = new class ('Foo') {
public function __construct(
Expand All @@ -160,7 +163,7 @@ public function __construct(
self::assertNull($propertyMetadata->normalizer());
}

public function testEventWithNormalizer(): void
public function testDefineNormalizer(): void
{
$object = new class (Email::fromString('info@patchlevel.de')) {
public function __construct(
Expand All @@ -184,7 +187,7 @@ public function __construct(
self::assertInstanceOf(EmailNormalizer::class, $propertyMetadata->normalizer());
}

public function testEventWithTypeAwareNormalizer(): void
public function testTypeAwareNormalizer(): void
{
$object = new class (Status::Draft) {
public function __construct(
Expand Down Expand Up @@ -212,7 +215,7 @@ public function __construct(
self::assertSame(Status::class, $normalizer->getEnum());
}

public function testEventWithInferNormalizer(): void
public function testInferNormalizer(): void
{
$object = new class {
public function __construct(
Expand All @@ -233,6 +236,34 @@ public function __construct(
self::assertEquals(new IdNormalizer(ProfileId::class), $propertyMetadata->normalizer());
}

public function testInferNormalizerWithGeneric(): void
{
$metadataFactory = new AttributeMetadataFactory();
$metadata = $metadataFactory->metadata(ProfileCreatedWithGeneric::class);

self::assertCount(2, $metadata->properties());

$propertyMetadata = $metadata->propertyForField('email');
self::assertEquals(new ObjectNormalizer(Wrapper::class), $propertyMetadata->normalizer());
}

public function testInferNormalizerWithTemplate(): void
{
$metadataFactory = new AttributeMetadataFactory();
$metadata = $metadataFactory->metadata(Wrapper::class);

self::assertCount(3, $metadata->properties());

$propertyMetadata = $metadata->propertyForField('value');
self::assertNull($propertyMetadata->normalizer());

$propertyMetadata = $metadata->propertyForField('object');
self::assertEquals(new ObjectNormalizer(Wrapper::class), $propertyMetadata->normalizer());

$propertyMetadata = $metadata->propertyForField('scalar');
self::assertEquals(new ObjectNormalizer(Wrapper::class), $propertyMetadata->normalizer());
}

public function testExtends(): void
{
$metadataFactory = new AttributeMetadataFactory();
Expand Down
28 changes: 27 additions & 1 deletion tests/Unit/Normalizer/ObjectNormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use ReflectionClass;
use ReflectionType;
use RuntimeException;
use Symfony\Component\TypeInfo\Type;

use function serialize;
use function unserialize;
Expand Down Expand Up @@ -115,7 +116,10 @@ public function testDenormalizeWithValue(): void
Email::fromString('info@patchlevel.de'),
);

$hydrator->expects($this->once())->method('hydrate')->with(ProfileCreated::class, ['profileId' => '1', 'email' => 'info@patchlevel.de'])
$hydrator->expects($this->once())->method('hydrate')->with(
ProfileCreated::class,
['profileId' => '1', 'email' => 'info@patchlevel.de'],
)
->willReturn($expected);

$normalizer = new ObjectNormalizer(ProfileCreated::class);
Expand Down Expand Up @@ -174,6 +178,28 @@ public function testAutoDetectMissingTypeBecauseNull(): void
$normalizer->getClassName();
}

public function testGeneric(): void
{
$hydrator = $this->createMock(Hydrator::class);

$normalizer = new ObjectNormalizer();
$normalizer->setHydrator($hydrator);
$normalizer->handleType(Type::generic(Type::object(ProfileCreated::class)));

self::assertEquals(ProfileCreated::class, $normalizer->getClassName());
}

public function testTemplate(): void
{
$hydrator = $this->createMock(Hydrator::class);

$normalizer = new ObjectNormalizer();
$normalizer->setHydrator($hydrator);
$normalizer->handleType(Type::template('T', Type::object(ProfileCreated::class)));

self::assertEquals(ProfileCreated::class, $normalizer->getClassName());
}

public function testSerialize(): void
{
$hydrator = $this->createMock(Hydrator::class);
Expand Down
Loading