From a12ad34fa6c5de402e0fb79016f9978281524a85 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Tue, 7 Jan 2025 15:21:25 +0100 Subject: [PATCH] [JsonEncoder] Add `JsonEncodable` attribute --- .../Compiler/UnusedTagsPass.php | 1 - .../FrameworkExtension.php | 5 ++++ .../Tests/Functional/JsonEncoderTest.php | 26 ++++++++++++++++--- .../Functional/app/JsonEncoder/Dto/Dummy.php | 2 ++ .../Functional/app/JsonEncoder/config.yml | 5 ++++ .../JsonEncoder/Attribute/JsonEncodable.php | 22 ++++++++++++++++ .../DependencyInjection/EncodablePass.php | 6 ++++- 7 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 src/Symfony/Component/JsonEncoder/Attribute/JsonEncodable.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php index a2a571f834be0..b6a4c8a7cf1bb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -54,7 +54,6 @@ class UnusedTagsPass implements CompilerPassInterface 'html_sanitizer', 'http_client.client', 'json_encoder.denormalizer', - 'json_encoder.encodable', 'json_encoder.normalizer', 'kernel.cache_clearer', 'kernel.cache_warmer', diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 38d37c7fc1990..6d20ca653e668 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -100,6 +100,7 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator; +use Symfony\Component\JsonEncoder\Attribute\JsonEncodable; use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface as JsonEncoderDenormalizerInterface; use Symfony\Component\JsonEncoder\DecoderInterface as JsonEncoderDecoderInterface; use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface as JsonEncoderNormalizerInterface; @@ -745,6 +746,10 @@ static function (ChildDefinition $definition, AsPeriodicTask|AsCronTask $attribu } ); } + $container->registerAttributeForAutoconfiguration(JsonEncodable::class, static function (ChildDefinition $definition): void { + $definition->addTag('json_encoder.encodable'); + $definition->addTag('container.excluded'); + }); if (!$container->getParameter('kernel.debug')) { // remove tagged iterator argument for resource checkers diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonEncoderTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonEncoderTest.php index 0ab66e6c1830f..93ca1fd6d7a23 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonEncoderTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonEncoderTest.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\Dummy; +use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\JsonEncoder\DecoderInterface; use Symfony\Component\JsonEncoder\EncoderInterface; use Symfony\Component\TypeInfo\Type; @@ -21,10 +22,13 @@ */ class JsonEncoderTest extends AbstractWebTestCase { - public function testEncode() + protected function setUp(): void { static::bootKernel(['test_case' => 'JsonEncoder']); + } + public function testEncode() + { /** @var EncoderInterface $encoder */ $encoder = static::getContainer()->get('json_encoder.encoder.alias'); @@ -33,8 +37,6 @@ public function testEncode() public function testDecode() { - static::bootKernel(['test_case' => 'JsonEncoder']); - /** @var DecoderInterface $decoder */ $decoder = static::getContainer()->get('json_encoder.decoder.alias'); @@ -44,4 +46,22 @@ public function testDecode() $this->assertEquals($expected, $decoder->decode('{"@name": "DUMMY", "range": "0..1"}', Type::object(Dummy::class))); } + + public function testWarmupEncodableClasses() + { + /** @var Filesystem $fs */ + $fs = static::getContainer()->get('filesystem'); + + $encodersDir = \sprintf('%s/json_encoder/encoder/', static::getContainer()->getParameter('kernel.cache_dir')); + + // clear already created encoders + if ($fs->exists($encodersDir)) { + $fs->remove($encodersDir); + } + + 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.'/*')); + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/Dto/Dummy.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/Dto/Dummy.php index 344b9d11cba03..8610de049fa28 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/Dto/Dummy.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/Dto/Dummy.php @@ -14,11 +14,13 @@ use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer; use Symfony\Component\JsonEncoder\Attribute\Denormalizer; use Symfony\Component\JsonEncoder\Attribute\EncodedName; +use Symfony\Component\JsonEncoder\Attribute\JsonEncodable; use Symfony\Component\JsonEncoder\Attribute\Normalizer; /** * @author Mathias Arlaud */ +#[JsonEncodable] class Dummy { #[EncodedName('@name')] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/config.yml index a92aa3969ea21..13b68adef54c5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/config.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/config.yml @@ -18,4 +18,9 @@ services: alias: json_encoder.decoder public: true + json_encoder.cache_warmer.encoder_decoder.alias: + alias: .json_encoder.cache_warmer.encoder_decoder + public: true + + Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\Dummy: ~ Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer: ~ diff --git a/src/Symfony/Component/JsonEncoder/Attribute/JsonEncodable.php b/src/Symfony/Component/JsonEncoder/Attribute/JsonEncodable.php new file mode 100644 index 0000000000000..f370009d2dcdf --- /dev/null +++ b/src/Symfony/Component/JsonEncoder/Attribute/JsonEncodable.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\JsonEncoder\Attribute; + +/** + * @author Mathias Arlaud + * + * @experimental + */ +#[\Attribute(\Attribute::TARGET_CLASS)] +final class JsonEncodable +{ +} diff --git a/src/Symfony/Component/JsonEncoder/DependencyInjection/EncodablePass.php b/src/Symfony/Component/JsonEncoder/DependencyInjection/EncodablePass.php index cf4fc31ff88c3..47fcd8940d1ea 100644 --- a/src/Symfony/Component/JsonEncoder/DependencyInjection/EncodablePass.php +++ b/src/Symfony/Component/JsonEncoder/DependencyInjection/EncodablePass.php @@ -30,7 +30,11 @@ public function process(ContainerBuilder $container): void $encodableClassNames = []; // retrieve concrete services tagged with "json_encoder.encodable" tag - foreach ($container->findTaggedServiceIds('json_encoder.encodable') as $id => $tags) { + foreach ($container->getDefinitions() as $id => $definition) { + if (!$definition->hasTag('json_encoder.encodable')) { + continue; + } + if (($className = $container->getDefinition($id)->getClass()) && !$container->getDefinition($id)->isAbstract()) { $encodableClassNames[] = $className; }