diff --git a/src/Extractor/FromSourceMappingExtractor.php b/src/Extractor/FromSourceMappingExtractor.php index 8d51cf8e..91298f8d 100644 --- a/src/Extractor/FromSourceMappingExtractor.php +++ b/src/Extractor/FromSourceMappingExtractor.php @@ -114,12 +114,20 @@ private function transformType(string $target, Type $type = null): ?Type $builtinType = $type->getBuiltinType(); $className = $type->getClassName(); + $collection = $type->isCollection(); if (Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType() && \stdClass::class !== $type->getClassName()) { $builtinType = 'array' === $target ? Type::BUILTIN_TYPE_ARRAY : Type::BUILTIN_TYPE_OBJECT; $className = 'array' === $target ? null : \stdClass::class; } + // Use array for generator + if (Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType() && \Generator::class === $type->getClassName()) { + $builtinType = Type::BUILTIN_TYPE_ARRAY; + $className = null; + $collection = true; + } + // Use string for datetime if (Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType() && (\DateTimeInterface::class === $type->getClassName() || is_subclass_of($type->getClassName(), \DateTimeInterface::class))) { $builtinType = 'string'; @@ -132,7 +140,7 @@ private function transformType(string $target, Type $type = null): ?Type $builtinType, $type->isNullable(), $className, - $type->isCollection(), + $collection, $this->transformType($target, $collectionKeyTypes[0] ?? null), $this->transformType($target, $collectionValueTypes[0] ?? null) ); diff --git a/src/Transformer/ArrayTransformerFactory.php b/src/Transformer/ArrayTransformerFactory.php index 8b059581..155b46e2 100644 --- a/src/Transformer/ArrayTransformerFactory.php +++ b/src/Transformer/ArrayTransformerFactory.php @@ -18,7 +18,7 @@ final class ArrayTransformerFactory extends AbstractUniqueTypeTransformerFactory protected function createTransformer(Type $sourceType, Type $targetType, MapperMetadataInterface $mapperMetadata): ?TransformerInterface { - if (!$sourceType->isCollection()) { + if (!($sourceType->isCollection() || ($sourceType->getBuiltinType() === Type::BUILTIN_TYPE_OBJECT && $sourceType->getClassName() === \Generator::class))) { return null; } @@ -27,7 +27,7 @@ protected function createTransformer(Type $sourceType, Type $targetType, MapperM } if ([] === $sourceType->getCollectionValueTypes() || [] === $targetType->getCollectionValueTypes()) { - return new CopyTransformer(); + return new DictionaryTransformer(new CopyTransformer()); } $subItemTransformer = $this->chainTransformerFactory->getTransformer($sourceType->getCollectionValueTypes(), $targetType->getCollectionValueTypes(), $mapperMetadata); diff --git a/src/Transformer/ObjectTransformerFactory.php b/src/Transformer/ObjectTransformerFactory.php index 54c1a969..ad29e2fb 100644 --- a/src/Transformer/ObjectTransformerFactory.php +++ b/src/Transformer/ObjectTransformerFactory.php @@ -55,6 +55,10 @@ private function isObjectType(Type $type): bool return false; } + if ($type->getClassName() === \Generator::class) { + return false; + } + return true; } diff --git a/tests/AutoMapperTest.php b/tests/AutoMapperTest.php index 048dd328..5deb3905 100644 --- a/tests/AutoMapperTest.php +++ b/tests/AutoMapperTest.php @@ -20,6 +20,7 @@ use AutoMapper\Tests\Fixtures\ClassWithNullablePropertyInConstructor; use AutoMapper\Tests\Fixtures\ClassWithPrivateProperty; use AutoMapper\Tests\Fixtures\Fish; +use AutoMapper\Tests\Fixtures\FooGenerator; use AutoMapper\Tests\Fixtures\HasDateTime; use AutoMapper\Tests\Fixtures\HasDateTimeImmutable; use AutoMapper\Tests\Fixtures\HasDateTimeImmutableWithNullValue; @@ -1277,4 +1278,31 @@ public function testNoErrorWithUninitializedProperty(): void $this->autoMapper->map(new Uninitialized(), 'array', ['skip_null_values' => true]) ); } + + public function testAutoMappingGenerator(): void + { + $this->buildAutoMapper(mapPrivatePropertiesAndMethod: true); + $foo = new FooGenerator(); + + /** @var Fixtures\BarGenerator $bar */ + $bar = $this->autoMapper->map($foo, Fixtures\BarGenerator::class); + + // Test mapping to class + self::assertInstanceOf(Fixtures\BarGenerator::class, $bar); + + self::assertSame([1, 2, 3, 'foo' => 'bar'], $bar->generator); + self::assertSame([1, 2, 3], $bar->array); + + // Test mapping to array + $data = $this->autoMapper->map($foo, 'array'); + + self::assertSame([1, 2, 3, 'foo' => 'bar'], $data['generator']); + self::assertSame([1, 2, 3], $data['array']); + + // Test mapping to stdClass + $data = $this->autoMapper->map($foo, \stdClass::class); + + self::assertSame([1, 2, 3, 'foo' => 'bar'], $data->generator); + self::assertSame([1, 2, 3], $data->array); + } } diff --git a/tests/Fixtures/BarGenerator.php b/tests/Fixtures/BarGenerator.php new file mode 100644 index 00000000..e3b29b8b --- /dev/null +++ b/tests/Fixtures/BarGenerator.php @@ -0,0 +1,12 @@ + 'bar'; + } + + public function getArray(): array + { + return [1, 2, 3]; + } +} diff --git a/tests/Fixtures/UserDTO.php b/tests/Fixtures/UserDTO.php index b3d31ad3..4c1b384b 100644 --- a/tests/Fixtures/UserDTO.php +++ b/tests/Fixtures/UserDTO.php @@ -56,6 +56,10 @@ class UserDTO */ public $languages = []; + public array $generator = []; + + public array $array = []; + public function setName($name) { $this->name = $name; diff --git a/tests/Transformer/ArrayTransformerFactoryTest.php b/tests/Transformer/ArrayTransformerFactoryTest.php index bfa2624b..1c15e641 100644 --- a/tests/Transformer/ArrayTransformerFactoryTest.php +++ b/tests/Transformer/ArrayTransformerFactoryTest.php @@ -7,7 +7,7 @@ use AutoMapper\MapperMetadata; use AutoMapper\Transformer\ArrayTransformerFactory; use AutoMapper\Transformer\ChainTransformerFactory; -use AutoMapper\Transformer\CopyTransformer; +use AutoMapper\Transformer\DictionaryTransformer; use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyInfo\Type; @@ -22,7 +22,7 @@ public function testGetTransformer(): void $transformer = $factory->getTransformer([new Type('array', false, null, true)], [new Type('array', false, null, true)], $mapperMetadata); - self::assertInstanceOf(CopyTransformer::class, $transformer); + self::assertInstanceOf(DictionaryTransformer::class, $transformer); } public function testNoTransformerTargetNoCollection(): void