From d89263d342bb173586cc9aeb2200af69bd762b10 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Fri, 8 Mar 2024 17:48:04 +0100 Subject: [PATCH] feat(transformer): use directly the custom transformer instead of extracting it as a callback --- src/AutoMapper.php | 27 +++++----- src/Extractor/CustomTransformerExtractor.php | 42 --------------- src/Extractor/FromSourceMappingExtractor.php | 14 ++--- src/Extractor/FromTargetMappingExtractor.php | 14 ++--- ...oContextPropertyInfoExtractorDecorator.php | 2 +- src/Extractor/MappingExtractor.php | 5 +- src/Extractor/PropertyMapping.php | 14 +---- .../SourceTargetMappingExtractor.php | 13 ++--- src/GeneratedMapper.php | 13 +++++ .../CreateTargetStatementsGenerator.php | 17 +----- .../MapMethodStatementsGenerator.php | 5 +- src/Generator/MapperConstructorGenerator.php | 2 +- src/Generator/MapperGenerator.php | 3 -- src/Generator/PropertyStatementsGenerator.php | 23 +++----- .../Shared/ClassDiscriminatorResolver.php | 1 - .../DiscriminatorStatementsGenerator.php | 9 +++- src/Transformer/AbstractArrayTransformer.php | 13 ++++- src/Transformer/BuiltinTransformer.php | 6 ++- src/Transformer/CallbackTransformer.php | 6 ++- src/Transformer/ChainTransformerFactory.php | 33 ++++++++++-- src/Transformer/CopyEnumTransformer.php | 6 ++- src/Transformer/CopyTransformer.php | 6 ++- .../CustomModelTransformer.php | 36 +++++++++++++ .../CustomPropertyTransformer.php | 45 ++++++++++++++++ .../CustomTransformerFactory.php | 53 +++++++++++++++++++ .../CustomTransformersRegistry.php | 35 +++++++----- ...ateTimeInterfaceToImmutableTransformer.php | 6 ++- .../DateTimeInterfaceToMutableTransformer.php | 6 ++- .../DateTimeToStringTransformer.php | 6 ++- src/Transformer/MultipleTransformer.php | 16 ++++-- src/Transformer/NullableTransformer.php | 13 ++++- src/Transformer/ObjectTransformer.php | 6 ++- src/Transformer/SourceEnumTransformer.php | 6 ++- .../StringToDateTimeTransformer.php | 6 ++- .../StringToSymfonyUidTransformer.php | 6 ++- src/Transformer/SymfonyUidCopyTransformer.php | 6 ++- .../SymfonyUidToStringTransformer.php | 6 ++- src/Transformer/TargetEnumTransformer.php | 6 ++- src/Transformer/TransformerInterface.php | 4 +- .../TransformerPropertyFactoryInterface.php | 24 +++++++++ tests/AutoMapperBaseTest.php | 3 -- tests/AutoMapperWithCustomTransformerTest.php | 18 +++++++ .../FromSourceMappingExtractorTest.php | 2 - .../FromTargetMappingExtractorTest.php | 2 - .../IssueTargetToPopulate/VatEntity.php | 2 +- .../CustomTransformer/FooDependency.php | 13 +++++ .../TransformerWithDependency.php | 25 +++++++++ tests/MapperGeneratorMetadataFactoryTest.php | 5 -- tools/php-cs-fixer/.php-cs-fixer.php | 4 +- tools/phpstan/phpstan-baseline.neon | 40 -------------- 50 files changed, 448 insertions(+), 226 deletions(-) delete mode 100644 src/Extractor/CustomTransformerExtractor.php create mode 100644 src/Transformer/CustomTransformer/CustomModelTransformer.php create mode 100644 src/Transformer/CustomTransformer/CustomPropertyTransformer.php create mode 100644 src/Transformer/CustomTransformer/CustomTransformerFactory.php create mode 100644 src/Transformer/TransformerPropertyFactoryInterface.php create mode 100644 tests/Fixtures/Transformer/CustomTransformer/FooDependency.php create mode 100644 tests/Fixtures/Transformer/CustomTransformer/TransformerWithDependency.php diff --git a/src/AutoMapper.php b/src/AutoMapper.php index 8b721712..f4c1bdd3 100644 --- a/src/AutoMapper.php +++ b/src/AutoMapper.php @@ -5,8 +5,6 @@ namespace AutoMapper; use AutoMapper\Exception\NoMappingFoundException; -use AutoMapper\Extractor\ClassMethodToCallbackExtractor; -use AutoMapper\Extractor\CustomTransformerExtractor; use AutoMapper\Extractor\FromSourceMappingExtractor; use AutoMapper\Extractor\FromTargetMappingExtractor; use AutoMapper\Extractor\MapToContextPropertyInfoExtractorDecorator; @@ -18,6 +16,7 @@ use AutoMapper\Transformer\ArrayTransformerFactory; use AutoMapper\Transformer\BuiltinTransformerFactory; use AutoMapper\Transformer\ChainTransformerFactory; +use AutoMapper\Transformer\CustomTransformer\CustomTransformerFactory; use AutoMapper\Transformer\CustomTransformer\CustomTransformerInterface; use AutoMapper\Transformer\CustomTransformer\CustomTransformersRegistry; use AutoMapper\Transformer\DateTimeTransformerFactory; @@ -63,7 +62,7 @@ public function __construct( private readonly ClassLoaderInterface $classLoader, private readonly ChainTransformerFactory $chainTransformerFactory, public readonly CustomTransformersRegistry $customTransformersRegistry, - private readonly ?MapperGeneratorMetadataFactoryInterface $mapperConfigurationFactory = null + private readonly ?MapperGeneratorMetadataFactoryInterface $mapperConfigurationFactory = null, ) { } @@ -90,13 +89,18 @@ public function getMapper(string $source, string $target): MapperInterface $this->classLoader->loadClass($metadata); } - $this->mapperRegistry[$className] = new $className(); - $this->mapperRegistry[$className]->injectMappers($this); + /** @var GeneratedMapper $mapper */ + $mapper = new $className(); + $this->mapperRegistry[$className] = $mapper; + + $mapper->injectMappers($this); foreach ($metadata->getCallbacks() as $property => $callback) { - $this->mapperRegistry[$className]->addCallback($property, $callback); + $mapper->addCallback($property, $callback); } + $mapper->setCustomTransformers($this->customTransformersRegistry->getCustomTransformers()); + return $this->mapperRegistry[$className]; } @@ -152,9 +156,9 @@ public function bindTransformerFactory(TransformerFactoryInterface $transformerF } } - public function bindCustomTransformer(CustomTransformerInterface $customTransformer): void + public function bindCustomTransformer(CustomTransformerInterface $customTransformer, ?string $id = null): void { - $this->customTransformersRegistry->addCustomTransformer($customTransformer); + $this->customTransformersRegistry->addCustomTransformer($customTransformer, $id); } public static function create( @@ -165,7 +169,7 @@ public static function create( bool $attributeChecking = true, bool $autoRegister = true, string $dateTimeFormat = \DateTimeInterface::RFC3339, - bool $allowReadOnlyTargetToPopulate = false + bool $allowReadOnlyTargetToPopulate = false, ): self { if (class_exists(AttributeLoader::class)) { $loaderClass = new AttributeLoader(); @@ -179,7 +183,6 @@ public static function create( if (null === $loader) { $loader = new EvalLoader( new MapperGenerator( - new CustomTransformerExtractor(new ClassMethodToCallbackExtractor()), new ClassDiscriminatorResolver(new ClassDiscriminatorFromClassMetadata($classMetadataFactory)), $allowReadOnlyTargetToPopulate )); @@ -205,7 +208,6 @@ public static function create( new MapToContextPropertyInfoExtractorDecorator($reflectionExtractor), $reflectionExtractor, $transformerFactory, - $customTransformerRegistry, $classMetadataFactory ); @@ -214,7 +216,6 @@ public static function create( $reflectionExtractor, $reflectionExtractor, $transformerFactory, - $customTransformerRegistry, $classMetadataFactory, $nameConverter ); @@ -224,7 +225,6 @@ public static function create( new MapToContextPropertyInfoExtractorDecorator($reflectionExtractor), $reflectionExtractor, $transformerFactory, - $customTransformerRegistry, $classMetadataFactory, $nameConverter ); @@ -252,6 +252,7 @@ public static function create( $transformerFactory->addTransformerFactory(new ArrayTransformerFactory($transformerFactory)); $transformerFactory->addTransformerFactory(new ObjectTransformerFactory($autoMapper)); $transformerFactory->addTransformerFactory(new EnumTransformerFactory()); + $transformerFactory->addTransformerFactory(new CustomTransformerFactory($customTransformerRegistry)); if (class_exists(AbstractUid::class)) { $transformerFactory->addTransformerFactory(new SymfonyUidTransformerFactory()); diff --git a/src/Extractor/CustomTransformerExtractor.php b/src/Extractor/CustomTransformerExtractor.php deleted file mode 100644 index 1a75f990..00000000 --- a/src/Extractor/CustomTransformerExtractor.php +++ /dev/null @@ -1,42 +0,0 @@ - $customTransformerClass - */ - public function extract(string $customTransformerClass, ?Expr $propertyToTransform, Expr $sourceObject): Expr - { - if (!$propertyToTransform && is_a($customTransformerClass, CustomModelTransformerInterface::class, allow_string: true)) { - throw new \LogicException('CustomModelTransformerInterface must use $propertyToTransform.'); - } - - $arg = is_a($customTransformerClass, CustomModelTransformerInterface::class, allow_string: true) - ? $propertyToTransform - // let's pass the full object when using "property" custom transform for more flexibility - : $sourceObject; - - return $this->extractor->extract( - $customTransformerClass, - 'transform', - [new Arg($arg)] - ); - } -} diff --git a/src/Extractor/FromSourceMappingExtractor.php b/src/Extractor/FromSourceMappingExtractor.php index 8a146cc9..8d51cf8e 100644 --- a/src/Extractor/FromSourceMappingExtractor.php +++ b/src/Extractor/FromSourceMappingExtractor.php @@ -6,8 +6,8 @@ use AutoMapper\Exception\InvalidMappingException; use AutoMapper\MapperGeneratorMetadataInterface; -use AutoMapper\Transformer\CustomTransformer\CustomTransformersRegistry; use AutoMapper\Transformer\TransformerFactoryInterface; +use AutoMapper\Transformer\TransformerPropertyFactoryInterface; use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; use Symfony\Component\PropertyInfo\PropertyReadInfo; use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface; @@ -33,12 +33,11 @@ public function __construct( PropertyInfoExtractorInterface $propertyInfoExtractor, PropertyReadInfoExtractorInterface $readInfoExtractor, PropertyWriteInfoExtractorInterface $writeInfoExtractor, - TransformerFactoryInterface $transformerFactory, - CustomTransformersRegistry $customTransformerRegistry, + TransformerFactoryInterface|TransformerPropertyFactoryInterface $transformerFactory, ClassMetadataFactoryInterface $classMetadataFactory = null, private readonly ?AdvancedNameConverterInterface $nameConverter = null, ) { - parent::__construct($propertyInfoExtractor, $readInfoExtractor, $writeInfoExtractor, $transformerFactory, $customTransformerRegistry, $classMetadataFactory); + parent::__construct($propertyInfoExtractor, $readInfoExtractor, $writeInfoExtractor, $transformerFactory, $classMetadataFactory); } public function getPropertiesMapping(MapperGeneratorMetadataInterface $mapperMetadata): array @@ -77,8 +76,11 @@ public function getPropertiesMapping(MapperGeneratorMetadataInterface $mapperMet } } - $transformer = $this->customTransformerRegistry->getCustomTransformerClass($mapperMetadata, $sourceTypes, $targetTypes, $property) - ?? $this->transformerFactory->getTransformer($sourceTypes, $targetTypes, $mapperMetadata); + if ($this->transformerFactory instanceof TransformerPropertyFactoryInterface) { + $transformer = $this->transformerFactory->getPropertyTransformer($sourceTypes, $targetTypes, $mapperMetadata, $property); + } else { + $transformer = $this->transformerFactory->getTransformer($sourceTypes, $targetTypes, $mapperMetadata); + } if (null === $transformer) { continue; diff --git a/src/Extractor/FromTargetMappingExtractor.php b/src/Extractor/FromTargetMappingExtractor.php index cee2f6e1..1b473cef 100644 --- a/src/Extractor/FromTargetMappingExtractor.php +++ b/src/Extractor/FromTargetMappingExtractor.php @@ -6,8 +6,8 @@ use AutoMapper\Exception\InvalidMappingException; use AutoMapper\MapperGeneratorMetadataInterface; -use AutoMapper\Transformer\CustomTransformer\CustomTransformersRegistry; use AutoMapper\Transformer\TransformerFactoryInterface; +use AutoMapper\Transformer\TransformerPropertyFactoryInterface; use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; use Symfony\Component\PropertyInfo\PropertyReadInfo; use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface; @@ -34,12 +34,11 @@ public function __construct( PropertyInfoExtractorInterface $propertyInfoExtractor, PropertyReadInfoExtractorInterface $readInfoExtractor, PropertyWriteInfoExtractorInterface $writeInfoExtractor, - TransformerFactoryInterface $transformerFactory, - CustomTransformersRegistry $customTransformerRegistry, + TransformerFactoryInterface|TransformerPropertyFactoryInterface $transformerFactory, ClassMetadataFactoryInterface $classMetadataFactory = null, private readonly ?AdvancedNameConverterInterface $nameConverter = null, ) { - parent::__construct($propertyInfoExtractor, $readInfoExtractor, $writeInfoExtractor, $transformerFactory, $customTransformerRegistry, $classMetadataFactory); + parent::__construct($propertyInfoExtractor, $readInfoExtractor, $writeInfoExtractor, $transformerFactory, $classMetadataFactory); } public function getPropertiesMapping(MapperGeneratorMetadataInterface $mapperMetadata): array @@ -72,8 +71,11 @@ public function getPropertiesMapping(MapperGeneratorMetadataInterface $mapperMet } } - $transformer = $this->customTransformerRegistry->getCustomTransformerClass($mapperMetadata, $sourceTypes, $targetTypes, $property) - ?? $this->transformerFactory->getTransformer($sourceTypes, $targetTypes, $mapperMetadata); + if ($this->transformerFactory instanceof TransformerPropertyFactoryInterface) { + $transformer = $this->transformerFactory->getPropertyTransformer($sourceTypes, $targetTypes, $mapperMetadata, $property); + } else { + $transformer = $this->transformerFactory->getTransformer($sourceTypes, $targetTypes, $mapperMetadata); + } if (null === $transformer) { continue; diff --git a/src/Extractor/MapToContextPropertyInfoExtractorDecorator.php b/src/Extractor/MapToContextPropertyInfoExtractorDecorator.php index 94bfadd7..933dc6f7 100644 --- a/src/Extractor/MapToContextPropertyInfoExtractorDecorator.php +++ b/src/Extractor/MapToContextPropertyInfoExtractorDecorator.php @@ -22,7 +22,7 @@ final readonly class MapToContextPropertyInfoExtractorDecorator implements PropertyListExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface, PropertyReadInfoExtractorInterface, PropertyWriteInfoExtractorInterface, ConstructorArgumentTypeExtractorInterface { public function __construct( - private ReflectionExtractor $decorated + private ReflectionExtractor $decorated, ) { } diff --git a/src/Extractor/MappingExtractor.php b/src/Extractor/MappingExtractor.php index b881353d..40c26bc4 100644 --- a/src/Extractor/MappingExtractor.php +++ b/src/Extractor/MappingExtractor.php @@ -4,8 +4,8 @@ namespace AutoMapper\Extractor; -use AutoMapper\Transformer\CustomTransformer\CustomTransformersRegistry; use AutoMapper\Transformer\TransformerFactoryInterface; +use AutoMapper\Transformer\TransformerPropertyFactoryInterface; use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; use Symfony\Component\PropertyInfo\PropertyReadInfo; use Symfony\Component\PropertyInfo\PropertyReadInfoExtractorInterface; @@ -25,8 +25,7 @@ public function __construct( protected readonly PropertyInfoExtractorInterface $propertyInfoExtractor, protected readonly PropertyReadInfoExtractorInterface $readInfoExtractor, protected readonly PropertyWriteInfoExtractorInterface $writeInfoExtractor, - protected readonly TransformerFactoryInterface $transformerFactory, - protected readonly CustomTransformersRegistry $customTransformerRegistry, + protected readonly TransformerFactoryInterface|TransformerPropertyFactoryInterface $transformerFactory, private readonly ?ClassMetadataFactoryInterface $classMetadataFactory = null, ) { } diff --git a/src/Extractor/PropertyMapping.php b/src/Extractor/PropertyMapping.php index 81e43f47..d290d728 100644 --- a/src/Extractor/PropertyMapping.php +++ b/src/Extractor/PropertyMapping.php @@ -5,7 +5,6 @@ namespace AutoMapper\Extractor; use AutoMapper\MapperGeneratorMetadataInterface; -use AutoMapper\Transformer\CustomTransformer\CustomTransformerInterface; use AutoMapper\Transformer\TransformerInterface; /** @@ -22,8 +21,7 @@ public function __construct( public ?ReadAccessor $readAccessor, public ?WriteMutator $writeMutator, public ?WriteMutator $writeMutatorConstructor, - /** @var TransformerInterface|class-string */ - public TransformerInterface|string $transformer, + public TransformerInterface $transformer, public string $property, public bool $checkExists = false, public ?array $sourceGroups = null, @@ -42,14 +40,4 @@ public function shouldIgnoreProperty(bool $shouldMapPrivateProperties = true): b || $this->targetIgnored || !($shouldMapPrivateProperties || $this->isPublic); } - - /** - * @phpstan-assert-if-false TransformerInterface $this->transformer - * - * @phpstan-assert-if-true string $this->transformer - */ - public function hasCustomTransformer(): bool - { - return \is_string($this->transformer); - } } diff --git a/src/Extractor/SourceTargetMappingExtractor.php b/src/Extractor/SourceTargetMappingExtractor.php index b4d01eb3..f380feda 100644 --- a/src/Extractor/SourceTargetMappingExtractor.php +++ b/src/Extractor/SourceTargetMappingExtractor.php @@ -6,6 +6,7 @@ use AutoMapper\MapperGeneratorMetadataInterface; use AutoMapper\MapperMetadataInterface; +use AutoMapper\Transformer\TransformerPropertyFactoryInterface; use Symfony\Component\PropertyInfo\PropertyReadInfo; /** @@ -56,7 +57,7 @@ public function getPropertiesMapping(MapperGeneratorMetadataInterface $mapperMet continue; } - if ($propertyMapping = $this->toPropertyMapping($mapperMetadata, $property, onlyCustomTransformer: true)) { + if ($propertyMapping = $this->toPropertyMapping($mapperMetadata, $property)) { $mapping[] = $propertyMapping; } } @@ -64,7 +65,7 @@ public function getPropertiesMapping(MapperGeneratorMetadataInterface $mapperMet return $mapping; } - private function toPropertyMapping(MapperGeneratorMetadataInterface $mapperMetadata, string $property, bool $onlyCustomTransformer = false): ?PropertyMapping + private function toPropertyMapping(MapperGeneratorMetadataInterface $mapperMetadata, string $property): ?PropertyMapping { $targetMutatorConstruct = $this->getWriteMutator($mapperMetadata->getSource(), $mapperMetadata->getTarget(), $property, [ 'enable_constructor_extraction' => true, @@ -77,9 +78,9 @@ private function toPropertyMapping(MapperGeneratorMetadataInterface $mapperMetad $sourceTypes = $this->propertyInfoExtractor->getTypes($mapperMetadata->getSource(), $property) ?? []; $targetTypes = $this->propertyInfoExtractor->getTypes($mapperMetadata->getTarget(), $property) ?? []; - $transformer = $this->customTransformerRegistry->getCustomTransformerClass($mapperMetadata, $sourceTypes, $targetTypes, $property); - - if (null === $transformer && !$onlyCustomTransformer) { + if ($this->transformerFactory instanceof TransformerPropertyFactoryInterface) { + $transformer = $this->transformerFactory->getPropertyTransformer($sourceTypes, $targetTypes, $mapperMetadata, $property); + } else { $transformer = $this->transformerFactory->getTransformer($sourceTypes, $targetTypes, $mapperMetadata); } @@ -115,7 +116,7 @@ private function guessMaxDepth(MapperMetadataInterface $mapperMetadata, string $ null !== $maxDepthSource && null !== $maxDepthTarget => min($maxDepthSource, $maxDepthTarget), null !== $maxDepthSource => $maxDepthSource, null !== $maxDepthTarget => $maxDepthTarget, - default => null + default => null, }; } } diff --git a/src/GeneratedMapper.php b/src/GeneratedMapper.php index 33835d61..ae5c21fa 100644 --- a/src/GeneratedMapper.php +++ b/src/GeneratedMapper.php @@ -4,6 +4,8 @@ namespace AutoMapper; +use AutoMapper\Transformer\CustomTransformer\CustomTransformerInterface; + /** * Class derived for each generated mapper. * @@ -28,6 +30,9 @@ abstract class GeneratedMapper implements MapperInterface protected $circularReferenceLimit; + /** @var array */ + protected array $transformers = []; + /** * Add a callable for a specific property. */ @@ -52,4 +57,12 @@ public function setCircularReferenceLimit(?int $circularReferenceLimit): void { $this->circularReferenceLimit = $circularReferenceLimit; } + + /** + * @param array $transformers + */ + public function setCustomTransformers(array $transformers): void + { + $this->transformers = $transformers; + } } diff --git a/src/Generator/CreateTargetStatementsGenerator.php b/src/Generator/CreateTargetStatementsGenerator.php index d9b7c09a..492722d3 100644 --- a/src/Generator/CreateTargetStatementsGenerator.php +++ b/src/Generator/CreateTargetStatementsGenerator.php @@ -4,7 +4,6 @@ namespace AutoMapper\Generator; -use AutoMapper\Extractor\CustomTransformerExtractor; use AutoMapper\Extractor\PropertyMapping; use AutoMapper\Generator\Shared\CachedReflectionStatementsGenerator; use AutoMapper\Generator\Shared\DiscriminatorStatementsGenerator; @@ -28,7 +27,6 @@ public function __construct( private DiscriminatorStatementsGenerator $discriminatorStatementsGenerator, private CachedReflectionStatementsGenerator $cachedReflectionStatementsGenerator, - private CustomTransformerExtractor $customTransformerExtractor, ?Parser $parser = null, ) { $this->parser = $parser ?? (new ParserFactory())->create(ParserFactory::PREFER_PHP7); @@ -207,22 +205,11 @@ private function constructorArgument(PropertyMapping $propertyMapping): ?array $constructVar = $variableRegistry->getVariableWithUniqueName('constructArg'); - if ($propertyMapping->hasCustomTransformer()) { - $propStatements = []; - /* - * let's extract custom transformer's transform() method in a closure, - * and add it as a constructor argument - */ - $output = $this->customTransformerExtractor->extract( - $propertyMapping->transformer, - $propertyMapping->readAccessor?->getExpression($variableRegistry->getSourceInput()), - $variableRegistry->getSourceInput() - ); - } elseif ($propertyMapping->readAccessor) { + if ($propertyMapping->readAccessor) { $fieldValueExpr = $propertyMapping->readAccessor->getExpression($variableRegistry->getSourceInput()); /* Get extract and transform statements for this property */ - [$output, $propStatements] = $propertyMapping->transformer->transform($fieldValueExpr, $constructVar, $propertyMapping, $variableRegistry->getUniqueVariableScope()); + [$output, $propStatements] = $propertyMapping->transformer->transform($fieldValueExpr, $constructVar, $propertyMapping, $variableRegistry->getUniqueVariableScope(), $variableRegistry->getSourceInput()); } else { return null; } diff --git a/src/Generator/MapMethodStatementsGenerator.php b/src/Generator/MapMethodStatementsGenerator.php index 3186ead5..d70b066f 100644 --- a/src/Generator/MapMethodStatementsGenerator.php +++ b/src/Generator/MapMethodStatementsGenerator.php @@ -5,7 +5,6 @@ namespace AutoMapper\Generator; use AutoMapper\Exception\ReadOnlyTargetException; -use AutoMapper\Extractor\CustomTransformerExtractor; use AutoMapper\Generator\Shared\CachedReflectionStatementsGenerator; use AutoMapper\Generator\Shared\DiscriminatorStatementsGenerator; use AutoMapper\MapperContext; @@ -28,15 +27,13 @@ public function __construct( DiscriminatorStatementsGenerator $discriminatorStatementsGenerator, CachedReflectionStatementsGenerator $cachedReflectionStatementsGenerator, - CustomTransformerExtractor $customTransformerExtractor, private bool $allowReadOnlyTargetToPopulate = false, ) { $this->createObjectStatementsGenerator = new CreateTargetStatementsGenerator( $discriminatorStatementsGenerator, $cachedReflectionStatementsGenerator, - $customTransformerExtractor ); - $this->propertyStatementsGenerator = new PropertyStatementsGenerator($customTransformerExtractor); + $this->propertyStatementsGenerator = new PropertyStatementsGenerator(); } /** diff --git a/src/Generator/MapperConstructorGenerator.php b/src/Generator/MapperConstructorGenerator.php index e65c6690..0e988657 100644 --- a/src/Generator/MapperConstructorGenerator.php +++ b/src/Generator/MapperConstructorGenerator.php @@ -17,7 +17,7 @@ final readonly class MapperConstructorGenerator { public function __construct( - private CachedReflectionStatementsGenerator $cachedReflectionStatementsGenerator + private CachedReflectionStatementsGenerator $cachedReflectionStatementsGenerator, ) { } diff --git a/src/Generator/MapperGenerator.php b/src/Generator/MapperGenerator.php index 2ecbca1e..b5558b4c 100644 --- a/src/Generator/MapperGenerator.php +++ b/src/Generator/MapperGenerator.php @@ -6,7 +6,6 @@ use AutoMapper\AutoMapperRegistryInterface; use AutoMapper\Exception\CompileException; -use AutoMapper\Extractor\CustomTransformerExtractor; use AutoMapper\GeneratedMapper; use AutoMapper\Generator\Shared\CachedReflectionStatementsGenerator; use AutoMapper\Generator\Shared\ClassDiscriminatorResolver; @@ -31,7 +30,6 @@ private MapMethodStatementsGenerator $mapMethodStatementsGenerator; public function __construct( - CustomTransformerExtractor $customTransformerExtractor, ClassDiscriminatorResolver $classDiscriminatorResolver, bool $allowReadOnlyTargetToPopulate = false, ) { @@ -42,7 +40,6 @@ public function __construct( $this->mapMethodStatementsGenerator = new MapMethodStatementsGenerator( $discriminatorStatementsGenerator = new DiscriminatorStatementsGenerator($classDiscriminatorResolver), $cachedReflectionStatementsGenerator, - $customTransformerExtractor, $allowReadOnlyTargetToPopulate, ); diff --git a/src/Generator/PropertyStatementsGenerator.php b/src/Generator/PropertyStatementsGenerator.php index d45d9c2e..cc1e8d09 100644 --- a/src/Generator/PropertyStatementsGenerator.php +++ b/src/Generator/PropertyStatementsGenerator.php @@ -4,7 +4,6 @@ namespace AutoMapper\Generator; -use AutoMapper\Extractor\CustomTransformerExtractor; use AutoMapper\Extractor\PropertyMapping; use AutoMapper\Extractor\WriteMutator; use AutoMapper\Transformer\AssignedByReferenceTransformerInterface; @@ -18,7 +17,6 @@ private PropertyConditionsGenerator $propertyConditionsGenerator; public function __construct( - private CustomTransformerExtractor $customTransformerExtractor, ) { $this->propertyConditionsGenerator = new PropertyConditionsGenerator(); } @@ -41,19 +39,14 @@ public function generate(PropertyMapping $propertyMapping): array $fieldValueVariable = $propertyMapping->readAccessor->getExpression($variableRegistry->getSourceInput()); } - if ($propertyMapping->hasCustomTransformer()) { - $propStatements = []; - - $output = $this->customTransformerExtractor->extract($propertyMapping->transformer, $fieldValueVariable, $variableRegistry->getSourceInput()); - } else { - /* Create expression to transform the read value into the wanted written value, depending on the transform it may add new statements to get the correct value */ - [$output, $propStatements] = $propertyMapping->transformer->transform( - $fieldValueVariable, - $variableRegistry->getResult(), - $propertyMapping, - $variableRegistry->getUniqueVariableScope() - ); - } + /* Create expression to transform the read value into the wanted written value, depending on the transform it may add new statements to get the correct value */ + [$output, $propStatements] = $propertyMapping->transformer->transform( + $fieldValueVariable, + $variableRegistry->getResult(), + $propertyMapping, + $variableRegistry->getUniqueVariableScope(), + $variableRegistry->getSourceInput() + ); if ($propertyMapping->writeMutator && $propertyMapping->writeMutator->type !== WriteMutator::TYPE_ADDER_AND_REMOVER) { /** Create expression to write the transformed value to the target only if not add / remove mutator, as it's already called by the transformer in this case */ diff --git a/src/Generator/Shared/ClassDiscriminatorResolver.php b/src/Generator/Shared/ClassDiscriminatorResolver.php index 34629c43..cdeb04da 100644 --- a/src/Generator/Shared/ClassDiscriminatorResolver.php +++ b/src/Generator/Shared/ClassDiscriminatorResolver.php @@ -25,7 +25,6 @@ public function hasClassDiscriminator(MapperGeneratorMetadataInterface $mapperMe if (!$mapperMetadata->targetIsAUserDefinedClass() || !($propertyMapping = $this->propertyMapping($mapperMetadata)) || !$propertyMapping->transformer instanceof TransformerInterface - || $propertyMapping->hasCustomTransformer() ) { return false; } diff --git a/src/Generator/Shared/DiscriminatorStatementsGenerator.php b/src/Generator/Shared/DiscriminatorStatementsGenerator.php index 46a91d1c..f9897761 100644 --- a/src/Generator/Shared/DiscriminatorStatementsGenerator.php +++ b/src/Generator/Shared/DiscriminatorStatementsGenerator.php @@ -20,7 +20,7 @@ final readonly class DiscriminatorStatementsGenerator { public function __construct( - private ClassDiscriminatorResolver $classDiscriminatorResolver + private ClassDiscriminatorResolver $classDiscriminatorResolver, ) { } @@ -83,6 +83,10 @@ public function createTargetStatements(MapperGeneratorMetadataInterface $mapperM $propertyMapping = $this->classDiscriminatorResolver->propertyMapping($mapperMetadata); + if (!$propertyMapping || $propertyMapping->readAccessor === null) { + return []; + } + $variableRegistry = $mapperMetadata->getVariableRegistry(); // Generate the code that allows to put the type into the output variable, @@ -91,7 +95,8 @@ public function createTargetStatements(MapperGeneratorMetadataInterface $mapperM $propertyMapping->readAccessor->getExpression($variableRegistry->getSourceInput()), $variableRegistry->getResult(), $propertyMapping, - $variableRegistry->getUniqueVariableScope() + $variableRegistry->getUniqueVariableScope(), + $variableRegistry->getSourceInput() ); foreach ($this->classDiscriminatorResolver->discriminatorMapperNamesIndexedByTypeValue($mapperMetadata) as $typeValue => $discriminatorMapperName) { diff --git a/src/Transformer/AbstractArrayTransformer.php b/src/Transformer/AbstractArrayTransformer.php index 226d00f5..7a2a679f 100644 --- a/src/Transformer/AbstractArrayTransformer.php +++ b/src/Transformer/AbstractArrayTransformer.php @@ -23,8 +23,17 @@ public function __construct( abstract protected function getAssignExpr(Expr $valuesVar, Expr $outputVar, Expr $loopKeyVar, bool $assignByRef): Expr; - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + + $source = new Expr\Variable('value'); + } else { + /** @var Expr\Variable $source */ + $source = func_get_arg(4); + } + /** * $values = [];. */ @@ -39,7 +48,7 @@ public function transform(Expr $input, Expr $target, PropertyMapping $propertyMa $assignByRef = $this->itemTransformer instanceof AssignedByReferenceTransformerInterface && $this->itemTransformer->assignByRef(); /* Get the transform statements for the source property */ - [$output, $itemStatements] = $this->itemTransformer->transform($loopValueVar, $target, $propertyMapping, $uniqueVariableScope); + [$output, $itemStatements] = $this->itemTransformer->transform($loopValueVar, $target, $propertyMapping, $uniqueVariableScope, $source); if ($propertyMapping->writeMutator && $propertyMapping->writeMutator->type === WriteMutator::TYPE_ADDER_AND_REMOVER) { /** diff --git a/src/Transformer/BuiltinTransformer.php b/src/Transformer/BuiltinTransformer.php index eb63472d..485df359 100644 --- a/src/Transformer/BuiltinTransformer.php +++ b/src/Transformer/BuiltinTransformer.php @@ -63,8 +63,12 @@ public function __construct( ) { } - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + $targetTypes = array_map(function (Type $type) { return $type->getBuiltinType(); }, $this->targetTypes); diff --git a/src/Transformer/CallbackTransformer.php b/src/Transformer/CallbackTransformer.php index f9472110..d0b35509 100644 --- a/src/Transformer/CallbackTransformer.php +++ b/src/Transformer/CallbackTransformer.php @@ -22,8 +22,12 @@ public function __construct( ) { } - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + $arguments = [ new Arg($input), new Arg($target), diff --git a/src/Transformer/ChainTransformerFactory.php b/src/Transformer/ChainTransformerFactory.php index b813448a..a387a4ed 100644 --- a/src/Transformer/ChainTransformerFactory.php +++ b/src/Transformer/ChainTransformerFactory.php @@ -9,19 +9,19 @@ /** * @author Joel Wurtz */ -final class ChainTransformerFactory implements TransformerFactoryInterface +final class ChainTransformerFactory implements TransformerPropertyFactoryInterface, TransformerFactoryInterface { - /** @var array> */ + /** @var array> */ private array $factories = []; - /** @var TransformerFactoryInterface[]|null */ + /** @var list|null */ private ?array $sorted = null; /** * Biggest priority is MultipleTransformerFactory with 128, so default priority will be bigger in order to * be used before it, 256 should be enough. */ - public function addTransformerFactory(TransformerFactoryInterface $transformerFactory, int $priority = 256): void + public function addTransformerFactory(TransformerFactoryInterface|TransformerPropertyFactoryInterface $transformerFactory, int $priority = 256): void { $this->sorted = null; @@ -49,12 +49,35 @@ public function hasTransformerFactory(TransformerFactoryInterface $transformerFa return false; } + public function getPropertyTransformer(?array $sourceTypes, ?array $targetTypes, MapperMetadataInterface $mapperMetadata, string $property): ?TransformerInterface + { + $this->sortFactories(); + + foreach ($this->sorted ?? [] as $factory) { + if ($factory instanceof TransformerPropertyFactoryInterface) { + $transformer = $factory->getPropertyTransformer($sourceTypes, $targetTypes, $mapperMetadata, $property); + } else { + $transformer = $factory->getTransformer($sourceTypes, $targetTypes, $mapperMetadata); + } + + if (null !== $transformer) { + return $transformer; + } + } + + return null; + } + public function getTransformer(?array $sourceTypes, ?array $targetTypes, MapperMetadataInterface $mapperMetadata): ?TransformerInterface { $this->sortFactories(); foreach ($this->sorted ?? [] as $factory) { - $transformer = $factory->getTransformer($sourceTypes, $targetTypes, $mapperMetadata); + $transformer = null; + + if ($factory instanceof TransformerFactoryInterface) { + $transformer = $factory->getTransformer($sourceTypes, $targetTypes, $mapperMetadata); + } if (null !== $transformer) { return $transformer; diff --git a/src/Transformer/CopyEnumTransformer.php b/src/Transformer/CopyEnumTransformer.php index 371016c3..e15a668b 100644 --- a/src/Transformer/CopyEnumTransformer.php +++ b/src/Transformer/CopyEnumTransformer.php @@ -15,8 +15,12 @@ */ final class CopyEnumTransformer implements TransformerInterface { - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + /* No transform here it's the same value and it's a copy so we do not need to clone */ return [$input, []]; } diff --git a/src/Transformer/CopyTransformer.php b/src/Transformer/CopyTransformer.php index c6421e64..b4b060f2 100644 --- a/src/Transformer/CopyTransformer.php +++ b/src/Transformer/CopyTransformer.php @@ -15,8 +15,12 @@ */ final class CopyTransformer implements TransformerInterface { - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + /* No transform here it's the same value and it's a copy so we do not need to clone */ return [$input, []]; } diff --git a/src/Transformer/CustomTransformer/CustomModelTransformer.php b/src/Transformer/CustomTransformer/CustomModelTransformer.php new file mode 100644 index 00000000..fb4e102e --- /dev/null +++ b/src/Transformer/CustomTransformer/CustomModelTransformer.php @@ -0,0 +1,36 @@ +transformers['id']($input) + */ + return [new Expr\MethodCall( + new Expr\ArrayDimFetch(new Expr\PropertyFetch(new Expr\Variable('this'), 'transformers'), new Scalar\String_($this->customTransformerId)), + 'transform', + [ + new Arg($input), + ] + ), []]; + } +} diff --git a/src/Transformer/CustomTransformer/CustomPropertyTransformer.php b/src/Transformer/CustomTransformer/CustomPropertyTransformer.php new file mode 100644 index 00000000..d6eda308 --- /dev/null +++ b/src/Transformer/CustomTransformer/CustomPropertyTransformer.php @@ -0,0 +1,45 @@ +transformers['id']($input) + */ + return [new Expr\MethodCall( + new Expr\ArrayDimFetch(new Expr\PropertyFetch(new Expr\Variable('this'), 'transformers'), new Scalar\String_($this->customTransformerId)), + 'transform', + [ + new Arg($source), + ] + ), []]; + } +} diff --git a/src/Transformer/CustomTransformer/CustomTransformerFactory.php b/src/Transformer/CustomTransformer/CustomTransformerFactory.php new file mode 100644 index 00000000..40e80a60 --- /dev/null +++ b/src/Transformer/CustomTransformer/CustomTransformerFactory.php @@ -0,0 +1,53 @@ +customTransformersRegistry->getCustomTransformerClass($mapperMetadata, $sourceTypes, $targetTypes, $property); + + if (null === $customTransformer) { + return null; + } + + [$id, $transformer] = $customTransformer; + + if ($transformer instanceof CustomModelTransformerInterface) { + return new CustomModelTransformer($id); + } + + if ($transformer instanceof CustomPropertyTransformerInterface) { + return new CustomPropertyTransformer($id); + } + + return null; + } +} diff --git a/src/Transformer/CustomTransformer/CustomTransformersRegistry.php b/src/Transformer/CustomTransformer/CustomTransformersRegistry.php index 029941ce..2e224b78 100644 --- a/src/Transformer/CustomTransformer/CustomTransformersRegistry.php +++ b/src/Transformer/CustomTransformer/CustomTransformersRegistry.php @@ -12,33 +12,44 @@ */ final class CustomTransformersRegistry { - /** @var list */ + /** @var array */ private array $customTransformers = []; - /** @var list|null */ + /** @var array|null */ private ?array $prioritizedCustomTransformers = null; - public function addCustomTransformer(CustomTransformerInterface $customTransformer): void + /** + * @return array + */ + public function getCustomTransformers(): array { - if (!\in_array($customTransformer, $this->customTransformers, true)) { - $this->customTransformers[] = $customTransformer; - } + return $this->customTransformers; + } + + public function addCustomTransformer(CustomTransformerInterface $customTransformer, ?string $id = null): void + { + $id = $id ?? $customTransformer::class; + $this->customTransformers[$id] = $customTransformer; } /** * @param Type[] $sourceTypes * @param Type[] $targetTypes * - * @return class-string|null + * @return array{string, CustomTransformerInterface}|null */ - public function getCustomTransformerClass(MapperMetadataInterface $mapperMetadata, array $sourceTypes, array $targetTypes, string $propertyName): ?string + public function getCustomTransformerClass(MapperMetadataInterface $mapperMetadata, array $sourceTypes, array $targetTypes, string $propertyName): ?array { - foreach ($this->prioritizedCustomTransformers() as $customTransformer) { + /** + * @var string $id + * @var CustomTransformerInterface $customTransformer + */ + foreach ($this->prioritizedCustomTransformers() as $id => $customTransformer) { if ( ($customTransformer instanceof CustomModelTransformerInterface && $sourceTypes && $targetTypes && $customTransformer->supports($sourceTypes, $targetTypes)) || $customTransformer instanceof CustomPropertyTransformerInterface && $customTransformer->supports($mapperMetadata->getSource(), $mapperMetadata->getTarget(), $propertyName) ) { - return $customTransformer::class; + return [$id, $customTransformer]; } } @@ -46,14 +57,14 @@ public function getCustomTransformerClass(MapperMetadataInterface $mapperMetadat } /** - * @return list + * @return array */ private function prioritizedCustomTransformers(): array { if (null === $this->prioritizedCustomTransformers) { $this->prioritizedCustomTransformers = $this->customTransformers; - usort( + uasort( $this->prioritizedCustomTransformers, static function (CustomTransformerInterface $a, CustomTransformerInterface $b): int { $aPriority = $a instanceof PrioritizedCustomTransformerInterface ? $a->getPriority() : 0; diff --git a/src/Transformer/DateTimeInterfaceToImmutableTransformer.php b/src/Transformer/DateTimeInterfaceToImmutableTransformer.php index 0267e61b..45241298 100644 --- a/src/Transformer/DateTimeInterfaceToImmutableTransformer.php +++ b/src/Transformer/DateTimeInterfaceToImmutableTransformer.php @@ -17,8 +17,12 @@ */ final class DateTimeInterfaceToImmutableTransformer implements TransformerInterface { - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + /* * Handles all DateTime instance types using createFromInterface. * diff --git a/src/Transformer/DateTimeInterfaceToMutableTransformer.php b/src/Transformer/DateTimeInterfaceToMutableTransformer.php index 16e38b0d..c73aaa8e 100644 --- a/src/Transformer/DateTimeInterfaceToMutableTransformer.php +++ b/src/Transformer/DateTimeInterfaceToMutableTransformer.php @@ -17,8 +17,12 @@ */ final class DateTimeInterfaceToMutableTransformer implements TransformerInterface { - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + /* * Handles all DateTime instance types using createFromInterface. * diff --git a/src/Transformer/DateTimeToStringTransformer.php b/src/Transformer/DateTimeToStringTransformer.php index 407a3504..68e3c1a7 100644 --- a/src/Transformer/DateTimeToStringTransformer.php +++ b/src/Transformer/DateTimeToStringTransformer.php @@ -23,8 +23,12 @@ public function __construct( ) { } - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + /* * Format the date time object to a string. * diff --git a/src/Transformer/MultipleTransformer.php b/src/Transformer/MultipleTransformer.php index 07be6ab0..80782fea 100644 --- a/src/Transformer/MultipleTransformer.php +++ b/src/Transformer/MultipleTransformer.php @@ -43,11 +43,17 @@ public function __construct( ) { } - /** - * {@inheritdoc} - */ - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + + $source = new Expr\Variable('value'); + } else { + /** @var Expr\Variable $source */ + $source = func_get_arg(4); + } + $output = new Expr\Variable($uniqueVariableScope->getUniqueName('value')); $statements = [ new Stmt\Expression(new Expr\Assign($output, $input)), @@ -69,7 +75,7 @@ public function transform(Expr $input, Expr $target, PropertyMapping $propertyMa $transformer = $transformerData['transformer']; $type = $transformerData['type']; - [$transformerOutput, $transformerStatements] = $transformer->transform($input, $target, $propertyMapping, $uniqueVariableScope); + [$transformerOutput, $transformerStatements] = $transformer->transform($input, $target, $propertyMapping, $uniqueVariableScope, $source); $assignClass = ($transformer instanceof AssignedByReferenceTransformerInterface && $transformer->assignByRef()) ? Expr\AssignRef::class : Expr\Assign::class; $statements[] = new Stmt\If_( diff --git a/src/Transformer/NullableTransformer.php b/src/Transformer/NullableTransformer.php index 0aaed6d0..3f403ddc 100644 --- a/src/Transformer/NullableTransformer.php +++ b/src/Transformer/NullableTransformer.php @@ -23,9 +23,18 @@ public function __construct( ) { } - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { - [$output, $itemStatements] = $this->itemTransformer->transform($input, $target, $propertyMapping, $uniqueVariableScope); + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + + $source = new Expr\Variable('value'); + } else { + /** @var Expr\Variable $source */ + $source = func_get_arg(4); + } + + [$output, $itemStatements] = $this->itemTransformer->transform($input, $target, $propertyMapping, $uniqueVariableScope, $source); $newOutput = null; $statements = []; diff --git a/src/Transformer/ObjectTransformer.php b/src/Transformer/ObjectTransformer.php index 9210d1a1..f877e54c 100644 --- a/src/Transformer/ObjectTransformer.php +++ b/src/Transformer/ObjectTransformer.php @@ -26,8 +26,12 @@ public function __construct( ) { } - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + $mapperName = $this->getDependencyName(); /* diff --git a/src/Transformer/SourceEnumTransformer.php b/src/Transformer/SourceEnumTransformer.php index 69828824..5647854b 100644 --- a/src/Transformer/SourceEnumTransformer.php +++ b/src/Transformer/SourceEnumTransformer.php @@ -15,8 +15,12 @@ */ final class SourceEnumTransformer implements TransformerInterface { - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + /* $input->value */ return [new Expr\PropertyFetch($input, 'value'), []]; } diff --git a/src/Transformer/StringToDateTimeTransformer.php b/src/Transformer/StringToDateTimeTransformer.php index da27fdff..98d1b955 100644 --- a/src/Transformer/StringToDateTimeTransformer.php +++ b/src/Transformer/StringToDateTimeTransformer.php @@ -25,8 +25,12 @@ public function __construct( ) { } - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + $className = \DateTimeInterface::class === $this->className ? \DateTimeImmutable::class : $this->className; /* diff --git a/src/Transformer/StringToSymfonyUidTransformer.php b/src/Transformer/StringToSymfonyUidTransformer.php index 2e72e0a0..2ffc8393 100644 --- a/src/Transformer/StringToSymfonyUidTransformer.php +++ b/src/Transformer/StringToSymfonyUidTransformer.php @@ -22,8 +22,12 @@ public function __construct( ) { } - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + /* * Create a Symfony Uid object from a string. * diff --git a/src/Transformer/SymfonyUidCopyTransformer.php b/src/Transformer/SymfonyUidCopyTransformer.php index 39b0a9a9..e83dcbd2 100644 --- a/src/Transformer/SymfonyUidCopyTransformer.php +++ b/src/Transformer/SymfonyUidCopyTransformer.php @@ -19,8 +19,12 @@ */ final class SymfonyUidCopyTransformer implements TransformerInterface { - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + /* * Create a Symfony Uid object from another Symfony Uid object. * diff --git a/src/Transformer/SymfonyUidToStringTransformer.php b/src/Transformer/SymfonyUidToStringTransformer.php index 70a0f30e..fc5a7f6c 100644 --- a/src/Transformer/SymfonyUidToStringTransformer.php +++ b/src/Transformer/SymfonyUidToStringTransformer.php @@ -20,8 +20,12 @@ public function __construct( ) { } - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + /* * Create a string from a Symfony Uid object. * diff --git a/src/Transformer/TargetEnumTransformer.php b/src/Transformer/TargetEnumTransformer.php index d3be03ed..909a235a 100644 --- a/src/Transformer/TargetEnumTransformer.php +++ b/src/Transformer/TargetEnumTransformer.php @@ -22,8 +22,12 @@ public function __construct( ) { } - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array { + if (\func_num_args() < 5) { + trigger_deprecation('jolicode/automapper', '8.2', 'The "%s()" method will have a new "Expr\Variable $source" argument in version 9.0, not defining it is deprecated.', __METHOD__); + } + /* * Transform a string into a BackendEnum. * diff --git a/src/Transformer/TransformerInterface.php b/src/Transformer/TransformerInterface.php index f6595b39..c6a09337 100644 --- a/src/Transformer/TransformerInterface.php +++ b/src/Transformer/TransformerInterface.php @@ -13,6 +13,8 @@ * Transformer tell how to transform a property mapping. * * @author Joel Wurtz + * + * @method array{0: Expr, 1: Stmt[]} transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source) */ interface TransformerInterface { @@ -21,5 +23,5 @@ interface TransformerInterface * * @return array{0: Expr, 1: Stmt[]} First value is the output expression, second value is an array of stmt needed to get the output */ - public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope): array; + public function transform(Expr $input, Expr $target, PropertyMapping $propertyMapping, UniqueVariableScope $uniqueVariableScope, /* Expr\Variable $source */): array; } diff --git a/src/Transformer/TransformerPropertyFactoryInterface.php b/src/Transformer/TransformerPropertyFactoryInterface.php new file mode 100644 index 00000000..f6fd1bbf --- /dev/null +++ b/src/Transformer/TransformerPropertyFactoryInterface.php @@ -0,0 +1,24 @@ + + */ +interface TransformerPropertyFactoryInterface +{ + /** + * Get transformer to use when mapping from an array of type to another array of type. + * + * @param Type[]|null $sourceTypes + * @param Type[]|null $targetTypes + */ + public function getPropertyTransformer(?array $sourceTypes, ?array $targetTypes, MapperMetadataInterface $mapperMetadata, string $property): ?TransformerInterface; +} diff --git a/tests/AutoMapperBaseTest.php b/tests/AutoMapperBaseTest.php index f878e529..ae3a809d 100644 --- a/tests/AutoMapperBaseTest.php +++ b/tests/AutoMapperBaseTest.php @@ -5,8 +5,6 @@ namespace AutoMapper\Tests; use AutoMapper\AutoMapper; -use AutoMapper\Extractor\ClassMethodToCallbackExtractor; -use AutoMapper\Extractor\CustomTransformerExtractor; use AutoMapper\Generator\MapperGenerator; use AutoMapper\Generator\Shared\ClassDiscriminatorResolver; use AutoMapper\Loader\ClassLoaderInterface; @@ -38,7 +36,6 @@ protected function buildAutoMapper(bool $allowReadOnlyTargetToPopulate = false, $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); $this->loader = new FileLoader(new MapperGenerator( - new CustomTransformerExtractor(new ClassMethodToCallbackExtractor()), new ClassDiscriminatorResolver(new ClassDiscriminatorFromClassMetadata($classMetadataFactory)), $allowReadOnlyTargetToPopulate ), __DIR__ . '/cache'); diff --git a/tests/AutoMapperWithCustomTransformerTest.php b/tests/AutoMapperWithCustomTransformerTest.php index 51dc380b..334b1cba 100644 --- a/tests/AutoMapperWithCustomTransformerTest.php +++ b/tests/AutoMapperWithCustomTransformerTest.php @@ -8,6 +8,8 @@ use AutoMapper\Tests\Fixtures\AddressDTO; use AutoMapper\Tests\Fixtures\BirthDateDateTime; use AutoMapper\Tests\Fixtures\BirthDateExploded; +use AutoMapper\Tests\Fixtures\CityFoo; +use AutoMapper\Tests\Fixtures\Transformer\CustomTransformer\FooDependency; use AutoMapper\Tests\Fixtures\Transformer\CustomTransformer\FromSourceCustomModelTransformer; use AutoMapper\Tests\Fixtures\Transformer\CustomTransformer\FromSourceCustomPropertyTransformer; use AutoMapper\Tests\Fixtures\Transformer\CustomTransformer\FromTargetCustomModelTransformer; @@ -16,6 +18,7 @@ use AutoMapper\Tests\Fixtures\Transformer\CustomTransformer\SourceTargetCustomModelTransformer; use AutoMapper\Tests\Fixtures\Transformer\CustomTransformer\SourceTargetCustomPropertyTransformer; use AutoMapper\Tests\Fixtures\Transformer\CustomTransformer\SourceTargetMultiFieldsCustomPropertyTransformer; +use AutoMapper\Tests\Fixtures\Transformer\CustomTransformer\TransformerWithDependency; use AutoMapper\Tests\Fixtures\User; use AutoMapper\Tests\Fixtures\UserDTO; @@ -115,6 +118,21 @@ public function testFromSourceToTargetMultipleFieldsTransformation(): void self::assertSame('1985-07-01', $birthDateDateTime->date->format('Y-m-d')); } + public function testCustomTransformerWithDependency(): void + { + $this->buildAutoMapper(mapPrivatePropertiesAndMethod: true, classPrefix: 'TransformerWithDependency'); + + $this->autoMapper->bindCustomTransformer(new TransformerWithDependency(new FooDependency())); + + $source = new CityFoo(); + $source->name = 'foo'; + + self::assertSame(['name' => 'bar'], $this->autoMapper->map( + $source, + 'array' + )); + } + private static function createUserDTO(?string $name = null, ?string $city = null): UserDTO { $user = new UserDTO(); diff --git a/tests/Extractor/FromSourceMappingExtractorTest.php b/tests/Extractor/FromSourceMappingExtractorTest.php index dc4c2c0c..3b274018 100644 --- a/tests/Extractor/FromSourceMappingExtractorTest.php +++ b/tests/Extractor/FromSourceMappingExtractorTest.php @@ -13,7 +13,6 @@ use AutoMapper\Transformer\ArrayTransformerFactory; use AutoMapper\Transformer\BuiltinTransformerFactory; use AutoMapper\Transformer\ChainTransformerFactory; -use AutoMapper\Transformer\CustomTransformer\CustomTransformersRegistry; use AutoMapper\Transformer\DateTimeTransformerFactory; use AutoMapper\Transformer\MultipleTransformerFactory; use AutoMapper\Transformer\NullableTransformerFactory; @@ -63,7 +62,6 @@ private function fromSourceMappingExtractorBootstrap(bool $private = true): void $reflectionExtractor, $reflectionExtractor, $transformerFactory, - new CustomTransformersRegistry(), $classMetadataFactory ); diff --git a/tests/Extractor/FromTargetMappingExtractorTest.php b/tests/Extractor/FromTargetMappingExtractorTest.php index 469c949c..1295b3c9 100644 --- a/tests/Extractor/FromTargetMappingExtractorTest.php +++ b/tests/Extractor/FromTargetMappingExtractorTest.php @@ -12,7 +12,6 @@ use AutoMapper\Transformer\ArrayTransformerFactory; use AutoMapper\Transformer\BuiltinTransformerFactory; use AutoMapper\Transformer\ChainTransformerFactory; -use AutoMapper\Transformer\CustomTransformer\CustomTransformersRegistry; use AutoMapper\Transformer\DateTimeTransformerFactory; use AutoMapper\Transformer\MultipleTransformerFactory; use AutoMapper\Transformer\NullableTransformerFactory; @@ -63,7 +62,6 @@ private function fromTargetMappingExtractorBootstrap(bool $private = true): void $reflectionExtractor, $reflectionExtractor, $transformerFactory, - new CustomTransformersRegistry(), $classMetadataFactory ); diff --git a/tests/Fixtures/IssueTargetToPopulate/VatEntity.php b/tests/Fixtures/IssueTargetToPopulate/VatEntity.php index be2412bc..e2040620 100644 --- a/tests/Fixtures/IssueTargetToPopulate/VatEntity.php +++ b/tests/Fixtures/IssueTargetToPopulate/VatEntity.php @@ -41,7 +41,7 @@ public function __construct( string $stateCode = null, float $standardVatRate = 0, float $reducedVatRate = 0, - bool $displayIncVatPrices = false + bool $displayIncVatPrices = false, ) { $this->countryCode = $countryCode; $this->stateCode = $stateCode; diff --git a/tests/Fixtures/Transformer/CustomTransformer/FooDependency.php b/tests/Fixtures/Transformer/CustomTransformer/FooDependency.php new file mode 100644 index 00000000..1216701a --- /dev/null +++ b/tests/Fixtures/Transformer/CustomTransformer/FooDependency.php @@ -0,0 +1,13 @@ +fooDependency->getBar(); + } +} diff --git a/tests/MapperGeneratorMetadataFactoryTest.php b/tests/MapperGeneratorMetadataFactoryTest.php index a281cc35..abfdb388 100644 --- a/tests/MapperGeneratorMetadataFactoryTest.php +++ b/tests/MapperGeneratorMetadataFactoryTest.php @@ -13,7 +13,6 @@ use AutoMapper\Transformer\ArrayTransformerFactory; use AutoMapper\Transformer\BuiltinTransformerFactory; use AutoMapper\Transformer\ChainTransformerFactory; -use AutoMapper\Transformer\CustomTransformer\CustomTransformersRegistry; use AutoMapper\Transformer\DateTimeTransformerFactory; use AutoMapper\Transformer\MultipleTransformerFactory; use AutoMapper\Transformer\NullableTransformerFactory; @@ -37,7 +36,6 @@ protected function setUp(): void parent::setUp(); $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); - $customTransformerRegistry = new CustomTransformersRegistry(); $reflectionExtractor = new ReflectionExtractor(null, null, null, true, ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PROTECTED | ReflectionExtractor::ALLOW_PRIVATE); $phpStanExtractor = new PhpStanExtractor(); @@ -54,7 +52,6 @@ protected function setUp(): void $reflectionExtractor, $reflectionExtractor, $transformerFactory, - $customTransformerRegistry, $classMetadataFactory ); @@ -63,7 +60,6 @@ protected function setUp(): void $reflectionExtractor, $reflectionExtractor, $transformerFactory, - $customTransformerRegistry, $classMetadataFactory ); @@ -72,7 +68,6 @@ protected function setUp(): void $reflectionExtractor, $reflectionExtractor, $transformerFactory, - $customTransformerRegistry, $classMetadataFactory ); diff --git a/tools/php-cs-fixer/.php-cs-fixer.php b/tools/php-cs-fixer/.php-cs-fixer.php index 97cb9830..4ac8757d 100644 --- a/tools/php-cs-fixer/.php-cs-fixer.php +++ b/tools/php-cs-fixer/.php-cs-fixer.php @@ -20,6 +20,8 @@ ], 'nullable_type_declaration_for_default_null_value' => false, 'declare_strict_types' => true, + 'no_trailing_comma_in_singleline' => false, + 'function_declaration' => ['trailing_comma_single_line' => true], ]) ->setFinder($finder) - ; +; diff --git a/tools/phpstan/phpstan-baseline.neon b/tools/phpstan/phpstan-baseline.neon index e9a14b71..60a53044 100644 --- a/tools/phpstan/phpstan-baseline.neon +++ b/tools/phpstan/phpstan-baseline.neon @@ -1,15 +1,5 @@ parameters: ignoreErrors: - - - message: "#^Call to an undefined method object\\:\\:addCallback\\(\\)\\.$#" - count: 1 - path: ../../src/AutoMapper.php - - - - message: "#^Call to an undefined method object\\:\\:injectMappers\\(\\)\\.$#" - count: 1 - path: ../../src/AutoMapper.php - - message: "#^Cannot access offset string on AutoMapper\\\\MapperGeneratorMetadataInterface\\.$#" count: 2 @@ -20,11 +10,6 @@ parameters: count: 2 path: ../../src/AutoMapper.php - - - message: "#^Method AutoMapper\\\\AutoMapper\\:\\:getMapper\\(\\) should return AutoMapper\\\\MapperInterface but returns object\\.$#" - count: 1 - path: ../../src/AutoMapper.php - - message: "#^Method AutoMapper\\\\AutoMapper\\:\\:map\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 @@ -60,11 +45,6 @@ parameters: count: 1 path: ../../src/AutoMapper.php - - - message: "#^Property AutoMapper\\\\AutoMapper\\:\\:\\$mapperRegistry \\(array\\\\) does not accept array\\\\.$#" - count: 1 - path: ../../src/AutoMapper.php - - message: "#^Method AutoMapper\\\\AutoMapperInterface\\:\\:map\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 @@ -255,26 +235,6 @@ parameters: count: 1 path: ../../src/Generator/CreateTargetStatementsGenerator.php - - - message: "#^Cannot access property \\$readAccessor on AutoMapper\\\\Extractor\\\\PropertyMapping\\|null\\.$#" - count: 1 - path: ../../src/Generator/Shared/DiscriminatorStatementsGenerator.php - - - - message: "#^Cannot access property \\$transformer on AutoMapper\\\\Extractor\\\\PropertyMapping\\|null\\.$#" - count: 1 - path: ../../src/Generator/Shared/DiscriminatorStatementsGenerator.php - - - - message: "#^Cannot call method getExpression\\(\\) on AutoMapper\\\\Extractor\\\\ReadAccessor\\|null\\.$#" - count: 1 - path: ../../src/Generator/Shared/DiscriminatorStatementsGenerator.php - - - - message: "#^Cannot call method transform\\(\\) on AutoMapper\\\\Transformer\\\\TransformerInterface\\|class\\-string\\\\.$#" - count: 1 - path: ../../src/Generator/Shared/DiscriminatorStatementsGenerator.php - - message: "#^Method AutoMapper\\\\Loader\\\\FileLoader\\:\\:addHashToRegistry\\(\\) has parameter \\$className with no type specified\\.$#" count: 1