├── .github ├── scripts │ └── build-documentation.sh └── workflows │ ├── ci.yml │ └── documentation.yml ├── .php-cs-fixer.php ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bench ├── README.md ├── composer.json ├── composer.lock └── src │ ├── CreateObjectBench.php │ └── IsInitializedBench.php ├── castor.composer.json ├── castor.composer.lock ├── castor.php ├── composer.json ├── docs ├── _nav.md ├── assets │ ├── favicon.png │ ├── javascripts │ │ └── highlight.min.js │ └── stylesheets │ │ └── highlight-github.min.css ├── bundle │ ├── api-platform.md │ ├── cache-warmup.md │ ├── configuration.md │ ├── debugging.md │ ├── expression-language.md │ ├── index.md │ ├── installation.md │ └── migrate.md ├── contributing.md ├── getting-started │ ├── cache.md │ ├── configuration.md │ ├── context.md │ ├── index.md │ └── source-and-target.md ├── images │ ├── debug-cli.png │ ├── debug-profiler-1.png │ └── debug-profiler-2.png ├── index.md ├── mapping │ ├── attributes.md │ ├── conditional-mapping.md │ ├── date-time.md │ ├── groups.md │ ├── ignoring-properties.md │ ├── index.md │ ├── inheritance.md │ ├── map-collection.md │ ├── mapper-attribute.md │ ├── provider.md │ ├── serializer.md │ └── transformer.md ├── overrides │ └── main.html └── upgrading-9.0.md ├── mkdocs.yaml ├── phpstan-baseline.neon ├── phpstan.neon ├── poetry.lock ├── pyproject.toml ├── src ├── Attribute │ ├── MapFrom.php │ ├── MapProvider.php │ ├── MapTo.php │ ├── MapToContext.php │ └── Mapper.php ├── AutoMapper.php ├── AutoMapperInterface.php ├── AutoMapperRegistryInterface.php ├── Configuration.php ├── ConstructorStrategy.php ├── Event │ ├── GenerateMapperEvent.php │ ├── PropertyMetadataEvent.php │ ├── SourcePropertyMetadata.php │ └── TargetPropertyMetadata.php ├── EventListener │ ├── ApiPlatform │ │ └── JsonLdListener.php │ ├── MapFromListener.php │ ├── MapListener.php │ ├── MapProviderListener.php │ ├── MapToContextListener.php │ ├── MapToListener.php │ ├── MapperListener.php │ └── Symfony │ │ ├── AdvancedNameConverterListener.php │ │ ├── ClassDiscriminatorListener.php │ │ ├── SerializerGroupListener.php │ │ ├── SerializerIgnoreListener.php │ │ └── SerializerMaxDepthListener.php ├── Exception │ ├── AutoMapperException.php │ ├── BadMapDefinitionException.php │ ├── CannotCreateTargetException.php │ ├── CircularReferenceException.php │ ├── CompileException.php │ ├── InvalidArgumentException.php │ ├── InvalidMappingException.php │ ├── LogicException.php │ ├── MetadataException.php │ ├── MissingConstructorArgumentsException.php │ ├── ReadOnlyTargetException.php │ └── RuntimeException.php ├── Extractor │ ├── FromSourceMappingExtractor.php │ ├── FromTargetMappingExtractor.php │ ├── GetTypeTrait.php │ ├── MappingExtractor.php │ ├── MappingExtractorInterface.php │ ├── ReadAccessor.php │ ├── ReadWriteTypeExtractor.php │ ├── SourceTargetMappingExtractor.php │ └── WriteMutator.php ├── GeneratedMapper.php ├── Generator │ ├── CreateTargetStatementsGenerator.php │ ├── InjectMapperMethodStatementsGenerator.php │ ├── MapMethodStatementsGenerator.php │ ├── MapperConstructorGenerator.php │ ├── MapperGenerator.php │ ├── PropertyConditionsGenerator.php │ ├── PropertyStatementsGenerator.php │ ├── Shared │ │ ├── CachedReflectionStatementsGenerator.php │ │ ├── ClassDiscriminatorResolver.php │ │ └── DiscriminatorStatementsGenerator.php │ ├── UniqueVariableScope.php │ └── VariableRegistry.php ├── Loader │ ├── ClassLoaderInterface.php │ ├── EvalLoader.php │ ├── FileLoader.php │ └── FileReloadStrategy.php ├── MapperContext.php ├── MapperInterface.php ├── Metadata │ ├── Dependency.php │ ├── GeneratorMetadata.php │ ├── MapperMetadata.php │ ├── MetadataFactory.php │ ├── MetadataRegistry.php │ ├── PropertyMetadata.php │ ├── SourcePropertyMetadata.php │ ├── TargetPropertyMetadata.php │ └── TypesMatching.php ├── Normalizer │ └── AutoMapperNormalizer.php ├── Provider │ ├── ApiPlatform │ │ └── IriProvider.php │ ├── EarlyReturn.php │ ├── ProviderInterface.php │ └── ProviderRegistry.php ├── Symfony │ ├── Attribute │ │ └── AsAutoMapperExpressionService.php │ ├── Bundle │ │ ├── AutoMapperBundle.php │ │ ├── CacheWarmup │ │ │ └── CacheWarmer.php │ │ ├── Command │ │ │ └── DebugMapperCommand.php │ │ ├── DataCollector │ │ │ └── MetadataCollector.php │ │ ├── DependencyInjection │ │ │ ├── AutoMapperExtension.php │ │ │ ├── Compiler │ │ │ │ └── TransformerFactoryPass.php │ │ │ └── Configuration.php │ │ ├── ReflectionClassRecursiveIterator.php │ │ └── Resources │ │ │ ├── config │ │ │ ├── api_platform.php │ │ │ ├── automapper.php │ │ │ ├── custom_transformers.php │ │ │ ├── event.php │ │ │ ├── event_serializer.php │ │ │ ├── expression_language.php │ │ │ ├── generator.php │ │ │ ├── metadata.php │ │ │ ├── normalizer.php │ │ │ ├── property_info.php │ │ │ ├── provider.php │ │ │ ├── symfony.php │ │ │ └── transformers.php │ │ │ └── views │ │ │ └── DataCollector │ │ │ ├── icon.svg │ │ │ └── metadata.html.twig │ └── ExpressionLanguageProvider.php ├── Transformer │ ├── AbstractArrayTransformer.php │ ├── AbstractUniqueTypeTransformerFactory.php │ ├── AllowNullValueTransformerInterface.php │ ├── ApiPlatform │ │ ├── JsonLdContextTransformer.php │ │ └── JsonLdIdTransformer.php │ ├── ArrayToDoctrineCollectionTransformer.php │ ├── ArrayTransformer.php │ ├── ArrayTransformerFactory.php │ ├── AssignedByReferenceTransformerInterface.php │ ├── BuiltinTransformer.php │ ├── BuiltinTransformerFactory.php │ ├── CallableTransformer.php │ ├── ChainTransformerFactory.php │ ├── ChainTransformerFactoryAwareInterface.php │ ├── ChainTransformerFactoryAwareTrait.php │ ├── CheckTypeInterface.php │ ├── CopyEnumTransformer.php │ ├── CopyTransformer.php │ ├── CopyTransformerFactory.php │ ├── DateTimeInterfaceToImmutableTransformer.php │ ├── DateTimeInterfaceToMutableTransformer.php │ ├── DateTimeToStringTransformer.php │ ├── DateTimeTransformerFactory.php │ ├── DependentTransformerInterface.php │ ├── DictionaryTransformer.php │ ├── DoctrineCollectionTransformerFactory.php │ ├── EnumTransformerFactory.php │ ├── ExpressionLanguageTransformer.php │ ├── FixedValueTransformer.php │ ├── MapperDependency.php │ ├── MultipleTransformer.php │ ├── MultipleTransformerFactory.php │ ├── NullableTransformer.php │ ├── NullableTransformerFactory.php │ ├── ObjectTransformer.php │ ├── ObjectTransformerFactory.php │ ├── PrioritizedTransformerFactoryInterface.php │ ├── PropertyTransformer │ │ ├── PrioritizedPropertyTransformerInterface.php │ │ ├── PropertyTransformer.php │ │ ├── PropertyTransformerFactory.php │ │ ├── PropertyTransformerInterface.php │ │ ├── PropertyTransformerRegistry.php │ │ └── PropertyTransformerSupportInterface.php │ ├── SourceEnumTransformer.php │ ├── StringToDateTimeTransformer.php │ ├── StringToSymfonyUidTransformer.php │ ├── SymfonyUidCopyTransformer.php │ ├── SymfonyUidToStringTransformer.php │ ├── SymfonyUidTransformerFactory.php │ ├── TargetEnumTransformer.php │ ├── TransformerFactoryInterface.php │ ├── TransformerInterface.php │ ├── UniqueTypeTransformerFactory.php │ └── VoidTransformer.php └── php-parser.php └── tests ├── AutoMapperBuilder.php ├── AutoMapperMapToTest.php ├── AutoMapperTest.php ├── AutoMapperTest ├── ArrayAccess │ ├── expected.1.data │ ├── expected.data │ └── map.php ├── ArrayConsistency │ ├── expected.to.data │ ├── expected.toAdder.data │ ├── expected.toAdderCollection.data │ ├── expected.toCollection.data │ └── map.php ├── ArrayNested │ ├── expected.array.data │ └── map.php ├── BuiltinClass │ ├── expected.data │ └── map.php ├── CircularReferenceDeep │ ├── expected.data │ └── map.php ├── ConstructorPropertyPromoted │ ├── expected.data │ └── map.php ├── ConstructorWithRelation │ ├── expected.constructor-and-relation-missing.data │ ├── expected.constructor-arguments.data │ ├── expected.ok.data │ └── map.php ├── ConstructorWithSerializedName │ ├── expected.from_array.data │ ├── expected.to_array.data │ └── map.php ├── Covariance │ ├── expected.data │ └── map.php ├── DeepPopulateWithArrayCollection │ ├── expected.array.data │ ├── expected.collection.data │ └── map.php ├── DifferentSetterGetterType │ ├── expected.data │ └── map.php ├── DiscriminatorMapAndInterface │ ├── expected.to-array.data │ ├── expected.to-class.data │ └── map.php ├── DiscriminatorMapBadConfiguration │ ├── expected.data │ └── map.php ├── DiscriminatorPopulate │ ├── expected.data │ └── map.php ├── DoctrineCollections │ ├── expected.from-array.data │ ├── expected.to-array.data │ └── map.php ├── GroupOverride │ ├── expected.data │ └── map.php ├── Ignore │ ├── expected.ignore-in-source.data │ ├── expected.ignore-in-target.data │ └── map.php ├── Issue111 │ ├── expected.data │ └── map.php ├── Issue425 │ ├── expected.data │ └── map.php ├── IssueParamDocBlock │ ├── expected.data │ └── map.php ├── MaxDepth │ ├── expected.data │ └── map.php ├── NoProperties │ ├── expected.data │ └── map.php ├── NoTypes │ ├── expected.data │ └── map.php ├── ObjectWithPropertyAsUnknownArray │ ├── expected.object-to-object-with-property-as-unknown-array.data │ ├── expected.object-with-property-as-unknown-array-to-object.data │ └── map.php ├── Paramters │ ├── expected.int.data │ ├── expected.mixed.data │ ├── expected.string.data │ └── map.php ├── Private │ ├── expected.data │ └── map.php ├── PrivatePropertyInConstructors │ ├── expected.from-array.data │ ├── expected.from-class.data │ └── map.php ├── Provider │ ├── expected.bar-foo.data │ ├── expected.bar.data │ ├── expected.early-return.data │ └── map.php ├── README.md ├── SkipNullValues │ ├── expected.data │ └── map.php ├── StdObject │ ├── expected.data │ └── map.php ├── SymfonyUId │ ├── expected.array-to-object-v1.data │ ├── expected.array-to-object-v3.data │ ├── expected.array-to-object-v4.data │ ├── expected.array-to-object.data │ ├── expected.object-to-array.data │ ├── expected.object-to-object.data │ └── map.php ├── TargetToPopulate │ ├── expected.data │ └── map.php ├── UninitializedProperties │ ├── expected.data │ └── map.php └── UnionProperty │ ├── expected.bar.data │ ├── expected.foo.data │ └── map.php ├── AutoMapperTestCase.php ├── AutoMapperWithCustomTransformerTest.php ├── Bundle ├── ApiPlatformTest.php ├── DependencyInjection │ └── AutoMapperExtensionTest.php ├── NormalizerTest.php ├── Resources │ ├── App │ │ ├── Api │ │ │ ├── Entity │ │ │ │ ├── Book.php │ │ │ │ └── Review.php │ │ │ ├── Processor │ │ │ │ └── BookProcessor.php │ │ │ └── Provider │ │ │ │ └── BookProvider.php │ │ ├── AppKernel.php │ │ ├── Controller │ │ │ └── HomeController.php │ │ ├── Entity │ │ │ ├── Address.php │ │ │ ├── AddressDTO.php │ │ │ ├── BaseUser.php │ │ │ ├── Cat.php │ │ │ ├── ClassWithMapToContextAttribute.php │ │ │ ├── ClassWithPrivateProperty.php │ │ │ ├── DTOWithEnum.php │ │ │ ├── Dog.php │ │ │ ├── FooMapTo.php │ │ │ ├── NestedObject.php │ │ │ ├── Order.php │ │ │ ├── Pet.php │ │ │ ├── SomeEnum.php │ │ │ ├── User.php │ │ │ └── UserDTO.php │ │ ├── Normalizer │ │ │ ├── AddressDTONormalizer.php │ │ │ ├── OrderNormalizer.php │ │ │ ├── UserDTODenormalizer.php │ │ │ └── UserNormalizer.php │ │ └── Service │ │ │ ├── CatProvider.php │ │ │ ├── FooService.php │ │ │ ├── IdNameConverter.php │ │ │ └── YearOfBirthTransformer.php │ ├── bin │ │ └── console │ ├── config │ │ ├── bundles.php │ │ ├── packages │ │ │ ├── api_platform.yaml │ │ │ ├── automapper.yaml │ │ │ ├── dev │ │ │ │ └── web_profiler.yaml │ │ │ └── framework.yaml │ │ ├── routes │ │ │ ├── dev │ │ │ │ └── profiler.yaml │ │ │ └── routes.yaml │ │ ├── services.yaml │ │ └── with-private-properties.yml │ └── public │ │ ├── dev.php │ │ └── index.php └── ServiceInstantiationTest.php ├── Extractor ├── Fixtures │ ├── Foo.php │ └── FooCustomMapper.php ├── FromSourceMappingExtractorTest.php └── FromTargetMappingExtractorTest.php ├── Fixtures ├── Address.php ├── AddressBar.php ├── AddressDTO.php ├── AddressDTOReadonlyClass.php ├── AddressDTOSecondReadonlyClass.php ├── AddressDTOWithReadonly.php ├── AddressDTOWithReadonlyPromotedProperty.php ├── AddressFoo.php ├── AddressNotWritable.php ├── AddressType.php ├── AddressWithEnum.php ├── Bar.php ├── BarGenerator.php ├── BirthDateDateTime.php ├── BirthDateExploded.php ├── Cat.php ├── CityFoo.php ├── ClassWithMapToContextAttribute.php ├── ClassWithNullablePropertyInConstructor.php ├── ClassWithPrivateProperty.php ├── ConstructorWithDefaultValues.php ├── ConstructorWithDefaultValuesAsObjects.php ├── Dog.php ├── Empty_.php ├── Fish.php ├── Foo.php ├── FooGenerator.php ├── GroupOverride.php ├── HasDateTime.php ├── HasDateTimeImmutable.php ├── HasDateTimeImmutableWithNullValue.php ├── HasDateTimeInterfaceWithImmutableInstance.php ├── HasDateTimeInterfaceWithMutableInstance.php ├── HasDateTimeInterfaceWithNullValue.php ├── HasDateTimeWithNullValue.php ├── IntDTO.php ├── IntDTOWithMapper.php ├── MapTo │ ├── BadMapToTransformer.php │ ├── Bar.php │ ├── DateTimeFormatMapTo.php │ ├── FooMapTo.php │ ├── MapperDateTimeFormatMapTo.php │ └── PriorityMapTo.php ├── Node.php ├── Normalizer │ ├── ChildOfGroupsAnnotationDummy.php │ ├── DeepObjectPopulateChildDummy.php │ ├── DeepObjectPopulateParentDummy.php │ ├── GroupDummy.php │ ├── GroupDummyInterface.php │ ├── GroupDummyParent.php │ ├── MaxDepthDummy.php │ ├── ProxyDummy.php │ └── ToBeProxyfiedDummy.php ├── ObjectWithDateTime.php ├── Order.php ├── Pet.php ├── PetOwner.php ├── PetOwnerWithConstructorArguments.php ├── Private_.php ├── Proxy.php ├── ReflectionExtractorTestFixture.php ├── SourceForConstructorWithDefaultValues.php ├── Transformer │ ├── ArrayToMoneyTransformer.php │ ├── CustomTransformer │ │ ├── FooDependency.php │ │ ├── FromSourceCustomModelTransformer.php │ │ ├── FromSourceCustomPropertyTransformer.php │ │ ├── FromTargetCustomModelTransformer.php │ │ ├── FromTargetCustomPropertyTransformer.php │ │ ├── PrioritizedFromSourcePropertyPriorityTransformer.php │ │ ├── SourceTargetCustomModelTransformer.php │ │ ├── SourceTargetCustomPropertyTransformer.php │ │ ├── SourceTargetMultiFieldsCustomPropertyTransformer.php │ │ └── TransformerWithDependency.php │ ├── MoneyToArrayTransformer.php │ ├── MoneyToMoneyTransformer.php │ └── MoneyTransformerFactory.php ├── Uninitialized.php ├── User.php ├── UserConstructorDTO.php ├── UserDTO.php ├── UserDTOMerged.php ├── UserDTONoAge.php ├── UserDTONoName.php ├── UserDTOProperties.php ├── UserPartialConstructor.php ├── UserWithYearOfBirth.php ├── WrongParameters.php └── proxies.php ├── Generator └── UniqueVariableScopeTest.php ├── MapperContextTest.php ├── Metadata └── MetadataFactoryTest.php ├── Normalizer ├── AutoMapperNormalizerTest.php └── Features │ ├── AttributesTestTrait.php │ ├── CallbacksObject.php │ ├── CallbacksTestTrait.php │ ├── CircularReferenceDummy.php │ ├── CircularReferenceTestTrait.php │ ├── ConstructorArgumentsObject.php │ ├── ConstructorArgumentsTestTrait.php │ ├── ContextMetadataTestTrait.php │ ├── DummyContextChild.php │ ├── GroupsTestTrait.php │ ├── IgnoredAttributesTestTrait.php │ ├── MaxDepthTestTrait.php │ ├── ObjectDummy.php │ ├── ObjectDummyWithContextAttribute.php │ ├── ObjectInner.php │ ├── ObjectOuter.php │ ├── ObjectToPopulateTestTrait.php │ ├── SkipNullValuesTestTrait.php │ ├── SkipUninitializedValuesTestTrait.php │ ├── TypeEnforcementNumberObject.php │ ├── TypeEnforcementTestTrait.php │ ├── TypedPropertiesObject.php │ └── TypedPropertiesObjectWithGetters.php └── Transformer ├── ArrayTransformerFactoryTest.php ├── ArrayTransformerTest.php ├── BuiltinTransformerFactoryTest.php ├── BuiltinTransformerTest.php ├── ChainTransformerFactoryTest.php ├── CopyTransformerTest.php ├── DateTimeInterfaceToImmutableTransformerTest.php ├── DateTimeInterfaceToMutableTransformerTest.php ├── DateTimeToStringTransformerTest.php ├── DateTimeTransformerFactoryTest.php ├── EvalTransformerTrait.php ├── MultipleTransformerFactoryTest.php ├── MultipleTransformerTest.php ├── NullableTransformerFactoryTest.php ├── NullableTransformerTest.php ├── ObjectTransformerFactoryTest.php ├── ObjectTransformerTest.php ├── StringToDateTimeTransformerTest.php ├── SymfonyUidTransformerFactoryTest.php └── UniqueTypeTransformerFactoryTest.php /.github/scripts/build-documentation.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # preparing fake remote 4 | mkdir .build 5 | git config user.name ci-bot 6 | git config user.email ci-bot@example.org 7 | git remote add gh-pages ./.build 8 | cd ./.build 9 | git init 10 | git checkout -b gh-pages 11 | git config receive.denyCurrentBranch ignore 12 | cd - 13 | 14 | # build documentation for main branch 15 | poetry run mike deploy --push --remote gh-pages dev 16 | 17 | # build documentation for 8.x 18 | git checkout tags/8.2.2 19 | poetry run mike deploy --push --remote gh-pages --update-aliases 8.2.2 20 | 21 | # build documentation for 9.x 22 | git checkout tags/9.2.0 23 | poetry run mike deploy --push --remote gh-pages 9.2.0 latest 24 | poetry run mike set-default --push latest 25 | 26 | # clean fake remote 27 | cd ./.build 28 | git reset --hard gh-pages 29 | cd - 30 | -------------------------------------------------------------------------------- /.php-cs-fixer.php: -------------------------------------------------------------------------------- 1 | in(__DIR__ . '/src') 5 | ->in(__DIR__ . '/tests') 6 | ->append([__DIR__ . '/castor.php']) 7 | ->exclude(['cache', 'Bundle/Resources/var']) 8 | ; 9 | 10 | return (new PhpCsFixer\Config()) 11 | ->setRiskyAllowed(true) 12 | ->registerCustomFixers((new PhpCsFixerCustomFixers\Fixers())) 13 | ->setRules([ 14 | '@Symfony' => true, 15 | '@Symfony:risky' => true, 16 | 'array_syntax' => ['syntax' => 'short'], 17 | 'concat_space' => ['spacing' => 'one'], 18 | 'yoda_style' => false, 19 | 'native_constant_invocation' => false, 20 | 'no_superfluous_phpdoc_tags' => [ 21 | 'remove_inheritdoc' => false, 22 | ], 23 | 'declare_strict_types' => true, 24 | 'no_trailing_comma_in_singleline' => false, 25 | 'function_declaration' => ['trailing_comma_single_line' => true], 26 | 'phpdoc_to_comment' => ['allow_before_return_statement' => true], 27 | 'psr_autoloading' => false, // Does not work well with "map.php" files in tests 28 | PhpCsFixerCustomFixers\Fixer\MultilinePromotedPropertiesFixer::name() => true, 29 | ]) 30 | ->setFinder($finder) 31 | ; 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016-present Baptiste Leduc 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoMapper 2 | 3 | The AutoMapper solves a simple problem: removing all the code you need to map one object to another. A boring code to 4 | write and often replaced by less-performant alternatives like Symfony's Serializer. 5 | ## Quick Start 🚀 6 | 7 | ```shell 8 | composer require jolicode/automapper 9 | ``` 10 | 11 | You can read more about this library and how to use it on the [documentation](https://jolicode.github.io/automapper/). 12 | ## Support 13 | 14 | For support, please create an issue on [Github tracker](https://github.com/jolicode/automapper/issues) 15 | -------------------------------------------------------------------------------- /bench/README.md: -------------------------------------------------------------------------------- 1 | # Bench package 2 | 3 | This package benchmarks performance of some PHP operations that allow us to take decision on which one to use. 4 | -------------------------------------------------------------------------------- /bench/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "automapper/bench", 3 | "require": { 4 | "phpbench/phpbench": "^1.2" 5 | }, 6 | "autoload": { 7 | "psr-4": { 8 | "Automapper\\Bench\\": "src/" 9 | } 10 | }, 11 | "authors": [ 12 | { 13 | "name": "Joel Wurtz", 14 | "email": "jwurtz@jolicode.com" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /castor.composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "castor-php/php-qa": "0.1.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docs/_nav.md: -------------------------------------------------------------------------------- 1 | - [Home](index.md) 2 | - [Getting started](getting-started/index.md) 3 | - [Understanding the `source` and `target`](getting-started/source-and-target.md) 4 | - [Using the context](getting-started/context.md) 5 | - [Configuration](getting-started/configuration.md) 6 | - [Cache](getting-started/cache.md) 7 | - [Mapping](mapping/index.md) 8 | - [MapTo and MapFrom attributes](mapping/attributes.md) 9 | - [Symfony Serializer](mapping/serializer.md) 10 | - [Mapping Collections](mapping/map-collection.md) 11 | - [Ignoring properties](mapping/ignoring-properties.md) 12 | - [Conditional mapping](mapping/conditional-mapping.md) 13 | - [Groups](mapping/groups.md) 14 | - [Transformer](mapping/transformer.md) 15 | - [Provider](mapping/provider.md) 16 | - [Mapping inheritance](mapping/inheritance.md) 17 | - [DateTime format](mapping/date-time.md) 18 | - [Symfony Bundle](bundle/index.md) 19 | - [Installation](bundle/installation.md) 20 | - [Configuration](bundle/configuration.md) 21 | - [Cache Warmup](bundle/cache-warmup.md) 22 | - [Expression Language](bundle/expression-language.md) 23 | - [Api Platform](bundle/api-platform.md) 24 | - [Migrate existing application](bundle/migrate.md) 25 | - [Debugging](bundle/debugging.md) 26 | - [Upgrading to 9.0](upgrading-9.0.md) 27 | - [Contributing](contributing.md) 28 | -------------------------------------------------------------------------------- /docs/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolicode/automapper/26f9739cc05aa1bd7ade4ccc6117b01029762297/docs/assets/favicon.png -------------------------------------------------------------------------------- /docs/bundle/api-platform.md: -------------------------------------------------------------------------------- 1 | # Api Platform integration 2 | 3 | > [!WARNING] 4 | > The api platform integration is in a experimental state, and may change in the future. 5 | > Some behavior may not be handled correctly, and some features may not be implemented. 6 | > 7 | > If you find a bug or missing feature, please report it on the [issue tracker](https://github.com/jolicode/automapper/issues). 8 | 9 | This bundle provides a way to integrate with [Api Platform](https://api-platform.com/) by generating the mappers for you. 10 | 11 | It injects extra data in the mappers when we map a Resource class to or from an array. 12 | 13 | You have to enable the `api_platform` option in the configuration to use this feature. 14 | 15 | If you have custom normalizer with some logic inside you will have to convert this logic with our library way of doing things. 16 | [See our migrate guide](migrate.md) for more information. 17 | -------------------------------------------------------------------------------- /docs/bundle/debugging.md: -------------------------------------------------------------------------------- 1 | # Debug a Mapper 2 | 3 | AutoMapper provides 2 ways to debug what's going on with a mapper when using the Symfony bundle: 4 | 5 | ## The `debug:mapper` Command 6 | 7 | The `debug:mapper` command will display the mapping information for a specific mapper. 8 | This can be useful to understand how AutoMapper is mapping your objects and why some properties are not mapped. 9 | 10 | ```bash 11 | php bin/console debug:mapper User UserDTO 12 | ``` 13 | 14 | ![Profiler](../images/debug-cli.png) 15 | 16 | ## Using the symfony profiler 17 | 18 | AutoMapper provides a panel in the Symfony profiler that will display the mapping information for each request. 19 | Please note that this only display Mapper that has been generated during the request, if you have a mapper that was not 20 | generated during the request it will not be displayed. 21 | 22 | You can find the panel in the Symfony profiler under the `AutoMapper` tab. 23 | 24 | ![Profiler](../images/debug-profiler-1.png) 25 | ![Profiler](../images/debug-profiler-2.png) -------------------------------------------------------------------------------- /docs/bundle/index.md: -------------------------------------------------------------------------------- 1 | # Symfony Bundle 2 | 3 | To make Symfony's users life easier, we made a bundle that will make all DependencyInjection for you and offer extra 4 | features linked to Symfony way of doing things. 5 | 6 | - [Installation](installation.md) 7 | - [Configuration](configuration.md) 8 | - [Cache Warmup](cache-warmup.md) 9 | - [Expression Language](expression-language.md) 10 | - [Api Platform](api-platform.md) 11 | - [Migrate existing application](migrate.md) 12 | - [Debugging](debugging.md) 13 | -------------------------------------------------------------------------------- /docs/bundle/installation.md: -------------------------------------------------------------------------------- 1 | # Installing the Symfony Bundle 2 | 3 | The bundle is already available on the `jolicode/automapper` package, you don't need to add any packages to your composer.json file. 4 | 5 | ## Registering the bundle 6 | 7 | To use it, you have to register the main bundle class in your `config/bundles.php` file. 8 | 9 | ```php 10 | return [ 11 | // ... 12 | AutoMapper\Symfony\Bundle\AutoMapperBundle::class => ['all' => true], 13 | ]; 14 | ``` 15 | 16 | ## Usage 17 | 18 | Once the bundle is registered, you can use the `AutoMapperInterface` service to map your objects. 19 | 20 | ```php 21 | use AutoMapper\AutoMapperInterface; 22 | 23 | class MyController 24 | { 25 | public function __construct(private AutoMapperInterface $autoMapper) 26 | { 27 | } 28 | 29 | #[Route('/my-route', name: 'my_route')] 30 | public function index() 31 | { 32 | $source = new Source(); 33 | $target = $this->autoMapper->map($source, 'array'); 34 | 35 | return new JsonResponse($target); 36 | } 37 | } 38 | ``` -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Releasing 4 | 5 | Whenever you're doing a release you need to do some updates in order for the project to keep history on what was done 6 | and some other stuff. 7 | 8 | ### Changelog 9 | 10 | First you'll need to update the CHANGELOG file (`./CHANGELOG.md`), take everything under the `Unreleased` section and 11 | create a new section for your new tag with the today's date. 12 | 13 | ### Version 14 | 15 | When a new version is tagged, you have to update the version constants within the `AutoMapper/AutoMapper` so 16 | transformers can be updated with last AutoMapper version. 17 | -------------------------------------------------------------------------------- /docs/getting-started/cache.md: -------------------------------------------------------------------------------- 1 | # Cache 2 | 3 | AutoMapper can uses a cache system to store each Mapper generated for a specific `source` and `target` class. 4 | This way, the Mapper is generated only once and reused for each mapping. 5 | 6 | By default, it will evaluate the generated Mapper, new call inside the same request or cli command will not regenerate the Mapper. 7 | 8 | However, next request or cli command will regenerate the Mapper. 9 | 10 | To avoid regenerating the Mapper, you can use the `cacheDirectory` option in the `AutoMapper::create()` method. 11 | 12 | ```php 13 | use AutoMapper\AutoMapper; 14 | 15 | $autoMapper = AutoMapper::create(cacheDirectory: '/path/to/cache'); 16 | ``` 17 | 18 | This way, the Mapper will be stored in the `/path/to/cache` directory and reused for each mapping. However, if you change 19 | the `source` or `target` class, the Mapper will be regenerated. 20 | 21 | > [!WARNING] 22 | > Some changes may not be detected by the cache system, like changing a dependency used by the source or target class. 23 | > You may need to clean it manually. 24 | > 25 | > However, we try our best to detect those changes, if you encounter a problem, please open an issue on the GitHub repository. 26 | -------------------------------------------------------------------------------- /docs/getting-started/index.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | - [Understanding the `source` and `target`](source-and-target.md) 4 | - [Using the context](context.md) 5 | - [Configuration](configuration.md) 6 | - [Cache](cache.md) 7 | -------------------------------------------------------------------------------- /docs/images/debug-cli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolicode/automapper/26f9739cc05aa1bd7ade4ccc6117b01029762297/docs/images/debug-cli.png -------------------------------------------------------------------------------- /docs/images/debug-profiler-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolicode/automapper/26f9739cc05aa1bd7ade4ccc6117b01029762297/docs/images/debug-profiler-1.png -------------------------------------------------------------------------------- /docs/images/debug-profiler-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jolicode/automapper/26f9739cc05aa1bd7ade4ccc6117b01029762297/docs/images/debug-profiler-2.png -------------------------------------------------------------------------------- /docs/mapping/groups.md: -------------------------------------------------------------------------------- 1 | # Groups 2 | 3 | In addition to use the Symfony Serializer `#[Groups]` attribute, you can also use the `#[MapTo]` and `#[MapFrom]` 4 | attributes to define groups of properties that should be mapped. 5 | 6 | ```php 7 | class Source 8 | { 9 | #[MapTo(target: 'array', groups: ['group1', 'group2'])] 10 | public $groupedProperty; 11 | } 12 | ``` 13 | 14 | When doing so the property will be mapped only if the context contains at least one group defined in the `groups` argument. 15 | 16 | ### Cumulative groups 17 | 18 | When using both groups from the Symfony Serializer `#[Groups]` attribute and the `groups` argument from the `#[MapTo]` 19 | or `#[MapFrom]` attributes, the latter groups will override the former groups. 20 | 21 | ```php 22 | use Symfony\Component\Serializer\Attribute\Groups; 23 | 24 | class Source 25 | { 26 | #[Groups(['group1', 'group2'])] 27 | #[MapTo(target: 'array', groups: ['group3'])] 28 | public $groupedProperty; 29 | } 30 | ``` 31 | 32 | In this case the property will be mapped only if the context contains the `group3` group. 33 | -------------------------------------------------------------------------------- /docs/mapping/ignoring-properties.md: -------------------------------------------------------------------------------- 1 | # Ignoring properties 2 | 3 | Sometimes you may want to ignore a property during the mapping process. This can be done using the `#[MapTo]` or `#[MapFrom]` attributes 4 | with the `ignore` argument set to `true`. 5 | 6 | ```php 7 | class Source 8 | { 9 | #[MapTo(target: SourceDTO::class, ignore: true)] 10 | #[MapTo(target: 'array', ignore: false)] 11 | public $ignoredProperty; 12 | } 13 | ``` 14 | 15 | Setting `ignore` to `false` may be useful when used in conjunction with the `#[Ignore]` attribute from the Symfony Serializer. 16 | 17 | ```php 18 | use Symfony\Component\Serializer\Attribute\Ignore; 19 | 20 | class Source 21 | { 22 | #[Ignore] 23 | #[MapTo(target: SourceDTO::class, ignore: false)] 24 | public $ignoredProperty; 25 | } 26 | ``` 27 | 28 | In this case the property will be mapped to the `SourceDTO` class, but will be ignored when using the Symfony Serializer. 29 | -------------------------------------------------------------------------------- /docs/mapping/index.md: -------------------------------------------------------------------------------- 1 | # Mapping 2 | 3 | Despite doing its best to map objects automatically, AutoMapper provides a lot of ways to customize the mapping between 4 | a `source` and a `target`. 5 | 6 | - [MapTo and MapFrom attributes](attributes.md) 7 | - [Symfony Serializer attributes](serializer.md) 8 | - [Ignoring properties](ignoring-properties.md) 9 | - [Conditional mapping](conditional-mapping.md) 10 | - [Groups](groups.md) 11 | - [Transformer](transformer.md) 12 | - [Provider](provider.md) 13 | - [Mapping inheritance](inheritance.md) 14 | - [DateTime format](date-time.md) 15 | -------------------------------------------------------------------------------- /docs/mapping/inheritance.md: -------------------------------------------------------------------------------- 1 | # Inheritance Mapping 2 | 3 | A `source` or `target` class may inherit from another class. 4 | 5 | When creating the mapping, AutoMapper can determine the correct mapping by using the inheritance information from 6 | the Symfony Serializer `#[DiscriminatorMap]` attribute. 7 | 8 | ```php 9 | #[DiscriminatorMap(typeProperty: 'type', mapping: [ 10 | 'cat' => Cat::class, 11 | 'dog' => Dog::class, 12 | 'fish' => Fish::class, 13 | ])] 14 | abstract class Pet 15 | { 16 | /** @var string */ 17 | public $type; 18 | 19 | /** @var string */ 20 | public $name; 21 | 22 | /** @var PetOwner */ 23 | public $owner; 24 | } 25 | ``` 26 | 27 | When mapping a `Pet` object, AutoMapper will automatically determine the correct class to instantiate based on the `type` property. 28 | 29 | [Learn more about the Symfony Serializer inheritance mapping](https://symfony.com/doc/current/components/serializer.html#serializing-interfaces-and-abstract-classes) 30 | 31 | > [!NOTE] 32 | > If you don't use the Symfony Serializer we do not provide, yet, any way to determine the correct class to instantiate. 33 | -------------------------------------------------------------------------------- /docs/overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block htmltitle %} 4 | {% if page.meta and page.meta.title %} 5 | {{ page.meta.title }} - AutoMapper 6 | {% elif page.title and not page.is_homepage %} 7 | {{ page.title | striptags }} - AutoMapper 8 | {% else %} 9 | {{ config.site_name }} 10 | {% endif %} 11 | {% endblock %} -------------------------------------------------------------------------------- /docs/upgrading-9.0.md: -------------------------------------------------------------------------------- 1 | # Upgrading from 8.x to 9.0 2 | 3 | 9.0 is major release of AutoMapper. It brings a lot of new features and improvements. We recommend first [to check 4 | the new documentation](./index.md) to see if the new features are useful for your project. 5 | 6 | If you upgrade from 8.x to 9.0, you will need to make some changes to your code, but most of existing behavior should 7 | still work. 8 | 9 | ## Bundle 10 | 11 | If you use the bundle, it is now integrated in the main package. You can remove the `jolicode/automapper-bundle` package from your 12 | `composer.json` file. 13 | 14 | Then you have to use the new namespace for the bundle: 15 | 16 | ```php 17 | use AutoMapper\Symfony\Bundle\AutoMapperBundle; 18 | ``` 19 | 20 | You will also need to update the bundle configuration, see the [bundle documentation](./bundle/configuration.md) for more 21 | information. 22 | 23 | ## Custom Transformers 24 | 25 | The `CustomPropertyTransformerInterface` and `CustomModelTransformerInterface` have been removed in favor of the 26 | `PropertyTransformerInterface` interface handling both case. 27 | 28 | See the [transformers documentation](./mapping/transformer.md#creating-a-custom-transformer) for more information. 29 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - phpstan-baseline.neon 3 | 4 | parameters: 5 | level: max 6 | paths: 7 | - src/ 8 | 9 | tmpDir: cache 10 | 11 | ignoreErrors: 12 | - "#Instantiated class fromIteratorToArray not found#" 13 | - "#Instantiated class toArray not found#" 14 | 15 | - 16 | message: "#^Method Symfony\\\\Component\\\\Serializer\\\\NameConverter\\\\NameConverterInterface\\:\\:normalize\\(\\) invoked with 2 parameters, 1 required\\.$#" 17 | count: 2 18 | path: src/EventListener/Symfony/AdvancedNameConverterListener.php 19 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "automapper-doc" 3 | version = "0.1.0" 4 | description = "Documentation for jolicode/automapper" 5 | authors = ["Joel Wurtz "] 6 | license = "MIT" 7 | readme = "README.md" 8 | package-mode = false 9 | 10 | [tool.poetry.dependencies] 11 | python = "^3.11" 12 | mkdocs = "^1.5.3" 13 | mkdocs-material = "^9.5.13" 14 | markdown-callouts = "^0.4.0" 15 | mkdocs-literate-nav = "^0.6.1" 16 | mike = "^2.0.0" 17 | 18 | 19 | [build-system] 20 | requires = ["poetry-core"] 21 | build-backend = "poetry.core.masonry.api" 22 | -------------------------------------------------------------------------------- /src/Attribute/MapProvider.php: -------------------------------------------------------------------------------- 1 | |'array'|array|'array'>|null $source The source class or classes 17 | * @param class-string|'array'|array|'array'>|null $target The target class or classes 18 | * @param string|null $dateTimeFormat The date-time format to use when transforming this property 19 | */ 20 | public function __construct( 21 | public string|array|null $source = null, 22 | public string|array|null $target = null, 23 | public ?bool $checkAttributes = null, 24 | public ?ConstructorStrategy $constructorStrategy = null, 25 | public ?bool $allowReadOnlyTargetToPopulate = null, 26 | public ?bool $strictTypes = null, 27 | public int $priority = 0, 28 | public ?string $dateTimeFormat = null, 29 | public ?bool $allowExtraProperties = null, 30 | ) { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/AutoMapperInterface.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * @phpstan-import-type MapperContextArray from MapperContext 13 | */ 14 | interface AutoMapperInterface 15 | { 16 | /** 17 | * @template Source of object 18 | * @template Target of object 19 | * 20 | * @param Source|array $source 21 | * @param class-string|'array'|array|Target $target 22 | * @param MapperContextArray $context 23 | * 24 | * @return ($target is class-string|Target ? Target|null : array|null) 25 | */ 26 | public function map(array|object $source, string|array|object $target, array $context = []): array|object|null; 27 | 28 | /** 29 | * @template Source of object 30 | * @template Target of object 31 | * 32 | * @param iterable>|iterable $collection 33 | * @param class-string|'array' $target 34 | * @param MapperContextArray $context 35 | * 36 | * @return ($target is class-string ? array : array) 37 | */ 38 | public function mapCollection(iterable $collection, string $target, array $context = []): array; 39 | } 40 | -------------------------------------------------------------------------------- /src/AutoMapperRegistryInterface.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * @internal 13 | */ 14 | interface AutoMapperRegistryInterface 15 | { 16 | /** 17 | * @template Source of object 18 | * @template Target of object 19 | * 20 | * @param class-string|'array' $source 21 | * @param class-string|'array' $target 22 | * 23 | * @return ($source is class-string ? ($target is 'array' ? MapperInterface> : MapperInterface) : MapperInterface, Target>) 24 | */ 25 | public function getMapper(string $source, string $target): MapperInterface; 26 | } 27 | -------------------------------------------------------------------------------- /src/ConstructorStrategy.php: -------------------------------------------------------------------------------- 1 | 14 | * 15 | * @internal 16 | */ 17 | final class SourcePropertyMetadata 18 | { 19 | /** 20 | * @param Type[]|null $types 21 | * @param string[]|null $groups 22 | */ 23 | public function __construct( 24 | public string $property, 25 | public ?array $types = null, 26 | public ?ReadAccessor $accessor = null, 27 | public ?bool $checkExists = null, 28 | public bool $extractGroupsIfNull = true, 29 | public ?array $groups = null, 30 | public ?string $dateTimeFormat = null, 31 | ) { 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Event/TargetPropertyMetadata.php: -------------------------------------------------------------------------------- 1 | 15 | * 16 | * @internal 17 | */ 18 | final class TargetPropertyMetadata 19 | { 20 | /** 21 | * @param Type[]|null $types 22 | * @param string[]|null $groups 23 | */ 24 | public function __construct( 25 | public string $property, 26 | public ?array $types = null, 27 | public ?ReadAccessor $readAccessor = null, 28 | public ?WriteMutator $writeMutator = null, 29 | public ?string $parameterInConstructor = null, 30 | public bool $extractGroupsIfNull = true, 31 | public ?array $groups = null, 32 | public ?string $dateTimeFormat = null, 33 | ) { 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/EventListener/Symfony/AdvancedNameConverterListener.php: -------------------------------------------------------------------------------- 1 | mapperMetadata->source === 'array' || $event->mapperMetadata->source === \stdClass::class) && $event->source->property === $event->target->property) { 21 | $event->source->property = $this->nameConverter->normalize($event->target->property, $event->mapperMetadata->target); 22 | } 23 | 24 | if (($event->mapperMetadata->target === 'array' || $event->mapperMetadata->target === \stdClass::class) && $event->source->property === $event->target->property) { 25 | $event->target->property = $this->nameConverter->normalize($event->source->property, $event->mapperMetadata->source); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Exception/AutoMapperException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | interface AutoMapperException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/Exception/BadMapDefinitionException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | final class BadMapDefinitionException extends MetadataException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/Exception/CannotCreateTargetException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | final class CannotCreateTargetException extends RuntimeException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/Exception/CircularReferenceException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | final class CircularReferenceException extends RuntimeException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/Exception/CompileException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | final class CompileException extends \LogicException implements AutoMapperException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/Exception/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | final class InvalidArgumentException extends \InvalidArgumentException implements AutoMapperException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/Exception/InvalidMappingException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | final class InvalidMappingException extends MetadataException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/Exception/LogicException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | final class LogicException extends \LogicException implements AutoMapperException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/Exception/MetadataException.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | final class ReadOnlyTargetException extends RuntimeException 13 | { 14 | public function __construct(int $code = 0, ?\Throwable $previous = null) 15 | { 16 | parent::__construct(sprintf('Cannot use readonly class as an object to populate. You can opt-out this behavior by using the context "%s"', MapperContext::ALLOW_READONLY_TARGET_TO_POPULATE), $code, $previous); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Exception/RuntimeException.php: -------------------------------------------------------------------------------- 1 | $context 25 | */ 26 | public function getTypes(string $class, string $property, array $context = []): ?array 27 | { 28 | if (($accessor = $context[self::READ_ACCESSOR] ?? false) && $accessor instanceof ReadAccessor) { 29 | return $accessor->getTypes($class); 30 | } 31 | 32 | if (!($context[self::EXTRACT_TYPE_FROM_GETTER] ?? false) && ($mutator = $context[self::WRITE_MUTATOR] ?? false) && $mutator instanceof WriteMutator) { 33 | return $mutator->getTypes($class); 34 | } 35 | 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Extractor/SourceTargetMappingExtractor.php: -------------------------------------------------------------------------------- 1 | 15 | * 16 | * @internal 17 | */ 18 | class SourceTargetMappingExtractor extends MappingExtractor 19 | { 20 | public function getTypes(string $source, SourcePropertyMetadata $sourceProperty, string $target, TargetPropertyMetadata $targetProperty, bool $extractTypesFromGetter): TypesMatching 21 | { 22 | $sourceTypes = $this->propertyInfoExtractor->getTypes($source, $sourceProperty->property, [ReadWriteTypeExtractor::READ_ACCESSOR => $sourceProperty->accessor]) ?? []; 23 | $targetTypes = $this->propertyInfoExtractor->getTypes($target, $targetProperty->property, [ReadWriteTypeExtractor::WRITE_MUTATOR => $targetProperty->writeMutator, ReadWriteTypeExtractor::EXTRACT_TYPE_FROM_GETTER => $extractTypesFromGetter]) ?? []; 24 | 25 | return TypesMatching::fromSourceAndTargetTypes($sourceTypes, $targetTypes); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Generator/UniqueVariableScope.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | final class UniqueVariableScope 15 | { 16 | /** @var array */ 17 | private array $registry = []; 18 | 19 | /** 20 | * Return a unique name for a variable name. 21 | */ 22 | public function getUniqueName(string $name): string 23 | { 24 | $name = strtolower($name); 25 | 26 | if (!isset($this->registry[$name])) { 27 | $this->registry[$name] = 0; 28 | 29 | return $name; 30 | } 31 | 32 | ++$this->registry[$name]; 33 | 34 | return "{$name}_{$this->registry[$name]}"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Loader/ClassLoaderInterface.php: -------------------------------------------------------------------------------- 1 | 14 | * 15 | * @internal 16 | */ 17 | interface ClassLoaderInterface 18 | { 19 | public function loadClass(MapperMetadata $mapperMetadata): void; 20 | 21 | public function buildMappers(MetadataRegistry $registry): bool; 22 | } 23 | -------------------------------------------------------------------------------- /src/Loader/EvalLoader.php: -------------------------------------------------------------------------------- 1 | 18 | * 19 | * @internal 20 | */ 21 | final readonly class EvalLoader implements ClassLoaderInterface 22 | { 23 | private PrettyPrinterAbstract $printer; 24 | 25 | public function __construct( 26 | private MapperGenerator $generator, 27 | private MetadataFactory $metadataFactory, 28 | ) { 29 | $this->printer = new Standard(); 30 | } 31 | 32 | public function loadClass(MapperMetadata $mapperMetadata): void 33 | { 34 | eval($this->printer->prettyPrint($this->generator->generate( 35 | $this->metadataFactory->getGeneratorMetadata($mapperMetadata->source, $mapperMetadata->target) 36 | ))); 37 | } 38 | 39 | public function buildMappers(MetadataRegistry $registry): bool 40 | { 41 | return false; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Loader/FileReloadStrategy.php: -------------------------------------------------------------------------------- 1 | 13 | * 14 | * @template Source of object|array 15 | * @template Target of object|array 16 | * 17 | * @phpstan-import-type MapperContextArray from MapperContext 18 | */ 19 | interface MapperInterface 20 | { 21 | /** 22 | * @param Source $value Value to map 23 | * @param MapperContextArray $context Mapper context 24 | * 25 | * @return Target|null The mapped value 26 | */ 27 | public function &map(mixed $value, array $context = []): mixed; 28 | } 29 | -------------------------------------------------------------------------------- /src/Metadata/Dependency.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final readonly class Dependency 17 | { 18 | public function __construct( 19 | public MapperDependency $mapperDependency, 20 | public GeneratorMetadata $metadata, 21 | ) { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Metadata/PropertyMetadata.php: -------------------------------------------------------------------------------- 1 | 13 | * 14 | * @internal 15 | */ 16 | final class PropertyMetadata 17 | { 18 | /** 19 | * @param string[]|null $groups 20 | */ 21 | public function __construct( 22 | public readonly SourcePropertyMetadata $source, 23 | public readonly TargetPropertyMetadata $target, 24 | public readonly TypesMatching $types, 25 | public TransformerInterface $transformer, 26 | public bool $ignored = false, 27 | public string $ignoreReason = '', 28 | public ?int $maxDepth = null, 29 | public ?string $if = null, 30 | public ?array $groups = null, 31 | public ?bool $disableGroupsCheck = null, 32 | ) { 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Metadata/SourcePropertyMetadata.php: -------------------------------------------------------------------------------- 1 | 14 | * 15 | * @experimental 16 | */ 17 | final readonly class SourcePropertyMetadata 18 | { 19 | /** 20 | * @param string[]|null $groups 21 | */ 22 | public function __construct( 23 | public string $property, 24 | public ?ReadAccessor $accessor = null, 25 | public bool $checkExists = false, 26 | public ?array $groups = null, 27 | public string $dateTimeFormat = \DateTimeInterface::RFC3339, 28 | ) { 29 | } 30 | 31 | public static function fromEvent(SourcePropertyMetadataEvent $metadata): self 32 | { 33 | return new self( 34 | $metadata->property, 35 | $metadata->accessor, 36 | $metadata->checkExists ?? false, 37 | $metadata->groups, 38 | $metadata->dateTimeFormat ?? \DateTimeInterface::RFC3339, 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Metadata/TargetPropertyMetadata.php: -------------------------------------------------------------------------------- 1 | 15 | * 16 | * @experimental 17 | */ 18 | final readonly class TargetPropertyMetadata 19 | { 20 | /** 21 | * @param string[]|null $groups 22 | */ 23 | public function __construct( 24 | public string $property, 25 | public ?ReadAccessor $readAccessor = null, 26 | public ?WriteMutator $writeMutator = null, 27 | public ?string $parameterInConstructor = null, 28 | public ?array $groups = null, 29 | public string $dateTimeFormat = \DateTimeInterface::RFC3339, 30 | ) { 31 | } 32 | 33 | public static function fromEvent(EventTargetPropertyMetadata $metadata): self 34 | { 35 | return new self( 36 | $metadata->property, 37 | $metadata->readAccessor, 38 | $metadata->writeMutator, 39 | $metadata->parameterInConstructor, 40 | $metadata->groups, 41 | $metadata->dateTimeFormat ?? \DateTimeInterface::RFC3339, 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Provider/EarlyReturn.php: -------------------------------------------------------------------------------- 1 | |null $value 14 | */ 15 | public function __construct( 16 | public object|array|null $value 17 | ) { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Provider/ProviderInterface.php: -------------------------------------------------------------------------------- 1 | |'array' $targetType the target type 18 | * @param mixed $source the source value) 19 | * @param MapperContextArray $context the context 20 | * 21 | * @return object|array|null the value to provide 22 | */ 23 | public function provide(string $targetType, mixed $source, array $context): object|array|null; 24 | } 25 | -------------------------------------------------------------------------------- /src/Provider/ProviderRegistry.php: -------------------------------------------------------------------------------- 1 | */ 15 | private array $providers; 16 | 17 | /** 18 | * @param iterable $providers 19 | */ 20 | public function __construct(iterable $providers) 21 | { 22 | /** @var array $indexedProviders */ 23 | $indexedProviders = []; 24 | 25 | foreach ($providers as $key => $provider) { 26 | if (\is_int($key)) { 27 | $key = $provider::class; 28 | } 29 | 30 | $indexedProviders[$key] = $provider; 31 | } 32 | 33 | $this->providers = $indexedProviders; 34 | } 35 | 36 | public function getProvider(string $id): ProviderInterface 37 | { 38 | if (!\array_key_exists($id, $this->providers)) { 39 | throw new InvalidArgumentException(sprintf('Provider with id "%s" not found.', $id)); 40 | } 41 | 42 | return $this->providers[$id]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Symfony/Bundle/AutoMapperBundle.php: -------------------------------------------------------------------------------- 1 | addCompilerPass(new TransformerFactoryPass()); 20 | } 21 | 22 | public function getContainerExtension(): ?ExtensionInterface 23 | { 24 | return new AutoMapperExtension(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Symfony/Bundle/DependencyInjection/Compiler/TransformerFactoryPass.php: -------------------------------------------------------------------------------- 1 | findAndSortTaggedServices('automapper.transformer_factory', $container) as $definition) { 21 | $selectors[] = $definition; 22 | } 23 | 24 | $definition = $container->getDefinition(ChainTransformerFactory::class); 25 | $definition->replaceArgument(0, $selectors); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Symfony/Bundle/Resources/config/custom_transformers.php: -------------------------------------------------------------------------------- 1 | services() 13 | ->set(PropertyTransformerRegistry::class) 14 | ->args([new TaggedIteratorArgument('automapper.property_transformer', needsIndexes: true)]) 15 | 16 | ->set(PropertyTransformerFactory::class) 17 | ->args([service(PropertyTransformerRegistry::class)]) 18 | ->tag('automapper.transformer_factory', ['priority' => 1003]) 19 | ; 20 | }; 21 | -------------------------------------------------------------------------------- /src/Symfony/Bundle/Resources/config/generator.php: -------------------------------------------------------------------------------- 1 | services() 13 | ->set(MapperGenerator::class) 14 | ->args([ 15 | service(ClassDiscriminatorResolver::class), 16 | service(Configuration::class), 17 | service('automapper.expression_language'), 18 | ]) 19 | 20 | ->set(ClassDiscriminatorResolver::class) 21 | ->args([null]) 22 | ; 23 | }; 24 | -------------------------------------------------------------------------------- /src/Symfony/Bundle/Resources/config/normalizer.php: -------------------------------------------------------------------------------- 1 | services() 12 | ->set(AutoMapperNormalizer::class) 13 | ->args([service(AutoMapperInterface::class)]) 14 | ; 15 | }; 16 | -------------------------------------------------------------------------------- /src/Symfony/Bundle/Resources/config/provider.php: -------------------------------------------------------------------------------- 1 | services() 12 | ->set(ProviderRegistry::class) 13 | ->args([new TaggedIteratorArgument('automapper.provider', needsIndexes: true)]) 14 | ; 15 | }; 16 | -------------------------------------------------------------------------------- /src/Symfony/Bundle/Resources/views/DataCollector/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Symfony/ExpressionLanguageProvider.php: -------------------------------------------------------------------------------- 1 | $functions 18 | */ 19 | public function __construct( 20 | private ServiceProviderInterface $functions 21 | ) { 22 | } 23 | 24 | public function getFunctions(): array 25 | { 26 | $functions = []; 27 | 28 | foreach ($this->functions->getProvidedServices() as $function => $type) { 29 | $functions[] = new ExpressionFunction( 30 | $function, 31 | static fn (...$args) => sprintf('($this->expressionLanguageProvider->get(%s)(%s))', var_export($function, true), implode(', ', $args)), 32 | fn ($values, ...$args) => $this->get($function)(...$args) 33 | ); 34 | } 35 | 36 | return $functions; 37 | } 38 | 39 | public function get(string $function): callable 40 | { 41 | return $this->functions->get($function); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Transformer/AllowNullValueTransformerInterface.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * @internal 13 | */ 14 | interface AllowNullValueTransformerInterface 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/Transformer/ApiPlatform/JsonLdIdTransformer.php: -------------------------------------------------------------------------------- 1 | iriConverter->getIriFromResource($source); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Transformer/AssignedByReferenceTransformerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @internal 11 | */ 12 | interface AssignedByReferenceTransformerInterface 13 | { 14 | /** 15 | * Should the resulting output be assigned by ref. 16 | */ 17 | public function assignByRef(): bool; 18 | } 19 | -------------------------------------------------------------------------------- /src/Transformer/ChainTransformerFactoryAwareInterface.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * @internal 13 | */ 14 | interface ChainTransformerFactoryAwareInterface 15 | { 16 | public function setChainTransformerFactory(ChainTransformerFactory $chainTransformerFactory): void; 17 | } 18 | -------------------------------------------------------------------------------- /src/Transformer/ChainTransformerFactoryAwareTrait.php: -------------------------------------------------------------------------------- 1 | chainTransformerFactory = $chainTransformerFactory; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Transformer/CheckTypeInterface.php: -------------------------------------------------------------------------------- 1 | 15 | * 16 | * @internal 17 | */ 18 | final class CopyEnumTransformer implements TransformerInterface 19 | { 20 | public function transform(Expr $input, Expr $target, PropertyMetadata $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source): array 21 | { 22 | /* No transform here it's the same value and it's a copy so we do not need to clone */ 23 | return [$input, []]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Transformer/CopyTransformer.php: -------------------------------------------------------------------------------- 1 | 15 | * 16 | * @internal 17 | */ 18 | final class CopyTransformer implements TransformerInterface 19 | { 20 | public function transform(Expr $input, Expr $target, PropertyMetadata $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source): array 21 | { 22 | /* No transform here it's the same value and it's a copy so we do not need to clone */ 23 | return [$input, []]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Transformer/CopyTransformerFactory.php: -------------------------------------------------------------------------------- 1 | = 1) { 20 | return null; 21 | } 22 | 23 | return new CopyTransformer(); 24 | } 25 | 26 | public function getPriority(): int 27 | { 28 | return -64; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Transformer/DateTimeInterfaceToImmutableTransformer.php: -------------------------------------------------------------------------------- 1 | 17 | * 18 | * @internal 19 | */ 20 | final class DateTimeInterfaceToImmutableTransformer implements TransformerInterface 21 | { 22 | public function transform(Expr $input, Expr $target, PropertyMetadata $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source): array 23 | { 24 | /* 25 | * Handles all DateTime instance types using createFromInterface. 26 | * 27 | * \DateTimeImmutable::createFromInterface($input); 28 | */ 29 | return [ 30 | new Expr\StaticCall(new Name\FullyQualified(\DateTimeImmutable::class), 'createFromInterface', [ 31 | new Arg($input), 32 | ]), 33 | [], 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Transformer/DateTimeInterfaceToMutableTransformer.php: -------------------------------------------------------------------------------- 1 | 17 | * 18 | * @internal 19 | */ 20 | final class DateTimeInterfaceToMutableTransformer implements TransformerInterface 21 | { 22 | public function transform(Expr $input, Expr $target, PropertyMetadata $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source): array 23 | { 24 | /* 25 | * Handles all DateTime instance types using createFromInterface. 26 | * 27 | * \DateTimeImmutable::createFromInterface($input); 28 | */ 29 | return [ 30 | new Expr\StaticCall(new Name\FullyQualified(\DateTime::class), 'createFromInterface', [ 31 | new Arg($input), 32 | ]), 33 | [], 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Transformer/DependentTransformerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @internal 11 | */ 12 | interface DependentTransformerInterface 13 | { 14 | /** 15 | * Get dependencies for this transformer. 16 | * 17 | * @return MapperDependency[] 18 | */ 19 | public function getDependencies(): array; 20 | } 21 | -------------------------------------------------------------------------------- /src/Transformer/DictionaryTransformer.php: -------------------------------------------------------------------------------- 1 | 13 | * 14 | * @internal 15 | */ 16 | final readonly class DictionaryTransformer extends AbstractArrayTransformer 17 | { 18 | /** 19 | * Assign the value by using the key as the array key. 20 | * 21 | * $values[$key] = $output; 22 | */ 23 | protected function getAssignExpr(Expr $valuesVar, Expr $outputVar, Expr $loopKeyVar, bool $assignByRef): Expr 24 | { 25 | if ($assignByRef) { 26 | return new Expr\AssignRef(new Expr\ArrayDimFetch($valuesVar, $loopKeyVar), $outputVar); 27 | } 28 | 29 | return new Expr\Assign(new Expr\ArrayDimFetch($valuesVar, $loopKeyVar), $outputVar); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Transformer/ExpressionLanguageTransformer.php: -------------------------------------------------------------------------------- 1 | parser = $parser ?? (new ParserFactory())->createForHostVersion(); 27 | } 28 | 29 | public function transform(Expr $input, Expr $target, PropertyMetadata $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source): array 30 | { 31 | $expr = $this->parser->parse('expression . ';')[0] ?? null; 32 | 33 | if ($expr instanceof Stmt\Expression) { 34 | return [$expr->expr, []]; 35 | } 36 | 37 | throw new CompileException('Cannot use callback or create expression language condition from expression "' . $this->expression . "'"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Transformer/FixedValueTransformer.php: -------------------------------------------------------------------------------- 1 | parser = $parser ?? (new ParserFactory())->createForHostVersion(); 27 | } 28 | 29 | public function transform(Expr $input, Expr $target, PropertyMetadata $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source): array 30 | { 31 | $expr = $this->parser->parse('value, true) . ';')[0] ?? null; 32 | 33 | if ($expr instanceof Stmt\Expression) { 34 | return [$expr->expr, []]; 35 | } 36 | 37 | throw new CompileException('Cannot create php code from value ' . json_encode($this->value)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Transformer/MapperDependency.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | final readonly class MapperDependency 15 | { 16 | /** 17 | * @param class-string|'array' $source 18 | * @param class-string|'array' $target 19 | */ 20 | public function __construct( 21 | public string $name, 22 | public string $source, 23 | public string $target, 24 | ) { 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Transformer/PrioritizedTransformerFactoryInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @internal 11 | */ 12 | interface PrioritizedTransformerFactoryInterface 13 | { 14 | /** 15 | * TransformerFactory priority. 16 | */ 17 | public function getPriority(): int; 18 | } 19 | -------------------------------------------------------------------------------- /src/Transformer/PropertyTransformer/PrioritizedPropertyTransformerInterface.php: -------------------------------------------------------------------------------- 1 | propertyTransformerRegistry->getPropertyTransformersForMapper($types, $source, $target, $mapperMetadata); 33 | 34 | if (null === $id) { 35 | return null; 36 | } 37 | 38 | return new PropertyTransformer($id); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Transformer/PropertyTransformer/PropertyTransformerInterface.php: -------------------------------------------------------------------------------- 1 | $source the source input on which the custom transformation applies 17 | * @param array $context Context during mapping 18 | */ 19 | public function transform(mixed $value, object|array $source, array $context): mixed; 20 | } 21 | -------------------------------------------------------------------------------- /src/Transformer/PropertyTransformer/PropertyTransformerSupportInterface.php: -------------------------------------------------------------------------------- 1 | 15 | * 16 | * @internal 17 | */ 18 | final class SourceEnumTransformer implements TransformerInterface 19 | { 20 | public function transform(Expr $input, Expr $target, PropertyMetadata $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source): array 21 | { 22 | /* $input->value */ 23 | return [new Expr\PropertyFetch($input, 'value'), []]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Transformer/StringToSymfonyUidTransformer.php: -------------------------------------------------------------------------------- 1 | 17 | * 18 | * @internal 19 | */ 20 | final readonly class StringToSymfonyUidTransformer implements TransformerInterface 21 | { 22 | public function __construct( 23 | private string $className, 24 | ) { 25 | } 26 | 27 | public function transform(Expr $input, Expr $target, PropertyMetadata $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source): array 28 | { 29 | /* 30 | * Create a Symfony Uid object from a string. 31 | * 32 | * new \Symfony\Component\Uid\Uuid($input); 33 | */ 34 | return [ 35 | new Expr\New_(new Name($this->className), [new Arg($input)]), 36 | [], 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Transformer/SymfonyUidToStringTransformer.php: -------------------------------------------------------------------------------- 1 | 15 | * 16 | * @internal 17 | */ 18 | final readonly class SymfonyUidToStringTransformer implements TransformerInterface 19 | { 20 | public function __construct( 21 | private bool $isUlid, 22 | ) { 23 | } 24 | 25 | public function transform(Expr $input, Expr $target, PropertyMetadata $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source): array 26 | { 27 | /* 28 | * Create a string from a Symfony Uid object. 29 | * 30 | * $input->toBase32() or $input->toRfc4122(); 31 | */ 32 | if ($this->isUlid) { 33 | return [ 34 | // ulid 35 | new Expr\MethodCall($input, 'toBase32'), 36 | [], 37 | ]; 38 | } 39 | 40 | return [ 41 | // uuid 42 | new Expr\MethodCall($input, 'toRfc4122'), 43 | [], 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Transformer/TargetEnumTransformer.php: -------------------------------------------------------------------------------- 1 | 17 | * 18 | * @internal 19 | */ 20 | final readonly class TargetEnumTransformer implements TransformerInterface 21 | { 22 | public function __construct( 23 | private string $targetClassName, 24 | ) { 25 | } 26 | 27 | public function transform(Expr $input, Expr $target, PropertyMetadata $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source): array 28 | { 29 | /* 30 | * Transform a string into a BackendEnum. 31 | * 32 | * \Backend\Enum\TargetEnum::from($input); 33 | */ 34 | return [new Expr\StaticCall(new Name\FullyQualified($this->targetClassName), 'from', [ 35 | new Arg($input), 36 | ]), []]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Transformer/TransformerFactoryInterface.php: -------------------------------------------------------------------------------- 1 | 17 | * 18 | * @internal 19 | */ 20 | interface TransformerFactoryInterface 21 | { 22 | /** 23 | * Get transformer to use when mapping from an array of type to another array of type. 24 | */ 25 | public function getTransformer(TypesMatching $types, SourcePropertyMetadata $source, TargetPropertyMetadata $target, MapperMetadata $mapperMetadata): ?TransformerInterface; 26 | } 27 | -------------------------------------------------------------------------------- /src/Transformer/TransformerInterface.php: -------------------------------------------------------------------------------- 1 | 16 | * 17 | * @internal 18 | */ 19 | interface TransformerInterface 20 | { 21 | /** 22 | * Get AST output and expressions for transforming a property mapping given an input. 23 | * 24 | * @return array{0: Expr, 1: Stmt[]} First value is the output expression, second value is an array of stmt needed to get the output 25 | */ 26 | public function transform(Expr $input, Expr $target, PropertyMetadata $propertyMapping, UniqueVariableScope $uniqueVariableScope, Expr\Variable $source): array; 27 | } 28 | -------------------------------------------------------------------------------- /src/Transformer/VoidTransformer.php: -------------------------------------------------------------------------------- 1 | 2 4 | "foo" => "foo" 5 | ] 6 | flag::STD_PROP_LIST: false 7 | flag::ARRAY_AS_PROPS: false 8 | iteratorClass: "ArrayIterator" 9 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ArrayAccess/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\ArrayAccess\Foo { 2 | +foo: "foofoo" 3 | +bar: 10 4 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ArrayAccess/map.php: -------------------------------------------------------------------------------- 1 | map(new LikeArray(['foo' => 'foofoo', 'bar' => 10]), Foo::class); 25 | yield $autoMapper->map(new Foo(), LikeArray::class); 26 | })(); 27 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/ArrayConsistency/expected.to.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\ArrayConsistency\To { 2 | +values: [ 3 | 1 4 | 2 5 | 3 6 | ] 7 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ArrayConsistency/expected.toAdder.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\ArrayConsistency\ToAdder { 2 | -values: [ 3 | 1 4 | 2 5 | 3 6 | ] 7 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ArrayConsistency/expected.toAdderCollection.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\ArrayConsistency\ToAdderCollection { 2 | -values: Doctrine\Common\Collections\ArrayCollection { 3 | -elements: [ 4 | 3 => 1 5 | 4 => 2 6 | 5 => 3 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ArrayConsistency/expected.toCollection.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\ArrayConsistency\ToCollection { 2 | +values: Doctrine\Common\Collections\ArrayCollection { 3 | -elements: [ 4 | 1 5 | 2 6 | 3 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ArrayNested/expected.array.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\ArrayNested\TestTarget { 2 | -endpoints: AutoMapper\Tests\AutoMapperTest\ArrayNested\TestSubTarget { 3 | -endpoint: "https://example.com" 4 | -params: [ 5 | "param1" 6 | "param2" 7 | ] 8 | } 9 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ArrayNested/map.php: -------------------------------------------------------------------------------- 1 | endpoints = [ 36 | 'endpoint' => 'https://example.com', 37 | 'params' => ['param1', 'param2'], 38 | ]; 39 | 40 | yield 'array' => $autoMapper->map($classContainer, TestTarget::class); 41 | })(); 42 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/BuiltinClass/expected.data: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /tests/AutoMapperTest/BuiltinClass/map.php: -------------------------------------------------------------------------------- 1 | map(new BuiltinClass(new \DateInterval('P1Y')), 'array'); 20 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/CircularReferenceDeep/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\CircularReferenceDeep\CircularFoo {#1 2 | +bar: AutoMapper\Tests\AutoMapperTest\CircularReferenceDeep\CircularBar { 3 | +baz: AutoMapper\Tests\AutoMapperTest\CircularReferenceDeep\CircularBaz { 4 | +foo: AutoMapper\Tests\AutoMapperTest\CircularReferenceDeep\CircularFoo {#1} 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/CircularReferenceDeep/map.php: -------------------------------------------------------------------------------- 1 | bar = $bar; 32 | $bar->baz = $baz; 33 | $baz->foo = $foo; 34 | 35 | return AutoMapperBuilder::buildAutoMapper()->map($foo, CircularFoo::class); 36 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/ConstructorPropertyPromoted/expected.data: -------------------------------------------------------------------------------- 1 | [ 2 | "addresses" => [ 3 | [ 4 | "city" => "city" 5 | ] 6 | [ 7 | "city" => "city" 8 | ] 9 | ] 10 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/ConstructorPropertyPromoted/map.php: -------------------------------------------------------------------------------- 1 | $addresses 21 | */ 22 | public function __construct( 23 | public array $addresses 24 | ) { 25 | } 26 | } 27 | 28 | $address = new AddressDTO(); 29 | $address->city = 'city'; 30 | 31 | $object = new UserPromoted([$address, $address]); 32 | 33 | return AutoMapperBuilder::buildAutoMapper()->map($object, 'array'); 34 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/ConstructorWithRelation/expected.constructor-and-relation-missing.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Exception\MissingConstructorArgumentsException { 2 | +class: "AutoMapper\Exception\MissingConstructorArgumentsException" 3 | +message: "Cannot create an instance of "AutoMapper\Tests\AutoMapperTest\ConstructorWithRelation\UserConstructorDTOWithRelation" from mapping data because its constructor requires the following parameters to be present : "$int"." 4 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ConstructorWithRelation/expected.constructor-arguments.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\ConstructorWithRelation\UserConstructorDTOWithRelation { 2 | +int: AutoMapper\Tests\AutoMapperTest\ConstructorWithRelation\IntDto { 3 | +foo: 1 4 | } 5 | +name: "foo" 6 | +age: 30 7 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ConstructorWithRelation/expected.ok.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\ConstructorWithRelation\UserConstructorDTOWithRelation { 2 | +int: AutoMapper\Tests\AutoMapperTest\ConstructorWithRelation\IntDto { 3 | +foo: 1 4 | } 5 | +name: "foo" 6 | +age: 30 7 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ConstructorWithSerializedName/expected.from_array.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\ConstructorWithSerializedName\Bar { 2 | +isStatic: true 3 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ConstructorWithSerializedName/expected.to_array.data: -------------------------------------------------------------------------------- 1 | [ 2 | "is_static" => true 3 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/ConstructorWithSerializedName/map.php: -------------------------------------------------------------------------------- 1 | $autoMapper->map(new Bar(true), 'array'); 23 | 24 | yield 'from_array' => $autoMapper->map(['is_static' => true], Bar::class); 25 | })(); 26 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/Covariance/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\Covariance\ExtendedA { 2 | #b: AutoMapper\Tests\AutoMapperTest\Covariance\ExtendedB {} 3 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/Covariance/map.php: -------------------------------------------------------------------------------- 1 | b; 25 | } 26 | 27 | public function setB(GenericB $b): void 28 | { 29 | $this->b = $b; 30 | } 31 | } 32 | 33 | class ExtendedB extends GenericB 34 | { 35 | public function specificToB(): string 36 | { 37 | return 'result from ExtendedB'; 38 | } 39 | } 40 | 41 | class ExtendedA extends GenericA 42 | { 43 | /** 44 | * @var ExtendedB 45 | */ 46 | protected $b; 47 | 48 | public function getB(): ExtendedB 49 | { 50 | return $this->b; 51 | } 52 | } 53 | 54 | $autoMapper = AutoMapperBuilder::buildAutoMapper(mapPrivatePropertiesAndMethod: true); 55 | 56 | $genericA = new GenericA(); 57 | $genericB = new GenericB(); 58 | $genericA->setB($genericB); 59 | 60 | return $autoMapper->map($genericA, ExtendedA::class); 61 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/DeepPopulateWithArrayCollection/expected.array.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Foo { 2 | +bars: [ 3 | AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar { 4 | +bar: "bar1" 5 | } 6 | AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar { 7 | +bar: "bar2" 8 | } 9 | AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar { 10 | +bar: "bar3" 11 | } 12 | AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar { 13 | +bar: "bar4" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/DeepPopulateWithArrayCollection/expected.collection.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\FooWithArrayCollection { 2 | +bars: Doctrine\Common\Collections\ArrayCollection { 3 | -elements: [ 4 | AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar { 5 | +bar: "bar1" 6 | } 7 | AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar { 8 | +bar: "bar2" 9 | } 10 | AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar { 11 | +bar: "bar3" 12 | } 13 | AutoMapper\Tests\AutoMapperTest\DeepPopulateWithArrayCollection\Bar { 14 | +bar: "bar4" 15 | } 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/DifferentSetterGetterType/expected.data: -------------------------------------------------------------------------------- 1 | [ 2 | "address" => "flat" 3 | "addressDocBlock" => "flat" 4 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/DifferentSetterGetterType/map.php: -------------------------------------------------------------------------------- 1 | addressDocBlock = $address; 23 | } 24 | 25 | public function getAddress(): string 26 | { 27 | return $this->address->value; 28 | } 29 | 30 | public function getAddressDocBlock(): string 31 | { 32 | return $this->addressDocBlock->value; 33 | } 34 | 35 | public function setAddress(AddressType $address): void 36 | { 37 | $this->address = $address; 38 | } 39 | } 40 | 41 | $object = new DifferentSetterGetterType(AddressType::FLAT); 42 | 43 | return AutoMapperBuilder::buildAutoMapper()->map($object, 'array'); 44 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/DiscriminatorMapAndInterface/expected.to-array.data: -------------------------------------------------------------------------------- 1 | [ 2 | "myInterface" => [ 3 | "name" => "my name" 4 | "type" => "type_a" 5 | ] 6 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/DiscriminatorMapAndInterface/expected.to-class.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\DiscriminatorMapAndInterface\Something { 2 | +myInterface: AutoMapper\Tests\AutoMapperTest\DiscriminatorMapAndInterface\TypeA { 3 | +name: "my name" 4 | } 5 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/DiscriminatorMapBadConfiguration/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Exception\CannotCreateTargetException { 2 | +class: "AutoMapper\Exception\CannotCreateTargetException" 3 | +message: "Cannot create target object, because the target is abstract or an interface, and the property "linkType" is not defined, or the value does not match any discriminator type." 4 | } 5 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/DiscriminatorMapBadConfiguration/map.php: -------------------------------------------------------------------------------- 1 | DefaultLinkType::class, 12 | ])] 13 | abstract class LinkType 14 | { 15 | } 16 | 17 | class DefaultLinkType extends LinkType 18 | { 19 | public function __construct( 20 | public string $link, 21 | ) { 22 | } 23 | } 24 | 25 | $source = [ 26 | // There is a typo on the key `type`. 27 | // It should be `linkType` instead of `type`. 28 | 'type' => 'default', 29 | 'link' => 'https://example.com', 30 | ]; 31 | 32 | try { 33 | return AutoMapperBuilder::buildAutoMapper()->map($source, LinkType::class); 34 | } catch (\Throwable $th) { 35 | return $th; 36 | } 37 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/DiscriminatorPopulate/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\DiscriminatorPopulate\LinkWrapper { 2 | +link: AutoMapper\Tests\AutoMapperTest\DiscriminatorPopulate\DefaultLinkType { 3 | +link: "https://example.com/new" 4 | +description: "description" 5 | } 6 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/DiscriminatorPopulate/map.php: -------------------------------------------------------------------------------- 1 | DefaultLinkType::class, 13 | ])] 14 | abstract class LinkType 15 | { 16 | } 17 | 18 | class DefaultLinkType extends LinkType 19 | { 20 | public function __construct( 21 | public string $link, 22 | public string $description = 'default', 23 | ) { 24 | } 25 | } 26 | 27 | class LinkWrapper 28 | { 29 | public function __construct( 30 | public LinkType $link, 31 | ) { 32 | } 33 | } 34 | 35 | $source = [ 36 | 'link' => [ 37 | 'linkType' => 'default', 38 | 'link' => 'https://example.com/new', 39 | ], 40 | ]; 41 | 42 | $existingData = new LinkWrapper(new DefaultLinkType('https://example.com/old', 'description')); 43 | 44 | return AutoMapperBuilder::buildAutoMapper()->map($source, $existingData, [MapperContext::DEEP_TARGET_TO_POPULATE => true]); 45 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/DoctrineCollections/expected.from-array.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\DoctrineCollections\Library { 2 | +books: Doctrine\Common\Collections\ArrayCollection { 3 | -elements: [ 4 | AutoMapper\Tests\AutoMapperTest\DoctrineCollections\Book { 5 | +name: "The Empyrean Onyx Storm" 6 | } 7 | AutoMapper\Tests\AutoMapperTest\DoctrineCollections\Book { 8 | +name: "Valentina" 9 | } 10 | AutoMapper\Tests\AutoMapperTest\DoctrineCollections\Book { 11 | +name: "Imbalance" 12 | } 13 | ] 14 | } 15 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/DoctrineCollections/expected.to-array.data: -------------------------------------------------------------------------------- 1 | [ 2 | "books" => [ 3 | [ 4 | "name" => "The Empyrean Onyx Storm" 5 | ] 6 | [ 7 | "name" => "Valentina" 8 | ] 9 | [ 10 | "name" => "Imbalance" 11 | ] 12 | ] 13 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/DoctrineCollections/map.php: -------------------------------------------------------------------------------- 1 | */ 22 | public Collection $books; 23 | } 24 | 25 | return (function () { 26 | $autoMapper = AutoMapperBuilder::buildAutoMapper(); 27 | 28 | $library = new Library(); 29 | $library->books = new ArrayCollection([ 30 | new Book('The Empyrean Onyx Storm'), 31 | new Book('Valentina'), 32 | new Book('Imbalance'), 33 | ]); 34 | yield 'to-array' => $autoMapper->map($library, 'array'); 35 | 36 | $data = [ 37 | 'books' => [ 38 | ['name' => 'The Empyrean Onyx Storm'], 39 | ['name' => 'Valentina'], 40 | ['name' => 'Imbalance'], 41 | ], 42 | ]; 43 | yield 'from-array' => $autoMapper->map($data, Library::class); 44 | })(); 45 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/GroupOverride/expected.data: -------------------------------------------------------------------------------- 1 | [ 2 | "id" => "id" 3 | "name" => "name" 4 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/GroupOverride/map.php: -------------------------------------------------------------------------------- 1 | map($group, 'array', ['groups' => ['group2']]); 28 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/Ignore/expected.ignore-in-source.data: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /tests/AutoMapperTest/Ignore/expected.ignore-in-target.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\Ignore\FooIgnore { 2 | +id: null 3 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/Ignore/map.php: -------------------------------------------------------------------------------- 1 | id; 21 | } 22 | } 23 | 24 | return (function () { 25 | $autoMapper = AutoMapperBuilder::buildAutoMapper(); 26 | 27 | $foo = new FooIgnore(); 28 | $foo->id = 5; 29 | yield 'ignore-in-source' => $autoMapper->map($foo, 'array'); 30 | 31 | $foo = ['id' => 5]; 32 | yield 'ignore-in-target' => $autoMapper->map($foo, FooIgnore::class); 33 | })(); 34 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/Issue111/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\Issue111\Foo { 2 | -colours: [ 3 | AutoMapper\Tests\AutoMapperTest\Issue111\Colour { 4 | +name: "red" 5 | } 6 | AutoMapper\Tests\AutoMapperTest\Issue111\Colour { 7 | +name: "green" 8 | } 9 | AutoMapper\Tests\AutoMapperTest\Issue111\Colour { 10 | +name: "blue" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/Issue425/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\Issue425\Bar { 2 | +property: [ 3 | 1 4 | 2 5 | 3 6 | 4 7 | 5 8 | ] 9 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/Issue425/map.php: -------------------------------------------------------------------------------- 1 | property = $property; 20 | } 21 | 22 | public function getProperty(): array 23 | { 24 | return $this->property; 25 | } 26 | } 27 | 28 | class Bar 29 | { 30 | public array $property = []; 31 | } 32 | 33 | return AutoMapperBuilder::buildAutoMapper()->map($foo, Bar::class); 34 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/IssueParamDocBlock/expected.data: -------------------------------------------------------------------------------- 1 | [ 2 | "bar" => "bar" 3 | "foo" => [ 4 | "foo1" 5 | "foo2" 6 | ] 7 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/IssueParamDocBlock/map.php: -------------------------------------------------------------------------------- 1 | map($foo, 'array'); 36 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/MaxDepth/expected.data: -------------------------------------------------------------------------------- 1 | [ 2 | "child" => [ 3 | "child" => [ 4 | "id" => 2 5 | ] 6 | "id" => 1 7 | ] 8 | "id" => 0 9 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/MaxDepth/map.php: -------------------------------------------------------------------------------- 1 | id = $id; 26 | $this->child = $child; 27 | } 28 | 29 | public function getId(): int 30 | { 31 | return $this->id; 32 | } 33 | 34 | public function getChild(): ?self 35 | { 36 | return $this->child; 37 | } 38 | } 39 | 40 | $foo = new FooMaxDepth(0, new FooMaxDepth(1, new FooMaxDepth(2, new FooMaxDepth(3, new FooMaxDepth(4))))); 41 | 42 | return AutoMapperBuilder::buildAutoMapper()->map($foo, 'array'); 43 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/NoProperties/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\NoProperties\FooNoProperties {} -------------------------------------------------------------------------------- /tests/AutoMapperTest/NoProperties/map.php: -------------------------------------------------------------------------------- 1 | map($noProperties, FooNoProperties::class); 16 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/NoTypes/expected.data: -------------------------------------------------------------------------------- 1 | [ 2 | "city" => "test" 3 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/NoTypes/map.php: -------------------------------------------------------------------------------- 1 | city = 'test'; 16 | 17 | $autoMapper = AutoMapperBuilder::buildAutoMapper(classPrefix: 'NotReadable_'); 18 | 19 | return $autoMapper->map($address, 'array'); 20 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/ObjectWithPropertyAsUnknownArray/expected.object-to-object-with-property-as-unknown-array.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\ObjectWithPropertyAsUnknownArray\Page { 2 | +title: "my title" 3 | +components: [ 4 | [ 5 | "name" => "my name" 6 | ] 7 | ] 8 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ObjectWithPropertyAsUnknownArray/expected.object-with-property-as-unknown-array-to-object.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\ObjectWithPropertyAsUnknownArray\PageDto { 2 | +title: "my title" 3 | +components: [ 4 | AutoMapper\Tests\AutoMapperTest\ObjectWithPropertyAsUnknownArray\ComponentDto { 5 | +name: "my name" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/ObjectWithPropertyAsUnknownArray/map.php: -------------------------------------------------------------------------------- 1 | $components 27 | */ 28 | public function __construct( 29 | public string $title, 30 | public array $components, 31 | ) { 32 | } 33 | } 34 | 35 | return (function () { 36 | $autoMapper = AutoMapperBuilder::buildAutoMapper(); 37 | $entity = new Page(); 38 | $entity->components[] = ['name' => 'my name']; 39 | 40 | yield 'object-with-property-as-unknown-array-to-object' => $autoMapper->map($entity, PageDto::class); 41 | 42 | $dto = new PageDto('my title', [new ComponentDto('my name')]); 43 | yield 'object-to-object-with-property-as-unknown-array' => $autoMapper->map($dto, Page::class); 44 | })(); 45 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/Paramters/expected.int.data: -------------------------------------------------------------------------------- 1 | [ 2 | "parameters" => [ 3 | "foo" 4 | "bar" 5 | "baz" 6 | ] 7 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/Paramters/expected.mixed.data: -------------------------------------------------------------------------------- 1 | [ 2 | "parameters" => [ 3 | 0 => "foo" 4 | "azerty" => "bar" 5 | 1 => "baz" 6 | ] 7 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/Paramters/expected.string.data: -------------------------------------------------------------------------------- 1 | [ 2 | "parameters" => [ 3 | "foo" => "azerty" 4 | "bar" => "qwerty" 5 | "baz" => "dvorak" 6 | ] 7 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/Private/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\Private\PrivateUserDTO { 2 | -id: 10 3 | -firstName: "foo" 4 | -lastName: "bar" 5 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/Private/map.php: -------------------------------------------------------------------------------- 1 | id; 23 | } 24 | 25 | public function getFirstName(): string 26 | { 27 | return $this->firstName; 28 | } 29 | 30 | public function getLastName(): string 31 | { 32 | return $this->lastName; 33 | } 34 | } 35 | 36 | class PrivateUser 37 | { 38 | /** @var int */ 39 | private $id; 40 | 41 | /** @var string */ 42 | private $firstName; 43 | 44 | /** @var string */ 45 | private $lastName; 46 | 47 | public function __construct(int $id, string $firstName, string $lastName) 48 | { 49 | $this->id = $id; 50 | $this->firstName = $firstName; 51 | $this->lastName = $lastName; 52 | } 53 | } 54 | 55 | $autoMapper = AutoMapperBuilder::buildAutoMapper(mapPrivatePropertiesAndMethod: true); 56 | 57 | $user = new PrivateUser(10, 'foo', 'bar'); 58 | 59 | return $autoMapper->map($user, PrivateUserDTO::class); 60 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/PrivatePropertyInConstructors/expected.from-array.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\PrivatePropertyInConstructors\ChildClass { 2 | -parentProp: "foo" 3 | -childProp: "bar" 4 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/PrivatePropertyInConstructors/expected.from-class.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\PrivatePropertyInConstructors\ChildClass { 2 | -parentProp: "foo" 3 | -childProp: "bar" 4 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/Provider/expected.bar-foo.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\Provider\MyObject { 2 | +foo: "foo" 3 | +bar: "foo" 4 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/Provider/expected.bar.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\Provider\MyObject { 2 | +foo: "bar" 3 | +bar: "foo" 4 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/Provider/expected.early-return.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\Provider\MyObject { 2 | +foo: "bar" 3 | +bar: "foo" 4 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/SkipNullValues/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\SkipNullValues\Entity { 2 | -name: "foobar" 3 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/SkipNullValues/map.php: -------------------------------------------------------------------------------- 1 | name = $name; 28 | } 29 | 30 | public function getName(): string 31 | { 32 | return $this->name; 33 | } 34 | } 35 | 36 | $entity = new Entity(); 37 | $entity->setName('foobar'); 38 | $input = new Input(); 39 | 40 | /** @var Entity $entity */ 41 | return AutoMapperBuilder::buildAutoMapper()->map($input, $entity, [MapperContext::SKIP_NULL_VALUES => true]); 42 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/StdObject/expected.data: -------------------------------------------------------------------------------- 1 | { 2 | +"id": 1 3 | +"nestedStd": { 4 | +"id": 2 5 | } 6 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/StdObject/map.php: -------------------------------------------------------------------------------- 1 | id = 1; 11 | $nestedStd = new \stdClass(); 12 | $nestedStd->id = 2; 13 | $user->nestedStd = $nestedStd; 14 | 15 | return AutoMapperBuilder::buildAutoMapper()->map($user, \stdClass::class); 16 | -------------------------------------------------------------------------------- /tests/AutoMapperTest/SymfonyUId/expected.array-to-object-v1.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\SymfonyUId\SymfonyUuidUser { 2 | -uuid: Symfony\Component\Uid\Uuid { 3 | #uid: "0a5411fe-fe79-11ef-9fea-a1a72bed7412" 4 | toBase58: "2GyRqKWsLTbfsViegTFY8q" 5 | toBase32: "0AAG8ZXZKS27QSZTN1MWNYTX0J" 6 | } 7 | +name: "Grégoire Pineau" 8 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/SymfonyUId/expected.array-to-object-v3.data: -------------------------------------------------------------------------------- 1 | [ 2 | "name" => "Grégoire Pineau" 3 | "uuid" => "42650c8f-f5d0-3b1d-a338-f821651471ff" 4 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/SymfonyUId/expected.array-to-object-v4.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\SymfonyUId\SymfonyUuidUser { 2 | -uuid: Symfony\Component\Uid\Uuid { 3 | #uid: "9dbee72c-ebe5-450e-843c-bb06ea7fd4be" 4 | toBase58: "LUnjmkZGWrPYUEHdNgy1eD" 5 | toBase32: "4XQVKJSTZ58M788F5V0VN7ZN5Y" 6 | } 7 | +name: "Grégoire Pineau" 8 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/SymfonyUId/expected.array-to-object.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\SymfonyUId\SymfonyUlidUser { 2 | -ulid: Symfony\Component\Uid\Ulid { 3 | #uid: "01EXE87A54256F05N8P6SB2M9M" 4 | toBase58: "1BW5skaQFE4XVqyhSXjpoV" 5 | toRfc4122: "01775c83-a8a4-114c-f016-a8b1b2b15134" 6 | time: "2021-02-01 07:34:28.260 UTC" 7 | } 8 | +name: "Grégoire Pineau" 9 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/SymfonyUId/expected.object-to-array.data: -------------------------------------------------------------------------------- 1 | [ 2 | "name" => "Grégoire Pineau" 3 | "ulid" => "01EXE89XR69GERC6GV3J4X38FJ" 4 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/SymfonyUId/expected.object-to-object.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\SymfonyUId\SymfonyUlidUser { 2 | -ulid: Symfony\Component\Uid\Ulid { 3 | #uid: "01EXE8A6TNWVCEGMZ36AX8N9MC" 4 | toBase58: "1BW5ssa8XagP9UEZjgabG7" 5 | toRfc4122: "01775c85-1b55-e6d8-e853-e332ba8aa68c" 6 | time: "2021-02-01 07:36:03.157 UTC" 7 | } 8 | +name: "Grégoire Pineau" 9 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/TargetToPopulate/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\TargetToPopulate\VatEntity { 2 | -id: 1 3 | -countryCode: "fr" 4 | -stateCode: null 5 | -standardVatRate: 21.0 6 | -reducedVatRate: 5.5 7 | -displayIncVatPrices: true 8 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/UninitializedProperties/expected.data: -------------------------------------------------------------------------------- 1 | AutoMapper\Tests\AutoMapperTest\UninitializedProperties\User { 2 | +lastName: "Doe" 3 | +firstName: "John" 4 | +birthDate: ? ?DateTimeImmutable 5 | } -------------------------------------------------------------------------------- /tests/AutoMapperTest/UnionProperty/expected.bar.data: -------------------------------------------------------------------------------- 1 | [ 2 | "prop" => [ 3 | "bar" => "bar" 4 | ] 5 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/UnionProperty/expected.foo.data: -------------------------------------------------------------------------------- 1 | [ 2 | "prop" => [ 3 | "foo" => "foo" 4 | ] 5 | ] -------------------------------------------------------------------------------- /tests/AutoMapperTest/UnionProperty/map.php: -------------------------------------------------------------------------------- 1 | $autoMapper->map(new ObjectsUnionProperty(new Bar('bar')), 'array'); 37 | 38 | yield 'foo' => $autoMapper->map(new ObjectsUnionProperty(new Foo('foo')), 'array'); 39 | })(); 40 | -------------------------------------------------------------------------------- /tests/AutoMapperTestCase.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | abstract class AutoMapperTestCase extends TestCase 14 | { 15 | protected AutoMapper $autoMapper; 16 | 17 | protected function setUp(): void 18 | { 19 | $this->autoMapper = AutoMapperBuilder::buildAutoMapper(); 20 | } 21 | 22 | protected function tearDown(): void 23 | { 24 | unset($this->autoMapper); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/App/Api/Entity/Book.php: -------------------------------------------------------------------------------- 1 | reviews = []; 40 | } 41 | 42 | public function getId(): ?int 43 | { 44 | return $this->id; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/App/Api/Entity/Review.php: -------------------------------------------------------------------------------- 1 | id; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/App/Api/Processor/BookProcessor.php: -------------------------------------------------------------------------------- 1 | getMethod() === 'POST') { 21 | $data->id = random_int(1, 1000); 22 | } 23 | 24 | return $data; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/App/Api/Provider/BookProvider.php: -------------------------------------------------------------------------------- 1 | title = 'The Book'; 18 | $book->id = 1; 19 | 20 | if ($operation instanceof CollectionOperationInterface) { 21 | return [$book]; 22 | } 23 | 24 | return $book; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/App/AppKernel.php: -------------------------------------------------------------------------------- 1 | additionalConfigFile) { 28 | $this->getContainerLoader($containerBuilder)->load($this->additionalConfigFile); 29 | } 30 | 31 | return $containerBuilder; 32 | } 33 | 34 | public function getProjectDir(): string 35 | { 36 | return __DIR__ . '/..'; 37 | } 38 | 39 | public function getCacheDir(): string 40 | { 41 | return parent::getCacheDir() . '/' . md5($this->additionalConfigFile ?? ''); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/App/Entity/Address.php: -------------------------------------------------------------------------------- 1 | city = $city; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/App/Entity/AddressDTO.php: -------------------------------------------------------------------------------- 1 | value}_{$suffix}"; 21 | } 22 | 23 | public function getVirtualProperty( 24 | #[MapToContext('prefix')] string $prefix, 25 | #[MapToContext('suffix')] string $suffix, 26 | ): string { 27 | return "{$prefix}_{$this->value}_{$suffix}"; 28 | } 29 | 30 | public function getPropertyWithDefaultValue( 31 | string $someVar = 'foo', 32 | ): string { 33 | return $someVar; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/App/Entity/ClassWithPrivateProperty.php: -------------------------------------------------------------------------------- 1 | Cat::class, 12 | 'dog' => Dog::class, 13 | ]) 14 | ] 15 | class Pet 16 | { 17 | /** @var string */ 18 | public $type; 19 | } 20 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/App/Entity/SomeEnum.php: -------------------------------------------------------------------------------- 1 | name = $name; 62 | } 63 | 64 | public function getName() 65 | { 66 | return $this->name; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/App/Normalizer/AddressDTONormalizer.php: -------------------------------------------------------------------------------- 1 | true]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/App/Service/CatProvider.php: -------------------------------------------------------------------------------- 1 | age); 23 | } 24 | 25 | public function supports(TypesMatching $types, SourcePropertyMetadata $source, TargetPropertyMetadata $target, MapperMetadata $mapperMetadata): bool 26 | { 27 | return User::class === $mapperMetadata->source && UserDTO::class === $mapperMetadata->target && 'yearOfBirth' === $source->property; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(['--env', '-e'], getenv('SYMFONY_ENV') ?: 'dev'); 18 | $debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(['--no-debug', '']) && $env !== 'prod'; 19 | 20 | if ($debug) { 21 | Debug::enable(); 22 | } 23 | 24 | $kernel = new AppKernel($env, $debug); 25 | $application = new Application($kernel); 26 | $application->run($input); 27 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 7 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['dev' => true], 8 | AutoMapper\Symfony\Bundle\AutoMapperBundle::class => ['all' => true], 9 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true], 10 | ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], 11 | ]; 12 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/config/packages/api_platform.yaml: -------------------------------------------------------------------------------- 1 | api_platform: 2 | title: automapper 3 | description: 'Automapper API' 4 | version: 1.0.0 5 | mapping: 6 | paths: 7 | - '%kernel.project_dir%/App/Api/Entity' 8 | formats: 9 | jsonld: [ 'application/ld+json' ] 10 | json: [ 'application/json' ] 11 | html: [ 'text/html' ] -------------------------------------------------------------------------------- /tests/Bundle/Resources/config/packages/automapper.yaml: -------------------------------------------------------------------------------- 1 | automapper: 2 | normalizer: 3 | enabled: true 4 | only_registered_mapping: true 5 | loader: 6 | eval: false 7 | reload_strategy: 'always' 8 | api_platform: true 9 | name_converter: AutoMapper\Tests\Bundle\Resources\App\Service\IdNameConverter 10 | map_private_properties: false 11 | check_attributes: false 12 | mapping: 13 | paths: 14 | - '%kernel.project_dir%/App/Api/Entity' 15 | mappers: 16 | - { source: 'AutoMapper\Tests\Bundle\Resources\App\Entity\NestedObject', target: 'array' } 17 | - { source: 'AutoMapper\Tests\Bundle\Resources\App\Entity\AddressDTO', target: 'array', reverse: true } 18 | - { source: 'AutoMapper\Tests\Bundle\Resources\App\Entity\Pet', target: 'array', reverse: true } 19 | - { source: 'AutoMapper\Tests\Bundle\Resources\App\Entity\UserDTO', target: 'array', reverse: false } 20 | - { source: 'array', target: 'AutoMapper\Tests\Bundle\Resources\App\Entity\Order', reverse: false } 21 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/config/packages/dev/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: true 3 | intercept_redirects: false -------------------------------------------------------------------------------- /tests/Bundle/Resources/config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: automapper 3 | property_info: ~ 4 | test: ~ 5 | serializer: 6 | enabled: true 7 | profiler: 8 | enabled: "%kernel.debug%" 9 | only_exceptions: false -------------------------------------------------------------------------------- /tests/Bundle/Resources/config/routes/dev/profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler_wdt: 2 | resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' 3 | prefix: /_wdt 4 | 5 | web_profiler_profiler: 6 | resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' 7 | prefix: /_profiler -------------------------------------------------------------------------------- /tests/Bundle/Resources/config/routes/routes.yaml: -------------------------------------------------------------------------------- 1 | controllers: 2 | resource: 3 | path: ../../App/Controller/ 4 | namespace: AutoMapper\Tests\Bundle\Resources\App\Controller 5 | type: attribute 6 | 7 | _api_platform: 8 | resource: '.' 9 | type: 'api_platform' -------------------------------------------------------------------------------- /tests/Bundle/Resources/config/services.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | _defaults: 3 | autoconfigure: true 4 | autowire: true 5 | 6 | AutoMapper\Tests\Bundle\Resources\App\: 7 | resource: '../App/*' 8 | exclude: '../App/{Entity,AppKernel.php}' 9 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/config/with-private-properties.yml: -------------------------------------------------------------------------------- 1 | automapper: 2 | class_prefix: "Symfony_Mapper_With_Private_Property" 3 | map_private_properties: true 4 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/public/dev.php: -------------------------------------------------------------------------------- 1 | handle($request); 15 | $response->send(); 16 | $kernel->terminate($request, $response); 17 | -------------------------------------------------------------------------------- /tests/Bundle/Resources/public/index.php: -------------------------------------------------------------------------------- 1 | handle($request); 13 | $response->send(); 14 | $kernel->terminate($request, $response); 15 | -------------------------------------------------------------------------------- /tests/Extractor/Fixtures/Foo.php: -------------------------------------------------------------------------------- 1 | bar = 'Hello World!'; 13 | } 14 | 15 | return $object; 16 | } 17 | 18 | public function switch(mixed $object, string $someString): mixed 19 | { 20 | if ($object instanceof Foo) { 21 | $object->bar = 'Hello World!'; 22 | $object->baz = $someString; 23 | } 24 | 25 | return $object; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Fixtures/Address.php: -------------------------------------------------------------------------------- 1 | city = $city; 17 | } 18 | 19 | public static function fromDTO(AddressDTO $addressDTO): self 20 | { 21 | $address = new self(); 22 | $address->city = $addressDTO->city; 23 | 24 | return $address; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Fixtures/AddressBar.php: -------------------------------------------------------------------------------- 1 | city = $city; 14 | } 15 | 16 | public function getCity(): string 17 | { 18 | return $this->city; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Fixtures/AddressDTOWithReadonlyPromotedProperty.php: -------------------------------------------------------------------------------- 1 | city; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Fixtures/AddressType.php: -------------------------------------------------------------------------------- 1 | type = $type; 14 | } 15 | 16 | public function getType(): AddressType 17 | { 18 | return $this->type; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Fixtures/Bar.php: -------------------------------------------------------------------------------- 1 | id; 20 | } 21 | 22 | public function setId(?int $id): void 23 | { 24 | $this->id = $id; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Fixtures/BarGenerator.php: -------------------------------------------------------------------------------- 1 | value}_{$suffix}"; 21 | } 22 | 23 | public function getVirtualProperty( 24 | #[MapToContext('prefix')] string $prefix, 25 | #[MapToContext('suffix')] string $suffix, 26 | ): string { 27 | return "{$prefix}_{$this->value}_{$suffix}"; 28 | } 29 | 30 | public function getPropertyWithDefaultValue( 31 | string $someVar = 'foo', 32 | ): string { 33 | return $someVar; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Fixtures/ClassWithNullablePropertyInConstructor.php: -------------------------------------------------------------------------------- 1 | id; 20 | } 21 | 22 | public function setId(int $id): void 23 | { 24 | $this->id = $id; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Fixtures/FooGenerator.php: -------------------------------------------------------------------------------- 1 | 'bar'; 15 | } 16 | 17 | public function getArray(): array 18 | { 19 | return [1, 2, 3]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Fixtures/GroupOverride.php: -------------------------------------------------------------------------------- 1 | dateTime = new \DateTime('2024-01-01 00:00:00'); 15 | 16 | return $self; 17 | } 18 | 19 | public function getString(): ?string 20 | { 21 | return $this->dateTime->format(\DateTime::ATOM); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Fixtures/HasDateTimeImmutable.php: -------------------------------------------------------------------------------- 1 | dateTime = new \DateTimeImmutable('2024-01-01 00:00:00'); 15 | 16 | return $self; 17 | } 18 | 19 | public function getString(): ?string 20 | { 21 | return $this->dateTime->format(\DateTime::ATOM); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Fixtures/HasDateTimeImmutableWithNullValue.php: -------------------------------------------------------------------------------- 1 | dateTime?->format(\DateTime::ATOM); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Fixtures/HasDateTimeInterfaceWithImmutableInstance.php: -------------------------------------------------------------------------------- 1 | dateTime = new \DateTimeImmutable('2024-01-01 00:00:00'); 15 | 16 | return $self; 17 | } 18 | 19 | public function getString(): ?string 20 | { 21 | return $this->dateTime->format(\DateTime::ATOM); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Fixtures/HasDateTimeInterfaceWithMutableInstance.php: -------------------------------------------------------------------------------- 1 | dateTime = new \DateTime('2024-01-01 00:00:00'); 15 | 16 | return $self; 17 | } 18 | 19 | public function getString(): ?string 20 | { 21 | return $this->dateTime->format(\DateTime::ATOM); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Fixtures/HasDateTimeInterfaceWithNullValue.php: -------------------------------------------------------------------------------- 1 | dateTime?->format(\DateTime::ATOM); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Fixtures/HasDateTimeWithNullValue.php: -------------------------------------------------------------------------------- 1 | dateTime?->format(\DateTime::ATOM); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Fixtures/IntDTO.php: -------------------------------------------------------------------------------- 1 | b = $b; 32 | } 33 | 34 | public function getB(): string 35 | { 36 | return $this->b; 37 | } 38 | 39 | public function transformC(string $c, array $source, array $context): string 40 | { 41 | return 'transformC_' . $c; 42 | } 43 | 44 | public static function transformDStatic(string $c, array $source, array $context): string 45 | { 46 | return 'transformDStatic_' . $c; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Fixtures/MapTo/DateTimeFormatMapTo.php: -------------------------------------------------------------------------------- 1 | foo; 17 | } 18 | 19 | public function setFoo($foo) 20 | { 21 | $this->foo = $foo; 22 | } 23 | 24 | public function setBar($bar) 25 | { 26 | $this->bar = $bar; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Fixtures/Normalizer/DeepObjectPopulateParentDummy.php: -------------------------------------------------------------------------------- 1 | child = $child; 17 | } 18 | 19 | public function getChild(): ?DeepObjectPopulateChildDummy 20 | { 21 | return $this->child; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Fixtures/Normalizer/GroupDummyInterface.php: -------------------------------------------------------------------------------- 1 | kevin = $kevin; 18 | } 19 | 20 | public function getKevin() 21 | { 22 | return $this->kevin; 23 | } 24 | 25 | public function setCoopTilleuls($coopTilleuls) 26 | { 27 | $this->coopTilleuls = $coopTilleuls; 28 | } 29 | 30 | #[Groups(['a', 'b'])] 31 | public function getCoopTilleuls() 32 | { 33 | return $this->coopTilleuls; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Fixtures/Normalizer/MaxDepthDummy.php: -------------------------------------------------------------------------------- 1 | bar; 25 | } 26 | 27 | public function getChild() 28 | { 29 | return $this->child; 30 | } 31 | 32 | public function getFoo() 33 | { 34 | return $this->foo; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Fixtures/Normalizer/ProxyDummy.php: -------------------------------------------------------------------------------- 1 | foo = $foo; 14 | } 15 | 16 | public function getFoo() 17 | { 18 | return $this->foo; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Fixtures/ObjectWithDateTime.php: -------------------------------------------------------------------------------- 1 | dateTime = $dateTime; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Fixtures/Order.php: -------------------------------------------------------------------------------- 1 | Cat::class, 11 | 'dog' => Dog::class, 12 | 'fish' => Fish::class, 13 | ])] 14 | abstract class Pet 15 | { 16 | /** @var string */ 17 | public $type; 18 | 19 | /** @var string */ 20 | public $name; 21 | 22 | /** @var PetOwner */ 23 | public $owner; 24 | } 25 | -------------------------------------------------------------------------------- /tests/Fixtures/PetOwner.php: -------------------------------------------------------------------------------- 1 | */ 10 | private $pets; 11 | 12 | public function __construct() 13 | { 14 | $this->pets = []; 15 | } 16 | 17 | /** 18 | * @return Pet[] 19 | */ 20 | public function getPets(): array 21 | { 22 | return $this->pets; 23 | } 24 | 25 | public function addPet(Pet $pet): void 26 | { 27 | $this->pets[] = $pet; 28 | } 29 | 30 | public function removePet(Pet $pet): void 31 | { 32 | $index = array_search($pet, $this->pets); 33 | 34 | if ($index !== false) { 35 | unset($this->pets[$index]); 36 | $this->pets = array_values($this->pets); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Fixtures/PetOwnerWithConstructorArguments.php: -------------------------------------------------------------------------------- 1 | */ 10 | private $pets; 11 | 12 | public function __construct(array $pets) 13 | { 14 | $this->pets = $pets; 15 | } 16 | 17 | /** 18 | * @return Pet[] 19 | */ 20 | public function getPets(): array 21 | { 22 | return $this->pets; 23 | } 24 | 25 | public function addPet(Pet $pet): void 26 | { 27 | $this->pets[] = $pet; 28 | } 29 | 30 | public function removePet(Pet $pet): void 31 | { 32 | $index = array_search($pet, $this->pets); 33 | 34 | if ($index !== false) { 35 | unset($this->pets[$index]); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Fixtures/Private_.php: -------------------------------------------------------------------------------- 1 | id = $id; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Fixtures/Proxy.php: -------------------------------------------------------------------------------- 1 | source === UserDTO::class && $mapperMetadata->target === 'array' && $source->property === 'name'; 20 | } 21 | 22 | public function transform(mixed $value, object|array $source, array $context): mixed 23 | { 24 | return "{$source->getName()} set by custom property transformer"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Fixtures/Transformer/CustomTransformer/FromTargetCustomPropertyTransformer.php: -------------------------------------------------------------------------------- 1 | source === 'array' && $mapperMetadata->target === UserDTO::class && $source->property === 'name'; 20 | } 21 | 22 | public function transform(mixed $value, object|array $source, array $context): mixed 23 | { 24 | return "{$source['name']} from custom property transformer"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Fixtures/Transformer/CustomTransformer/SourceTargetCustomPropertyTransformer.php: -------------------------------------------------------------------------------- 1 | source === UserDTO::class && $mapperMetadata->target === User::class && $source->property === 'name'; 21 | } 22 | 23 | public function transform(mixed $value, object|array $source, array $context): mixed 24 | { 25 | return "{$source->getName()} from custom property transformer"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Fixtures/Transformer/CustomTransformer/SourceTargetMultiFieldsCustomPropertyTransformer.php: -------------------------------------------------------------------------------- 1 | source === BirthDateExploded::class && $mapperMetadata->target === BirthDateDateTime::class && $target->property === 'date'; 21 | } 22 | 23 | public function transform(mixed $value, array|object $source, array $context): \DateTimeImmutable 24 | { 25 | return new \DateTimeImmutable("{$source->year}-{$source->month}-{$source->day}"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Fixtures/Transformer/CustomTransformer/TransformerWithDependency.php: -------------------------------------------------------------------------------- 1 | source === CityFoo::class && $mapperMetadata->target === 'array' && $source->property === 'name'; 25 | } 26 | 27 | public function transform(mixed $value, object|array $source, array $context): string 28 | { 29 | return $this->fooDependency->getBar(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Fixtures/Uninitialized.php: -------------------------------------------------------------------------------- 1 | id = $id; 31 | $this->name = $name; 32 | $this->age = $age; 33 | $this->constructor = true; 34 | } 35 | 36 | /** 37 | * @return int 38 | */ 39 | public function getId(): string 40 | { 41 | return $this->id; 42 | } 43 | 44 | public function getName(): ?string 45 | { 46 | return $this->name; 47 | } 48 | 49 | /** 50 | * @return int|null 51 | */ 52 | public function getAge() 53 | { 54 | return $this->age; 55 | } 56 | 57 | public function getConstructor(): bool 58 | { 59 | return $this->constructor; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/Fixtures/UserDTO.php: -------------------------------------------------------------------------------- 1 | name = $name; 69 | } 70 | 71 | public function getName() 72 | { 73 | return $this->name; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/Fixtures/UserDTOMerged.php: -------------------------------------------------------------------------------- 1 | properties; 17 | } 18 | 19 | public function setProperties(iterable $properties): void 20 | { 21 | $this->properties = $properties; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Fixtures/UserDTONoAge.php: -------------------------------------------------------------------------------- 1 | properties; 17 | } 18 | 19 | public function setProperties(iterable $properties): void 20 | { 21 | $this->properties = $properties; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Fixtures/UserPartialConstructor.php: -------------------------------------------------------------------------------- 1 | id = $id; 27 | $this->name = $name; 28 | } 29 | 30 | public function getId(): int 31 | { 32 | return $this->id; 33 | } 34 | 35 | /** 36 | * @param int|string $age 37 | */ 38 | public function setAge($age): void 39 | { 40 | $this->age = $age; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/Fixtures/UserWithYearOfBirth.php: -------------------------------------------------------------------------------- 1 | id = $id; 32 | $this->name = $name; 33 | $this->age = $age; 34 | } 35 | 36 | #[Groups('read')] 37 | public function getYearOfBirth() 38 | { 39 | return ((int) date('Y')) - ((int) $this->age); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Fixtures/WrongParameters.php: -------------------------------------------------------------------------------- 1 | setParameters($parameters); 18 | } 19 | 20 | /** 21 | * @return string[]|null 22 | */ 23 | public function getParameters(): array 24 | { 25 | return $this->parameters; 26 | } 27 | 28 | /** 29 | * @param string[]|null $parameters 30 | */ 31 | public function setParameters(array $parameters): void 32 | { 33 | $this->parameters = $parameters; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Fixtures/proxies.php: -------------------------------------------------------------------------------- 1 | getUniqueName('value'); 16 | $var2 = $uniqueVariable->getUniqueName('value'); 17 | $var3 = $uniqueVariable->getUniqueName('VALUE'); 18 | 19 | self::assertNotSame($var1, $var2); 20 | self::assertNotSame($var1, $var3); 21 | self::assertNotSame($var2, $var3); 22 | self::assertNotSame(strtolower($var1), strtolower($var3)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Normalizer/Features/CallbacksObject.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AutoMapper\Tests\Normalizer\Features; 15 | 16 | class CallbacksObject 17 | { 18 | public $bar; 19 | 20 | /** 21 | * @var string|null 22 | */ 23 | public $foo; 24 | 25 | public function __construct($bar = null, ?string $foo = null) 26 | { 27 | $this->bar = $bar; 28 | $this->foo = $foo; 29 | } 30 | 31 | public function getBar() 32 | { 33 | return $this->bar; 34 | } 35 | 36 | public function setBar($bar) 37 | { 38 | $this->bar = $bar; 39 | } 40 | 41 | public function getFoo(): ?string 42 | { 43 | return $this->foo; 44 | } 45 | 46 | public function setFoo(?string $foo) 47 | { 48 | $this->foo = $foo; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Normalizer/Features/CircularReferenceDummy.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AutoMapper\Tests\Normalizer\Features; 15 | 16 | class ConstructorArgumentsObject 17 | { 18 | private $foo; 19 | private $bar; 20 | private $baz; 21 | 22 | public function __construct(int $foo, $bar, $baz) 23 | { 24 | $this->foo = $foo; 25 | $this->bar = $bar; 26 | $this->baz = $baz; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Normalizer/Features/DummyContextChild.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AutoMapper\Tests\Normalizer\Features; 15 | 16 | use Symfony\Component\Serializer\Attribute\Context; 17 | 18 | #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)] 19 | class DummyContextChild extends Context 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /tests/Normalizer/Features/ObjectDummyWithContextAttribute.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AutoMapper\Tests\Normalizer\Features; 15 | 16 | use Symfony\Component\Serializer\Attribute\Context; 17 | use Symfony\Component\Serializer\Attribute\SerializedName; 18 | use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; 19 | 20 | final class ObjectDummyWithContextAttribute 21 | { 22 | public function __construct( 23 | #[Context([DateTimeNormalizer::FORMAT_KEY => 'm-d-Y'])] 24 | #[SerializedName('property_with_serialized_name')] 25 | public \DateTimeImmutable $propertyWithSerializedName, 26 | #[Context([DateTimeNormalizer::FORMAT_KEY => 'm-d-Y'])] 27 | public \DateTimeImmutable $propertyWithoutSerializedName, 28 | ) { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Normalizer/Features/ObjectInner.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AutoMapper\Tests\Normalizer\Features; 15 | 16 | class ObjectInner 17 | { 18 | public $foo; 19 | public $bar; 20 | 21 | public function getBar() 22 | { 23 | return $this->bar; 24 | } 25 | 26 | public function setBar($bar): void 27 | { 28 | $this->bar = $bar; 29 | } 30 | 31 | public function getFoo() 32 | { 33 | return $this->foo; 34 | } 35 | 36 | public function setFoo($foo): void 37 | { 38 | $this->foo = $foo; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Normalizer/Features/SkipNullValuesTestTrait.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AutoMapper\Tests\Normalizer\Features; 15 | 16 | use AutoMapper\MapperContext; 17 | use Symfony\Component\Serializer\Normalizer\NormalizerInterface; 18 | 19 | /** 20 | * Test AbstractObjectNormalizer::SKIP_NULL_VALUES. 21 | */ 22 | trait SkipNullValuesTestTrait 23 | { 24 | abstract protected function getNormalizerForSkipNullValues(): NormalizerInterface; 25 | 26 | public function testSkipNullValues() 27 | { 28 | $dummy = new ObjectDummy(); 29 | $dummy->bar = 'present'; 30 | 31 | $normalizer = $this->getNormalizerForSkipNullValues(); 32 | $result = $normalizer->normalize($dummy, null, [MapperContext::SKIP_NULL_VALUES => true]); 33 | $this->assertSame(['bar' => 'present', 'fooBar' => 'present'], $result); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Normalizer/Features/TypeEnforcementNumberObject.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AutoMapper\Tests\Normalizer\Features; 15 | 16 | class TypeEnforcementNumberObject 17 | { 18 | /** 19 | * @var float 20 | */ 21 | public $number; 22 | 23 | public function setNumber($number) 24 | { 25 | $this->number = $number; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Normalizer/Features/TypedPropertiesObject.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AutoMapper\Tests\Normalizer\Features; 15 | 16 | use Symfony\Component\Serializer\Attribute\Groups; 17 | 18 | class TypedPropertiesObject 19 | { 20 | #[Groups(['foo'])] 21 | public string $unInitialized; 22 | 23 | #[Groups(['foo'])] 24 | public string $initialized = 'value'; 25 | 26 | #[Groups(['bar'])] 27 | public string $initialized2 = 'value'; 28 | } 29 | -------------------------------------------------------------------------------- /tests/Normalizer/Features/TypedPropertiesObjectWithGetters.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace AutoMapper\Tests\Normalizer\Features; 15 | 16 | class TypedPropertiesObjectWithGetters extends TypedPropertiesObject 17 | { 18 | public function getUnInitialized(): string 19 | { 20 | return $this->unInitialized; 21 | } 22 | 23 | public function setUnInitialized(string $unInitialized): self 24 | { 25 | $this->unInitialized = $unInitialized; 26 | 27 | return $this; 28 | } 29 | 30 | public function getInitialized(): string 31 | { 32 | return $this->initialized; 33 | } 34 | 35 | public function setInitialized(string $initialized): self 36 | { 37 | $this->initialized = $initialized; 38 | 39 | return $this; 40 | } 41 | 42 | public function getInitialized2(): string 43 | { 44 | return $this->initialized2; 45 | } 46 | 47 | public function setInitialized2(string $initialized2): self 48 | { 49 | $this->initialized2 = $initialized2; 50 | 51 | return $this; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Transformer/ArrayTransformerTest.php: -------------------------------------------------------------------------------- 1 | evalTransformer($transformer, ['test']); 20 | 21 | self::assertEquals(['test'], $output); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/Transformer/CopyTransformerTest.php: -------------------------------------------------------------------------------- 1 | evalTransformer($transformer, 'foo'); 19 | 20 | self::assertSame('foo', $output); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Transformer/DateTimeInterfaceToImmutableTransformerTest.php: -------------------------------------------------------------------------------- 1 | evalTransformer($transformer, $date); 20 | 21 | self::assertInstanceOf(\DateTimeImmutable::class, $output); 22 | self::assertSame($date->format(\DateTime::RFC3339), $output->format(\DateTime::RFC3339)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Transformer/DateTimeInterfaceToMutableTransformerTest.php: -------------------------------------------------------------------------------- 1 | evalTransformer($transformer, $date); 20 | 21 | self::assertInstanceOf(\DateTime::class, $output); 22 | self::assertSame($date->format(\DateTime::RFC3339), $output->format(\DateTime::RFC3339)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Transformer/DateTimeToStringTransformerTest.php: -------------------------------------------------------------------------------- 1 | evalTransformer($transformer, new \DateTime()); 20 | 21 | self::assertSame($date->format(\DateTime::RFC3339), $output); 22 | } 23 | 24 | public function testDateTimeTransformerCustomFormat(): void 25 | { 26 | $transformer = new DateTimeToStringTransformer(\DateTime::COOKIE); 27 | 28 | $date = new \DateTime(); 29 | $output = $this->evalTransformer($transformer, new \DateTime()); 30 | 31 | self::assertSame($date->format(\DateTime::COOKIE), $output); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Transformer/MultipleTransformerTest.php: -------------------------------------------------------------------------------- 1 | new BuiltinTransformer(new Type('string'), [new Type('int')]), 21 | 'type' => new Type('string'), 22 | ], 23 | [ 24 | 'transformer' => new BuiltinTransformer(new Type('int'), [new Type('string')]), 25 | 'type' => new Type('int'), 26 | ], 27 | ]); 28 | 29 | $output = $this->evalTransformer($transformer, '12'); 30 | 31 | self::assertSame(12, $output); 32 | 33 | $output = $this->evalTransformer($transformer, 12); 34 | 35 | self::assertSame('12', $output); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Transformer/NullableTransformerTest.php: -------------------------------------------------------------------------------- 1 | evalTransformer($transformer, 'foo'); 21 | 22 | self::assertSame('foo', $output); 23 | 24 | $output = $this->evalTransformer($transformer, null); 25 | 26 | self::assertNull($output); 27 | } 28 | 29 | public function testNullTransformerTargetNotNullable(): void 30 | { 31 | $transformer = new NullableTransformer(new BuiltinTransformer(new Type('string'), [new Type('string')]), false); 32 | 33 | $output = $this->evalTransformer($transformer, 'foo'); 34 | 35 | self::assertSame('foo', $output); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Transformer/ObjectTransformerTest.php: -------------------------------------------------------------------------------- 1 | createTransformerFunction($transformer); 20 | $class = new class() { 21 | public $mappers; 22 | 23 | public function __construct() 24 | { 25 | $this->mappers['Mapper_' . Foo::class . '_' . Foo::class] = new class() { 26 | public function map() 27 | { 28 | return new Foo(); 29 | } 30 | }; 31 | } 32 | }; 33 | 34 | $transform = \Closure::bind($function, $class); 35 | $output = $transform(new Foo()); 36 | 37 | self::assertNotNull($output); 38 | self::assertInstanceOf(Foo::class, $output); 39 | } 40 | } 41 | 42 | class Foo 43 | { 44 | public string $bar; 45 | } 46 | --------------------------------------------------------------------------------