From cb36ec0d09b5cd3cc3ae390e6ae5ea51c556c8af Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Fri, 17 Jan 2025 09:45:41 +0100 Subject: [PATCH] [JsonEncoder] Allow to warm up item and list --- .../FrameworkExtension.php | 7 +++++-- .../Tests/Functional/JsonEncoderTest.php | 2 +- .../JsonEncoder/Attribute/JsonEncodable.php | 5 +++++ .../Component/JsonEncoder/CHANGELOG.md | 1 + .../CacheWarmer/EncoderDecoderCacheWarmer.php | 21 +++++++++++++------ .../DependencyInjection/EncodablePass.php | 15 +++++++------ .../EncoderDecoderCacheWarmerTest.php | 19 ++++++++++++++--- .../DependencyInjection/EncodablePassTest.php | 10 ++++----- 8 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 6d20ca653e668..3ef5de07097bb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -746,8 +746,11 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu } ); } - $container->registerAttributeForAutoconfiguration(JsonEncodable::class, static function (ChildDefinition $definition): void { - $definition->addTag('json_encoder.encodable'); + $container->registerAttributeForAutoconfiguration(JsonEncodable::class, static function (ChildDefinition $definition, JsonEncodable $attribute): void { + $definition->addTag('json_encoder.encodable', [ + 'object' => $attribute->asObject, + 'list' => $attribute->asList, + ]); $definition->addTag('container.excluded'); }); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonEncoderTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonEncoderTest.php index 93ca1fd6d7a23..b5410e1e1127b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonEncoderTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonEncoderTest.php @@ -62,6 +62,6 @@ public function testWarmupEncodableClasses() static::getContainer()->get('json_encoder.cache_warmer.encoder_decoder.alias')->warmUp(static::getContainer()->getParameter('kernel.cache_dir')); $this->assertFileExists($encodersDir); - $this->assertCount(1, glob($encodersDir.'/*')); + $this->assertCount(2, glob($encodersDir.'/*')); } } diff --git a/src/Symfony/Component/JsonEncoder/Attribute/JsonEncodable.php b/src/Symfony/Component/JsonEncoder/Attribute/JsonEncodable.php index f370009d2dcdf..b03346be2e36b 100644 --- a/src/Symfony/Component/JsonEncoder/Attribute/JsonEncodable.php +++ b/src/Symfony/Component/JsonEncoder/Attribute/JsonEncodable.php @@ -19,4 +19,9 @@ #[\Attribute(\Attribute::TARGET_CLASS)] final class JsonEncodable { + public function __construct( + public bool $asObject = true, + public bool $asList = true, + ) { + } } diff --git a/src/Symfony/Component/JsonEncoder/CHANGELOG.md b/src/Symfony/Component/JsonEncoder/CHANGELOG.md index 327d5f6cec3ef..6cc6bc5f6b52b 100644 --- a/src/Symfony/Component/JsonEncoder/CHANGELOG.md +++ b/src/Symfony/Component/JsonEncoder/CHANGELOG.md @@ -6,3 +6,4 @@ CHANGELOG * Introduce the component as experimental * Add native PHP lazy ghost support + * Allow to select the warmup of object and list in `JsonEncodable` and `json_encoder.encodable` diff --git a/src/Symfony/Component/JsonEncoder/CacheWarmer/EncoderDecoderCacheWarmer.php b/src/Symfony/Component/JsonEncoder/CacheWarmer/EncoderDecoderCacheWarmer.php index a01bb63794bfd..5ea39b3b96d04 100644 --- a/src/Symfony/Component/JsonEncoder/CacheWarmer/EncoderDecoderCacheWarmer.php +++ b/src/Symfony/Component/JsonEncoder/CacheWarmer/EncoderDecoderCacheWarmer.php @@ -33,10 +33,10 @@ final class EncoderDecoderCacheWarmer implements CacheWarmerInterface private DecoderGenerator $decoderGenerator; /** - * @param iterable $encodableClassNames + * @param iterable $encodable */ public function __construct( - private iterable $encodableClassNames, + private iterable $encodable, PropertyMetadataLoaderInterface $encodePropertyMetadataLoader, PropertyMetadataLoaderInterface $decodePropertyMetadataLoader, string $encodersDir, @@ -49,11 +49,20 @@ public function __construct( public function warmUp(string $cacheDir, ?string $buildDir = null): array { - foreach ($this->encodableClassNames as $className) { - $type = Type::object($className); + foreach ($this->encodable as $className => $encodable) { + if ($encodable['object']) { + $type = Type::object($className); - $this->warmUpEncoder($type); - $this->warmUpDecoders($type); + $this->warmUpEncoder($type); + $this->warmUpDecoders($type); + } + + if ($encodable['list']) { + $type = Type::list(Type::object($className)); + + $this->warmUpEncoder($type); + $this->warmUpDecoders($type); + } } return []; diff --git a/src/Symfony/Component/JsonEncoder/DependencyInjection/EncodablePass.php b/src/Symfony/Component/JsonEncoder/DependencyInjection/EncodablePass.php index 47fcd8940d1ea..7d0f2a3b27c45 100644 --- a/src/Symfony/Component/JsonEncoder/DependencyInjection/EncodablePass.php +++ b/src/Symfony/Component/JsonEncoder/DependencyInjection/EncodablePass.php @@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; /** - * Sets the encodable classes to the services that need them. + * Sets the encodable metadata to the services that need them. * * @author Mathias Arlaud */ @@ -27,27 +27,30 @@ public function process(ContainerBuilder $container): void return; } - $encodableClassNames = []; + $encodable = []; // retrieve concrete services tagged with "json_encoder.encodable" tag foreach ($container->getDefinitions() as $id => $definition) { - if (!$definition->hasTag('json_encoder.encodable')) { + if (!$tag = ($definition->getTag('json_encoder.encodable')[0] ?? null)) { continue; } if (($className = $container->getDefinition($id)->getClass()) && !$container->getDefinition($id)->isAbstract()) { - $encodableClassNames[] = $className; + $encodable[$className] = [ + 'object' => $tag['object'], + 'list' => $tag['list'], + ]; } $container->removeDefinition($id); } $container->getDefinition('.json_encoder.cache_warmer.encoder_decoder') - ->replaceArgument(0, $encodableClassNames); + ->replaceArgument(0, $encodable); if ($container->hasDefinition('.json_encoder.cache_warmer.lazy_ghost')) { $container->getDefinition('.json_encoder.cache_warmer.lazy_ghost') - ->replaceArgument(0, $encodableClassNames); + ->replaceArgument(0, array_keys($encodable)); } } } diff --git a/src/Symfony/Component/JsonEncoder/Tests/CacheWarmer/EncoderDecoderCacheWarmerTest.php b/src/Symfony/Component/JsonEncoder/Tests/CacheWarmer/EncoderDecoderCacheWarmerTest.php index d0df4df12d5ce..e0882365ba521 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/CacheWarmer/EncoderDecoderCacheWarmerTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/CacheWarmer/EncoderDecoderCacheWarmerTest.php @@ -15,6 +15,7 @@ use Symfony\Component\JsonEncoder\CacheWarmer\EncoderDecoderCacheWarmer; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy; +use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes; use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; class EncoderDecoderCacheWarmerTest extends TestCase @@ -42,24 +43,36 @@ protected function setUp(): void public function testWarmUp() { - $this->cacheWarmer()->warmUp('useless'); + $this->cacheWarmer([ + ClassicDummy::class => ['object' => true, 'list' => true], + DummyWithNameAttributes::class => ['object' => true, 'list' => false], + ])->warmUp('useless'); $this->assertSame([ + \sprintf('%s/5acb3571777e02a2712fb9a9126a338f.json.php', $this->encodersDir), \sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.php', $this->encodersDir), + \sprintf('%s/de878efdd0bf652bdd72d1dc95f6d80d.json.php', $this->encodersDir), ], glob($this->encodersDir.'/*')); $this->assertSame([ + \sprintf('%s/5acb3571777e02a2712fb9a9126a338f.json.php', $this->decodersDir), + \sprintf('%s/5acb3571777e02a2712fb9a9126a338f.json.stream.php', $this->decodersDir), \sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.php', $this->decodersDir), \sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.stream.php', $this->decodersDir), + \sprintf('%s/de878efdd0bf652bdd72d1dc95f6d80d.json.php', $this->decodersDir), + \sprintf('%s/de878efdd0bf652bdd72d1dc95f6d80d.json.stream.php', $this->decodersDir), ], glob($this->decodersDir.'/*')); } - private function cacheWarmer(): EncoderDecoderCacheWarmer + /** + * @param array $encodableClasses + */ + private function cacheWarmer(array $encodableClasses = []): EncoderDecoderCacheWarmer { $typeResolver = TypeResolver::create(); return new EncoderDecoderCacheWarmer( - [ClassicDummy::class], + $encodableClasses, new PropertyMetadataLoader($typeResolver), new PropertyMetadataLoader($typeResolver), $this->encodersDir, diff --git a/src/Symfony/Component/JsonEncoder/Tests/DependencyInjection/EncodablePassTest.php b/src/Symfony/Component/JsonEncoder/Tests/DependencyInjection/EncodablePassTest.php index 55ad1c036a130..36a7938e66b5a 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/DependencyInjection/EncodablePassTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/DependencyInjection/EncodablePassTest.php @@ -25,8 +25,8 @@ public function testSetEncodableClassNames() $container->register('.json_encoder.cache_warmer.encoder_decoder')->setArguments([null]); $container->register('.json_encoder.cache_warmer.lazy_ghost')->setArguments([null]); - $container->register('encodable')->setClass('Foo')->addTag('json_encoder.encodable'); - $container->register('abstractEncodable')->setClass('Bar')->addTag('json_encoder.encodable')->setAbstract(true); + $container->register('encodable')->setClass('Foo')->addTag('json_encoder.encodable', ['object' => true, 'list' => true]); + $container->register('abstractEncodable')->setClass('Bar')->addTag('json_encoder.encodable', ['object' => true, 'list' => true])->setAbstract(true); $container->register('notEncodable')->setClass('Baz'); $pass = new EncodablePass(); @@ -35,9 +35,7 @@ public function testSetEncodableClassNames() $encoderDecoderCacheWarmer = $container->getDefinition('.json_encoder.cache_warmer.encoder_decoder'); $lazyGhostCacheWarmer = $container->getDefinition('.json_encoder.cache_warmer.lazy_ghost'); - $expectedEncodableClassNames = ['Foo']; - - $this->assertSame($expectedEncodableClassNames, $encoderDecoderCacheWarmer->getArgument(0)); - $this->assertSame($expectedEncodableClassNames, $lazyGhostCacheWarmer->getArgument(0)); + $this->assertSame(['Foo' => ['object' => true, 'list' => true]], $encoderDecoderCacheWarmer->getArgument(0)); + $this->assertSame(['Foo'], $lazyGhostCacheWarmer->getArgument(0)); } }