From 8ab422f61946643f0bd155cd28453d433f282c52 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Mon, 23 Dec 2024 11:41:35 +0100 Subject: [PATCH] [JsonEncoder] Allow to warm up arbitrary types --- .../DependencyInjection/Configuration.php | 8 ++++ .../FrameworkExtension.php | 1 + .../Resources/config/json_encoder.php | 3 +- .../Resources/config/schema/symfony-1.0.xsd | 3 ++ .../DependencyInjection/ConfigurationTest.php | 1 + .../CacheWarmer/EncoderDecoderCacheWarmer.php | 19 +++++++-- .../DependencyInjection/EncodablePass.php | 11 ++++-- .../EncoderDecoderCacheWarmerTest.php | 39 ++++++++++++++++--- .../DependencyInjection/EncodablePassTest.php | 21 ++++++++++ 9 files changed, 92 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 265fba9fd1f2d..29e7f3962b9f2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -2595,6 +2595,14 @@ private function addJsonEncoderSection(ArrayNodeDefinition $rootNode, callable $ ->arrayNode('json_encoder') ->info('JSON encoder configuration') ->{$enableIfStandalone('symfony/json-encoder', EncoderInterface::class)}() + ->fixXmlConfig('type') + ->children() + ->arrayNode('types') + ->info('Encodable/decodable types to warm up during cache generation.') + ->scalarPrototype()->end() + ->defaultValue([]) + ->end() + ->end() ->end() ->end() ; diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 38d37c7fc1990..cd833a0639538 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2028,6 +2028,7 @@ private function registerJsonEncoderConfiguration(array $config, ContainerBuilde $container->setParameter('.json_encoder.encoders_dir', '%kernel.cache_dir%/json_encoder/encoder'); $container->setParameter('.json_encoder.decoders_dir', '%kernel.cache_dir%/json_encoder/decoder'); $container->setParameter('.json_encoder.lazy_ghosts_dir', '%kernel.cache_dir%/json_encoder/lazy_ghost'); + $container->setParameter('.json_encoder.encodable_types', $config['types']); if (\PHP_VERSION_ID >= 80400) { $container->removeDefinition('.json_encoder.cache_warmer.lazy_ghost'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_encoder.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_encoder.php index 67cb25d0aa13a..37ffba64b1635 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_encoder.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_encoder.php @@ -107,7 +107,8 @@ // cache ->set('.json_encoder.cache_warmer.encoder_decoder', EncoderDecoderCacheWarmer::class) ->args([ - abstract_arg('encodable class names'), + abstract_arg('encodable types'), + service('type_info.resolver.string')->nullOnInvalid(), service('json_encoder.encode.property_metadata_loader'), service('json_encoder.decode.property_metadata_loader'), param('.json_encoder.encoders_dir'), diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index b0eaf1679f3d9..7ff91f82e55ac 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -1007,6 +1007,9 @@ + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index e8ca9873f1a70..288581934c191 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -973,6 +973,7 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor ], 'json_encoder' => [ 'enabled' => !class_exists(FullStack::class) && class_exists(JsonEncoder::class), + 'types' => [], ], ]; } diff --git a/src/Symfony/Component/JsonEncoder/CacheWarmer/EncoderDecoderCacheWarmer.php b/src/Symfony/Component/JsonEncoder/CacheWarmer/EncoderDecoderCacheWarmer.php index a01bb63794bfd..4c7c425cf3183 100644 --- a/src/Symfony/Component/JsonEncoder/CacheWarmer/EncoderDecoderCacheWarmer.php +++ b/src/Symfony/Component/JsonEncoder/CacheWarmer/EncoderDecoderCacheWarmer.php @@ -17,8 +17,10 @@ use Symfony\Component\JsonEncoder\Decode\DecoderGenerator; use Symfony\Component\JsonEncoder\Encode\EncoderGenerator; use Symfony\Component\JsonEncoder\Exception\ExceptionInterface; +use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface; use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface; /** * Generates encoders and decoders PHP files. @@ -33,10 +35,11 @@ final class EncoderDecoderCacheWarmer implements CacheWarmerInterface private DecoderGenerator $decoderGenerator; /** - * @param iterable $encodableClassNames + * @param iterable $encodableTypes */ public function __construct( - private iterable $encodableClassNames, + private iterable $encodableTypes, + private ?TypeResolverInterface $stringTypeResolver, PropertyMetadataLoaderInterface $encodePropertyMetadataLoader, PropertyMetadataLoaderInterface $decodePropertyMetadataLoader, string $encodersDir, @@ -49,8 +52,16 @@ public function __construct( public function warmUp(string $cacheDir, ?string $buildDir = null): array { - foreach ($this->encodableClassNames as $className) { - $type = Type::object($className); + foreach ($this->encodableTypes as $typeString) { + if ($this->stringTypeResolver) { + $type = $this->stringTypeResolver->resolve($typeString); + } else { + if (!class_exists($typeString)) { + throw new InvalidArgumentException(\sprintf('Unable to parse "%s" as string type resolver is not available. Try running "composer require phpstan/phpoc-parser".', $typeString)); + } + + $type = Type::object($typeString); + } $this->warmUpEncoder($type); $this->warmUpDecoders($type); diff --git a/src/Symfony/Component/JsonEncoder/DependencyInjection/EncodablePass.php b/src/Symfony/Component/JsonEncoder/DependencyInjection/EncodablePass.php index cf4fc31ff88c3..783e44188b95b 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 types to the services that need them. * * @author Mathias Arlaud */ @@ -38,12 +38,15 @@ public function process(ContainerBuilder $container): void $container->removeDefinition($id); } - $container->getDefinition('.json_encoder.cache_warmer.encoder_decoder') - ->replaceArgument(0, $encodableClassNames); - if ($container->hasDefinition('.json_encoder.cache_warmer.lazy_ghost')) { $container->getDefinition('.json_encoder.cache_warmer.lazy_ghost') ->replaceArgument(0, $encodableClassNames); } + + $encodableTypes = array_merge($encodableClassNames, $container->getParameter('.json_encoder.encodable_types')); + $encodableTypes = array_values(array_unique($encodableTypes)); + + $container->getDefinition('.json_encoder.cache_warmer.encoder_decoder') + ->replaceArgument(0, $encodableTypes); } } diff --git a/src/Symfony/Component/JsonEncoder/Tests/CacheWarmer/EncoderDecoderCacheWarmerTest.php b/src/Symfony/Component/JsonEncoder/Tests/CacheWarmer/EncoderDecoderCacheWarmerTest.php index 142d1ef09d1fa..7b1b3ce1c330f 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/CacheWarmer/EncoderDecoderCacheWarmerTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/CacheWarmer/EncoderDecoderCacheWarmerTest.php @@ -13,9 +13,12 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\JsonEncoder\CacheWarmer\EncoderDecoderCacheWarmer; +use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy; +use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver; use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; +use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface; class EncoderDecoderCacheWarmerTest extends TestCase { @@ -42,29 +45,55 @@ protected function setUp(): void public function testWarmUp() { - $this->cacheWarmer([ClassicDummy::class])->warmUp('useless'); + $this->cacheWarmer([ClassicDummy::class, \sprintf('list<%s>', ClassicDummy::class)])->warmUp('useless'); $this->assertSame([ + \sprintf('%s/5acb3571777e02a2712fb9a9126a338f.json.php', $this->encodersDir), \sprintf('%s/d147026bb5d25e5012afcdc1543cf097.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), ], glob($this->decodersDir.'/*')); } + public function testWarmupClassWithoutStringTypeResolver() + { + $this->cacheWarmer([ClassicDummy::class], stringTypeResolver: null)->warmUp('useless'); + + $this->assertSame([ + \sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.php', $this->encodersDir), + ], glob($this->encodersDir.'/*')); + + $this->assertSame([ + \sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.php', $this->decodersDir), + \sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.stream.php', $this->decodersDir), + ], glob($this->decodersDir.'/*')); + } + + public function testThrowWhenCannotWarmupTypeWithoutStringTypeResolver() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Unable to parse "foo" as string type resolver is not available. Try running "composer require phpstan/phpoc-parser".'); + + $this->cacheWarmer(['foo'], stringTypeResolver: null)->warmUp('useless'); + } + /** * @param list $encodable */ - private function cacheWarmer(array $encodable): EncoderDecoderCacheWarmer + private function cacheWarmer(array $encodable, ?TypeResolverInterface $stringTypeResolver = new StringTypeResolver()): EncoderDecoderCacheWarmer { - $typeResolver = TypeResolver::create(); + $propertyMetadataLoader = new PropertyMetadataLoader(TypeResolver::create()); return new EncoderDecoderCacheWarmer( $encodable, - new PropertyMetadataLoader($typeResolver), - new PropertyMetadataLoader($typeResolver), + $stringTypeResolver, + $propertyMetadataLoader, + $propertyMetadataLoader, $this->encodersDir, $this->decodersDir, ); diff --git a/src/Symfony/Component/JsonEncoder/Tests/DependencyInjection/EncodablePassTest.php b/src/Symfony/Component/JsonEncoder/Tests/DependencyInjection/EncodablePassTest.php index 55ad1c036a130..d83fbe28a633f 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/DependencyInjection/EncodablePassTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/DependencyInjection/EncodablePassTest.php @@ -24,6 +24,7 @@ public function testSetEncodableClassNames() $container->register('json_encoder.encoder'); $container->register('.json_encoder.cache_warmer.encoder_decoder')->setArguments([null]); $container->register('.json_encoder.cache_warmer.lazy_ghost')->setArguments([null]); + $container->setParameter('.json_encoder.encodable_types', []); $container->register('encodable')->setClass('Foo')->addTag('json_encoder.encodable'); $container->register('abstractEncodable')->setClass('Bar')->addTag('json_encoder.encodable')->setAbstract(true); @@ -40,4 +41,24 @@ public function testSetEncodableClassNames() $this->assertSame($expectedEncodableClassNames, $encoderDecoderCacheWarmer->getArgument(0)); $this->assertSame($expectedEncodableClassNames, $lazyGhostCacheWarmer->getArgument(0)); } + + public function testSetEncodableTypes() + { + $container = new ContainerBuilder(); + + $container->register('json_encoder.encoder'); + $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('encodable')->setClass('Bar')->addTag('json_encoder.encodable'); + $container->setParameter('.json_encoder.encodable_types', ['array', 'Foo', 'list']); + + $pass = new EncodablePass(); + $pass->process($container); + + $encoderDecoderCacheWarmer = $container->getDefinition('.json_encoder.cache_warmer.encoder_decoder'); + + $this->assertSame(['Bar', 'array', 'Foo', 'list'], $encoderDecoderCacheWarmer->getArgument(0)); + } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy