Skip to content

Commit e6d4568

Browse files
committed
Add CachingParser decorator for in-memory type parsing cache
Types like "string", "DateTime<'Y-m-d'>", "array<MyClass>" are re-parsed on every serialize/deserialize call. Cache eliminates redudant lexer/parser work. Wired as default in SerializerBuilder; users calling setTypeParser() still override entirely.
1 parent b02a6c0 commit e6d4568

File tree

3 files changed

+109
-1
lines changed

3 files changed

+109
-1
lines changed

src/SerializerBuilder.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
use JMS\Serializer\Naming\CamelCaseNamingStrategy;
4343
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
4444
use JMS\Serializer\Naming\SerializedNameAnnotationStrategy;
45+
use JMS\Serializer\Type\CachingParser;
4546
use JMS\Serializer\Type\Parser;
4647
use JMS\Serializer\Type\ParserInterface;
4748
use JMS\Serializer\Visitor\Factory\DeserializationVisitorFactory;
@@ -198,7 +199,7 @@ public static function create(...$args): self
198199

199200
public function __construct(?HandlerRegistryInterface $handlerRegistry = null, ?EventDispatcherInterface $eventDispatcher = null)
200201
{
201-
$this->typeParser = new Parser();
202+
$this->typeParser = new CachingParser(new Parser());
202203
$this->handlerRegistry = $handlerRegistry ?: new HandlerRegistry();
203204
$this->eventDispatcher = $eventDispatcher ?: new EventDispatcher();
204205
$this->serializationVisitors = [];

src/Type/CachingParser.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace JMS\Serializer\Type;
6+
7+
/**
8+
* @internal
9+
*/
10+
final class CachingParser implements ParserInterface
11+
{
12+
/**
13+
* @var ParserInterface
14+
*/
15+
private $inner;
16+
17+
/**
18+
* @var array<string, array>
19+
*/
20+
private $cache = [];
21+
22+
public function __construct(ParserInterface $inner)
23+
{
24+
$this->inner = $inner;
25+
}
26+
27+
public function parse(string $type): array
28+
{
29+
return $this->cache[$type] ?? $this->cache[$type] = $this->inner->parse($type);
30+
}
31+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace JMS\Serializer\Tests\Serializer\Type;
6+
7+
use JMS\Serializer\Type\CachingParser;
8+
use JMS\Serializer\Type\Parser;
9+
use JMS\Serializer\Type\ParserInterface;
10+
use PHPUnit\Framework\TestCase;
11+
12+
class CachingParserTest extends TestCase
13+
{
14+
/** @var CachingParser */
15+
private $parser;
16+
17+
protected function setUp(): void
18+
{
19+
$this->parser = new CachingParser(new Parser());
20+
}
21+
22+
public function testReturnsSameResultAsInnerParser(): void
23+
{
24+
$inner = new Parser();
25+
$cached = new CachingParser($inner);
26+
27+
$types = [
28+
'string',
29+
'int',
30+
'array<Foo>',
31+
'array<string, int>',
32+
'DateTime<\'Y-m-d\'>',
33+
'Foo<Bar<Baz>>',
34+
];
35+
36+
foreach ($types as $type) {
37+
self::assertSame($inner->parse($type), $cached->parse($type), sprintf('Mismatch for type "%s"', $type));
38+
}
39+
}
40+
41+
public function testCachesResults(): void
42+
{
43+
$inner = $this->createMock(ParserInterface::class);
44+
$inner->expects(self::once())
45+
->method('parse')
46+
->with('string')
47+
->willReturn(['name' => 'string', 'params' => []]);
48+
49+
$cached = new CachingParser($inner);
50+
51+
$result1 = $cached->parse('string');
52+
$result2 = $cached->parse('string');
53+
54+
self::assertSame($result1, $result2);
55+
}
56+
57+
public function testDifferentTypesAreNotConfused(): void
58+
{
59+
$result1 = $this->parser->parse('string');
60+
$result2 = $this->parser->parse('int');
61+
62+
self::assertSame(['name' => 'string', 'params' => []], $result1);
63+
self::assertSame(['name' => 'int', 'params' => []], $result2);
64+
}
65+
66+
public function testComplexTypesAreCached(): void
67+
{
68+
$type = 'DateTime<\'Y-m-d\', \'UTC\'>';
69+
$result1 = $this->parser->parse($type);
70+
$result2 = $this->parser->parse($type);
71+
72+
self::assertSame($result1, $result2);
73+
self::assertSame('DateTime', $result1['name']);
74+
self::assertCount(2, $result1['params']);
75+
}
76+
}

0 commit comments

Comments
 (0)