From 8afc8111957d564ae92bd2e4eb24631bed2396e1 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Tue, 14 May 2024 08:59:07 +0200 Subject: [PATCH] fix(bundle): use map_private_properties when configuring ReflectionExtractor --- .../AutoMapperExtension.php | 2 + .../Compiler/PropertyInfoPass.php | 8 ++- tests/Bundle/Resources/App/AppKernel.php | 25 +++++++++ .../config/with-private-properties.yml | 3 ++ tests/Bundle/ServiceInstantiationTest.php | 51 +++++++++++++++---- 5 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 tests/Bundle/Resources/config/with-private-properties.yml diff --git a/src/Symfony/Bundle/DependencyInjection/AutoMapperExtension.php b/src/Symfony/Bundle/DependencyInjection/AutoMapperExtension.php index c2d619ed..a26b92a7 100644 --- a/src/Symfony/Bundle/DependencyInjection/AutoMapperExtension.php +++ b/src/Symfony/Bundle/DependencyInjection/AutoMapperExtension.php @@ -67,6 +67,8 @@ public function load(array $configs, ContainerBuilder $container): void ->setArgument('$allowReadOnlyTargetToPopulate', $config['allow_readonly_target_to_populate']) ; + $container->setParameter('automapper.map_private_properties', $config['map_private_properties']); + $container->registerForAutoconfiguration(PropertyTransformerInterface::class)->addTag('automapper.property_transformer'); if ($config['loader']['eval']) { diff --git a/src/Symfony/Bundle/DependencyInjection/Compiler/PropertyInfoPass.php b/src/Symfony/Bundle/DependencyInjection/Compiler/PropertyInfoPass.php index 98c08ea5..3237bcb2 100644 --- a/src/Symfony/Bundle/DependencyInjection/Compiler/PropertyInfoPass.php +++ b/src/Symfony/Bundle/DependencyInjection/Compiler/PropertyInfoPass.php @@ -22,6 +22,12 @@ public function process(ContainerBuilder $container): void return; } + $flags = ReflectionExtractor::ALLOW_PUBLIC; + + if ($container->getParameter('automapper.map_private_properties')) { + $flags |= ReflectionExtractor::ALLOW_PRIVATE | ReflectionExtractor::ALLOW_PROTECTED; + } + $container->setDefinition( 'automapper.property_info.reflection_extractor', new Definition( @@ -31,7 +37,7 @@ public function process(ContainerBuilder $container): void '$accessorPrefixes' => null, '$arrayMutatorPrefixes' => null, '$enableConstructorExtraction' => true, - '$accessFlags' => ReflectionExtractor::ALLOW_PUBLIC | ReflectionExtractor::ALLOW_PROTECTED | ReflectionExtractor::ALLOW_PRIVATE, + '$accessFlags' => $flags, ] ) ); diff --git a/tests/Bundle/Resources/App/AppKernel.php b/tests/Bundle/Resources/App/AppKernel.php index 44ddd041..980fec0c 100644 --- a/tests/Bundle/Resources/App/AppKernel.php +++ b/tests/Bundle/Resources/App/AppKernel.php @@ -5,14 +5,39 @@ namespace AutoMapper\Tests\Bundle\Resources\App; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Kernel; class AppKernel extends Kernel { use MicroKernelTrait; + public function __construct( + string $environment, + bool $debug, + private ?string $additionalConfigFile = null + ) { + parent::__construct($environment, $debug); + } + + protected function buildContainer(): ContainerBuilder + { + $containerBuilder = parent::buildContainer(); + + if ($this->additionalConfigFile) { + $this->getContainerLoader($containerBuilder)->load($this->additionalConfigFile); + } + + return $containerBuilder; + } + public function getProjectDir(): string { return __DIR__ . '/..'; } + + public function getCacheDir(): string + { + return parent::getCacheDir() . '/' . md5($this->additionalConfigFile ?? ''); + } } diff --git a/tests/Bundle/Resources/config/with-private-properties.yml b/tests/Bundle/Resources/config/with-private-properties.yml new file mode 100644 index 00000000..aaf31d60 --- /dev/null +++ b/tests/Bundle/Resources/config/with-private-properties.yml @@ -0,0 +1,3 @@ +automapper: + class_prefix: "Symfony_Mapper_With_Private_Property" + map_private_properties: true diff --git a/tests/Bundle/ServiceInstantiationTest.php b/tests/Bundle/ServiceInstantiationTest.php index e2fee812..69e90ec9 100644 --- a/tests/Bundle/ServiceInstantiationTest.php +++ b/tests/Bundle/ServiceInstantiationTest.php @@ -11,6 +11,7 @@ use AutoMapper\Metadata\TargetPropertyMetadata; use AutoMapper\Symfony\Bundle\CacheWarmup\CacheWarmer; use AutoMapper\Symfony\Bundle\DataCollector\MetadataCollector; +use AutoMapper\Tests\Bundle\Resources\App\AppKernel; use AutoMapper\Tests\Bundle\Resources\App\Entity\AddressDTO; use AutoMapper\Tests\Bundle\Resources\App\Entity\ClassWithMapToContextAttribute; use AutoMapper\Tests\Bundle\Resources\App\Entity\ClassWithPrivateProperty; @@ -23,15 +24,14 @@ use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\KernelInterface; class ServiceInstantiationTest extends WebTestCase { - protected function setUp(): void + public static function setUpBeforeClass(): void { static::$class = null; $_SERVER['KERNEL_DIR'] = __DIR__ . '/Resources/App'; - $_SERVER['KERNEL_CLASS'] = 'AutoMapper\Tests\Bundle\Resources\App\AppKernel'; - $_SERVER['APP_DEBUG'] = false; (new Filesystem())->remove(__DIR__ . '/Resources/var/cache/test'); } @@ -47,13 +47,14 @@ public function testWarmup(): void $service = static::$kernel->getContainer()->get(CacheWarmer::class); $service->warmUp(__DIR__ . '/Resources/var/cache/test'); - self::assertFileExists(__DIR__ . '/Resources/var/cache/test/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_NestedObject_array.php'); - self::assertFileExists(__DIR__ . '/Resources/var/cache/test/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_User_array.php'); - self::assertFileExists(__DIR__ . '/Resources/var/cache/test/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_AddressDTO_array.php'); - self::assertFileExists(__DIR__ . '/Resources/var/cache/test/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_Pet_array.php'); - self::assertFileExists(__DIR__ . '/Resources/var/cache/test/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_Dog_array.php'); - self::assertFileExists(__DIR__ . '/Resources/var/cache/test/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_Cat_array.php'); - self::assertFileExists(__DIR__ . '/Resources/var/cache/test/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Api_Entity_Book_array.php'); + $cacheDir = static::$kernel->getCacheDir(); + self::assertFileExists("{$cacheDir}/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_NestedObject_array.php"); + self::assertFileExists("{$cacheDir}/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_User_array.php"); + self::assertFileExists("{$cacheDir}/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_AddressDTO_array.php"); + self::assertFileExists("{$cacheDir}/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_Pet_array.php"); + self::assertFileExists("{$cacheDir}/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_Dog_array.php"); + self::assertFileExists("{$cacheDir}/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Entity_Cat_array.php"); + self::assertFileExists("{$cacheDir}/automapper/Symfony_Mapper_AutoMapper_Tests_Bundle_Resources_App_Api_Entity_Book_array.php"); } public function testAutoMapper(): void @@ -121,9 +122,10 @@ public function testItCanMapEnums(): void /** * This test validates that PropertyInfoPass is correctly applied. */ - public function testMapClassWithPrivateProperty(): void + public function testMapToClassWithPrivateProperty(): void { static::bootKernel(); + $container = static::$kernel->getContainer(); $autoMapper = $container->get(AutoMapperInterface::class); @@ -133,6 +135,28 @@ public function testMapClassWithPrivateProperty(): void ); } + /** + * This test validates that PropertyInfoPass is correctly applied. + * + * @dataProvider mapFromClassWithPrivatePropertyProvider + */ + public function testMapFromClassWithPrivateProperty(array $kernelOptions, array $expected): void + { + static::bootKernel($kernelOptions); + $autoMapper = self::getContainer()->get(AutoMapperInterface::class); + + self::assertEquals( + $expected, + $autoMapper->map(new ClassWithPrivateProperty('foo'), 'array') + ); + } + + public static function mapFromClassWithPrivatePropertyProvider(): iterable + { + yield 'disallow private properties' => [[], []]; + yield 'allow private properties' => [['additionalConfigFile' => __DIR__ . '/Resources/config/with-private-properties.yml'], ['foo' => 'foo', 'bar' => 'bar']]; + } + /** * We need to test that the mapToContext attribute is correctly used, * because this behavior is dependent of the dependency injection. @@ -253,4 +277,9 @@ public function getData(): array } } } + + protected static function createKernel(array $options = []): KernelInterface + { + return new AppKernel('test', false, $options['additionalConfigFile'] ?? null); + } }