Skip to content

Commit aa66777

Browse files
committed
[JsonEncoder] Allow to warm up arbitrary types
1 parent f3fb3a5 commit aa66777

File tree

9 files changed

+92
-14
lines changed

9 files changed

+92
-14
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2595,6 +2595,14 @@ private function addJsonEncoderSection(ArrayNodeDefinition $rootNode, callable $
25952595
->arrayNode('json_encoder')
25962596
->info('JSON encoder configuration')
25972597
->{$enableIfStandalone('symfony/json-encoder', EncoderInterface::class)}()
2598+
->fixXmlConfig('type')
2599+
->children()
2600+
->arrayNode('types')
2601+
->info('Encodable/decodable types to warm up during cache generation.')
2602+
->scalarPrototype()->end()
2603+
->defaultValue([])
2604+
->end()
2605+
->end()
25982606
->end()
25992607
->end()
26002608
;

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2028,6 +2028,7 @@ private function registerJsonEncoderConfiguration(array $config, ContainerBuilde
20282028
$container->setParameter('.json_encoder.encoders_dir', '%kernel.cache_dir%/json_encoder/encoder');
20292029
$container->setParameter('.json_encoder.decoders_dir', '%kernel.cache_dir%/json_encoder/decoder');
20302030
$container->setParameter('.json_encoder.lazy_ghosts_dir', '%kernel.cache_dir%/json_encoder/lazy_ghost');
2031+
$container->setParameter('.json_encoder.encodable_types', $config['types']);
20312032

20322033
if (\PHP_VERSION_ID >= 80400) {
20332034
$container->removeDefinition('.json_encoder.cache_warmer.lazy_ghost');

src/Symfony/Bundle/FrameworkBundle/Resources/config/json_encoder.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@
107107
// cache
108108
->set('.json_encoder.cache_warmer.encoder_decoder', EncoderDecoderCacheWarmer::class)
109109
->args([
110-
abstract_arg('encodable class names'),
110+
abstract_arg('encodable types'),
111+
service('type_info.resolver.string')->nullOnInvalid(),
111112
service('json_encoder.encode.property_metadata_loader'),
112113
service('json_encoder.decode.property_metadata_loader'),
113114
param('.json_encoder.encoders_dir'),

src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,9 @@
10071007
</xsd:complexType>
10081008

10091009
<xsd:complexType name="json-encoder">
1010+
<xsd:sequence>
1011+
<xsd:element name="type" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
1012+
</xsd:sequence>
10101013
<xsd:attribute name="enabled" type="xsd:boolean" />
10111014
</xsd:complexType>
10121015

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,7 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor
973973
],
974974
'json_encoder' => [
975975
'enabled' => !class_exists(FullStack::class) && class_exists(JsonEncoder::class),
976+
'types' => [],
976977
],
977978
];
978979
}

src/Symfony/Component/JsonEncoder/CacheWarmer/EncoderDecoderCacheWarmer.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
use Symfony\Component\JsonEncoder\Decode\DecoderGenerator;
1818
use Symfony\Component\JsonEncoder\Encode\EncoderGenerator;
1919
use Symfony\Component\JsonEncoder\Exception\ExceptionInterface;
20+
use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException;
2021
use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface;
2122
use Symfony\Component\TypeInfo\Type;
23+
use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface;
2224

2325
/**
2426
* Generates encoders and decoders PHP files.
@@ -33,10 +35,11 @@ final class EncoderDecoderCacheWarmer implements CacheWarmerInterface
3335
private DecoderGenerator $decoderGenerator;
3436

3537
/**
36-
* @param iterable<class-string> $encodableClassNames
38+
* @param iterable<string> $encodableTypes
3739
*/
3840
public function __construct(
39-
private iterable $encodableClassNames,
41+
private iterable $encodableTypes,
42+
private ?TypeResolverInterface $stringTypeResolver,
4043
PropertyMetadataLoaderInterface $encodePropertyMetadataLoader,
4144
PropertyMetadataLoaderInterface $decodePropertyMetadataLoader,
4245
string $encodersDir,
@@ -49,8 +52,16 @@ public function __construct(
4952

5053
public function warmUp(string $cacheDir, ?string $buildDir = null): array
5154
{
52-
foreach ($this->encodableClassNames as $className) {
53-
$type = Type::object($className);
55+
foreach ($this->encodableTypes as $typeString) {
56+
if ($this->stringTypeResolver) {
57+
$type = $this->stringTypeResolver->resolve($typeString);
58+
} else {
59+
if (!class_exists($typeString)) {
60+
throw new InvalidArgumentException(\sprintf('Unable to parse "%s" as string type resolver is not available. Try running "composer require phpstan/phpoc-parser".', $typeString));
61+
}
62+
63+
$type = Type::object($typeString);
64+
}
5465

5566
$this->warmUpEncoder($type);
5667
$this->warmUpDecoders($type);

src/Symfony/Component/JsonEncoder/DependencyInjection/EncodablePass.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use Symfony\Component\DependencyInjection\ContainerBuilder;
1616

1717
/**
18-
* Sets the encodable classes to the services that need them.
18+
* Sets the encodable types to the services that need them.
1919
*
2020
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
2121
*/
@@ -38,12 +38,15 @@ public function process(ContainerBuilder $container): void
3838
$container->removeDefinition($id);
3939
}
4040

41-
$container->getDefinition('.json_encoder.cache_warmer.encoder_decoder')
42-
->replaceArgument(0, $encodableClassNames);
43-
4441
if ($container->hasDefinition('.json_encoder.cache_warmer.lazy_ghost')) {
4542
$container->getDefinition('.json_encoder.cache_warmer.lazy_ghost')
4643
->replaceArgument(0, $encodableClassNames);
4744
}
45+
46+
$encodableTypes = array_merge($encodableClassNames, $container->getParameter('.json_encoder.encodable_types'));
47+
$encodableTypes = array_values(array_unique($encodableTypes));
48+
49+
$container->getDefinition('.json_encoder.cache_warmer.encoder_decoder')
50+
->replaceArgument(0, $encodableTypes);
4851
}
4952
}

src/Symfony/Component/JsonEncoder/Tests/CacheWarmer/EncoderDecoderCacheWarmerTest.php

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\JsonEncoder\CacheWarmer\EncoderDecoderCacheWarmer;
16+
use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException;
1617
use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader;
1718
use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy;
19+
use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver;
1820
use Symfony\Component\TypeInfo\TypeResolver\TypeResolver;
21+
use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface;
1922

2023
class EncoderDecoderCacheWarmerTest extends TestCase
2124
{
@@ -42,29 +45,55 @@ protected function setUp(): void
4245

4346
public function testWarmUp()
4447
{
45-
$this->cacheWarmer([ClassicDummy::class])->warmUp('useless');
48+
$this->cacheWarmer([ClassicDummy::class, \sprintf('list<%s>', ClassicDummy::class)])->warmUp('useless');
4649

4750
$this->assertSame([
51+
\sprintf('%s/5acb3571777e02a2712fb9a9126a338f.json.php', $this->encodersDir),
4852
\sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.php', $this->encodersDir),
4953
], glob($this->encodersDir.'/*'));
5054

5155
$this->assertSame([
56+
\sprintf('%s/5acb3571777e02a2712fb9a9126a338f.json.php', $this->decodersDir),
57+
\sprintf('%s/5acb3571777e02a2712fb9a9126a338f.json.stream.php', $this->decodersDir),
5258
\sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.php', $this->decodersDir),
5359
\sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.stream.php', $this->decodersDir),
5460
], glob($this->decodersDir.'/*'));
5561
}
5662

63+
public function testWarmupClassWithoutStringTypeResolver()
64+
{
65+
$this->cacheWarmer([ClassicDummy::class], stringTypeResolver: null)->warmUp('useless');
66+
67+
$this->assertSame([
68+
\sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.php', $this->encodersDir),
69+
], glob($this->encodersDir.'/*'));
70+
71+
$this->assertSame([
72+
\sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.php', $this->decodersDir),
73+
\sprintf('%s/d147026bb5d25e5012afcdc1543cf097.json.stream.php', $this->decodersDir),
74+
], glob($this->decodersDir.'/*'));
75+
}
76+
77+
public function testThrowWhenCannotWarmupTypeWithoutStringTypeResolver()
78+
{
79+
$this->expectException(InvalidArgumentException::class);
80+
$this->expectExceptionMessage('Unable to parse "foo" as string type resolver is not available. Try running "composer require phpstan/phpoc-parser".');
81+
82+
$this->cacheWarmer(['foo'], stringTypeResolver: null)->warmUp('useless');
83+
}
84+
5785
/**
5886
* @param list<class-string> $encodable
5987
*/
60-
private function cacheWarmer(array $encodable): EncoderDecoderCacheWarmer
88+
private function cacheWarmer(array $encodable, ?TypeResolverInterface $stringTypeResolver = new StringTypeResolver()): EncoderDecoderCacheWarmer
6189
{
62-
$typeResolver = TypeResolver::create();
90+
$propertyMetadataLoader = new PropertyMetadataLoader(TypeResolver::create());
6391

6492
return new EncoderDecoderCacheWarmer(
6593
$encodable,
66-
new PropertyMetadataLoader($typeResolver),
67-
new PropertyMetadataLoader($typeResolver),
94+
$stringTypeResolver,
95+
$propertyMetadataLoader,
96+
$propertyMetadataLoader,
6897
$this->encodersDir,
6998
$this->decodersDir,
7099
);

src/Symfony/Component/JsonEncoder/Tests/DependencyInjection/EncodablePassTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public function testSetEncodableClassNames()
2424
$container->register('json_encoder.encoder');
2525
$container->register('.json_encoder.cache_warmer.encoder_decoder')->setArguments([null]);
2626
$container->register('.json_encoder.cache_warmer.lazy_ghost')->setArguments([null]);
27+
$container->setParameter('.json_encoder.encodable_types', []);
2728

2829
$container->register('encodable')->setClass('Foo')->addTag('json_encoder.encodable');
2930
$container->register('abstractEncodable')->setClass('Bar')->addTag('json_encoder.encodable')->setAbstract(true);
@@ -40,4 +41,24 @@ public function testSetEncodableClassNames()
4041
$this->assertSame($expectedEncodableClassNames, $encoderDecoderCacheWarmer->getArgument(0));
4142
$this->assertSame($expectedEncodableClassNames, $lazyGhostCacheWarmer->getArgument(0));
4243
}
44+
45+
public function testSetEncodableTypes()
46+
{
47+
$container = new ContainerBuilder();
48+
49+
$container->register('json_encoder.encoder');
50+
$container->register('.json_encoder.cache_warmer.encoder_decoder')->setArguments([null]);
51+
$container->register('.json_encoder.cache_warmer.lazy_ghost')->setArguments([null]);
52+
53+
$container->register('encodable')->setClass('Foo')->addTag('json_encoder.encodable');
54+
$container->register('encodable')->setClass('Bar')->addTag('json_encoder.encodable');
55+
$container->setParameter('.json_encoder.encodable_types', ['array<string, bool>', 'Foo', 'list<Foo>']);
56+
57+
$pass = new EncodablePass();
58+
$pass->process($container);
59+
60+
$encoderDecoderCacheWarmer = $container->getDefinition('.json_encoder.cache_warmer.encoder_decoder');
61+
62+
$this->assertSame(['Bar', 'array<string, bool>', 'Foo', 'list<Foo>'], $encoderDecoderCacheWarmer->getArgument(0));
63+
}
4364
}

0 commit comments

Comments
 (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