From 59023805f3fe3af28e3f6a137f94b02e231ccd72 Mon Sep 17 00:00:00 2001 From: Axel Guckelsberger Date: Tue, 28 Sep 2021 12:41:47 +0200 Subject: [PATCH] [Serializer] Fix caching context-aware encoders/decoders in ChainEncoder/ChainDecoder --- .../Serializer/Encoder/ChainDecoder.php | 6 ++++- .../Serializer/Encoder/ChainEncoder.php | 6 ++++- .../Tests/Encoder/ChainDecoderTest.php | 22 ++++++++++++++++++- .../Tests/Encoder/ChainEncoderTest.php | 22 ++++++++++++++++++- 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php index 2a55d93a6066f..29c656397f1f0 100644 --- a/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php +++ b/src/Symfony/Component/Serializer/Encoder/ChainDecoder.php @@ -67,9 +67,13 @@ private function getDecoder(string $format, array $context): DecoderInterface return $this->decoders[$this->decoderByFormat[$format]]; } + $cache = true; foreach ($this->decoders as $i => $decoder) { + $cache = $cache && !$decoder instanceof ContextAwareDecoderInterface; if ($decoder->supportsDecoding($format, $context)) { - $this->decoderByFormat[$format] = $i; + if ($cache) { + $this->decoderByFormat[$format] = $i; + } return $decoder; } diff --git a/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php index b13333e88aabb..c2c9e3c8ad2e2 100644 --- a/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/ChainEncoder.php @@ -85,9 +85,13 @@ private function getEncoder(string $format, array $context): EncoderInterface return $this->encoders[$this->encoderByFormat[$format]]; } + $cache = true; foreach ($this->encoders as $i => $encoder) { + $cache = $cache && !$encoder instanceof ContextAwareEncoderInterface; if ($encoder->supportsEncoding($format, $context)) { - $this->encoderByFormat[$format] = $i; + if ($cache) { + $this->encoderByFormat[$format] = $i; + } return $encoder; } diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php index 5cac8d99a5270..8f433ce0fa15a 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/ChainDecoderTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Encoder\ChainDecoder; +use Symfony\Component\Serializer\Encoder\ContextAwareDecoderInterface; use Symfony\Component\Serializer\Encoder\DecoderInterface; use Symfony\Component\Serializer\Exception\RuntimeException; @@ -28,7 +29,7 @@ class ChainDecoderTest extends TestCase protected function setUp(): void { - $this->decoder1 = $this->createMock(DecoderInterface::class); + $this->decoder1 = $this->createMock(ContextAwareDecoderInterface::class); $this->decoder1 ->method('supportsDecoding') ->willReturnMap([ @@ -36,6 +37,7 @@ protected function setUp(): void [self::FORMAT_2, [], false], [self::FORMAT_3, [], false], [self::FORMAT_3, ['foo' => 'bar'], true], + [self::FORMAT_3, ['foo' => 'bar2'], false], ]); $this->decoder2 = $this->createMock(DecoderInterface::class); @@ -45,6 +47,8 @@ protected function setUp(): void [self::FORMAT_1, [], false], [self::FORMAT_2, [], true], [self::FORMAT_3, [], false], + [self::FORMAT_3, ['foo' => 'bar'], false], + [self::FORMAT_3, ['foo' => 'bar2'], true], ]); $this->chainDecoder = new ChainDecoder([$this->decoder1, $this->decoder2]); @@ -52,10 +56,26 @@ protected function setUp(): void public function testSupportsDecoding() { + $this->decoder1 + ->method('decode') + ->willReturn('result1'); + $this->decoder2 + ->method('decode') + ->willReturn('result2'); + $this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_1)); + $this->assertEquals('result1', $this->chainDecoder->decode('', self::FORMAT_1, [])); + $this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_2)); + $this->assertEquals('result2', $this->chainDecoder->decode('', self::FORMAT_2, [])); + $this->assertFalse($this->chainDecoder->supportsDecoding(self::FORMAT_3)); + $this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_3, ['foo' => 'bar'])); + $this->assertEquals('result1', $this->chainDecoder->decode('', self::FORMAT_3, ['foo' => 'bar'])); + + $this->assertTrue($this->chainDecoder->supportsDecoding(self::FORMAT_3, ['foo' => 'bar2'])); + $this->assertEquals('result2', $this->chainDecoder->decode('', self::FORMAT_3, ['foo' => 'bar2'])); } public function testDecode() diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php index e80dc4f1843a6..8ae9e38c1337a 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/ChainEncoderTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Encoder\ChainEncoder; +use Symfony\Component\Serializer\Encoder\ContextAwareEncoderInterface; use Symfony\Component\Serializer\Encoder\EncoderInterface; use Symfony\Component\Serializer\Encoder\NormalizationAwareInterface; use Symfony\Component\Serializer\Exception\RuntimeException; @@ -29,7 +30,7 @@ class ChainEncoderTest extends TestCase protected function setUp(): void { - $this->encoder1 = $this->createMock(EncoderInterface::class); + $this->encoder1 = $this->createMock(ContextAwareEncoderInterface::class); $this->encoder1 ->method('supportsEncoding') ->willReturnMap([ @@ -37,6 +38,7 @@ protected function setUp(): void [self::FORMAT_2, [], false], [self::FORMAT_3, [], false], [self::FORMAT_3, ['foo' => 'bar'], true], + [self::FORMAT_3, ['foo' => 'bar2'], false], ]); $this->encoder2 = $this->createMock(EncoderInterface::class); @@ -46,6 +48,8 @@ protected function setUp(): void [self::FORMAT_1, [], false], [self::FORMAT_2, [], true], [self::FORMAT_3, [], false], + [self::FORMAT_3, ['foo' => 'bar'], false], + [self::FORMAT_3, ['foo' => 'bar2'], true], ]); $this->chainEncoder = new ChainEncoder([$this->encoder1, $this->encoder2]); @@ -53,10 +57,26 @@ protected function setUp(): void public function testSupportsEncoding() { + $this->encoder1 + ->method('encode') + ->willReturn('result1'); + $this->encoder2 + ->method('encode') + ->willReturn('result2'); + $this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_1)); + $this->assertEquals('result1', $this->chainEncoder->encode('', self::FORMAT_1, [])); + $this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_2)); + $this->assertEquals('result2', $this->chainEncoder->encode('', self::FORMAT_2, [])); + $this->assertFalse($this->chainEncoder->supportsEncoding(self::FORMAT_3)); + $this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_3, ['foo' => 'bar'])); + $this->assertEquals('result1', $this->chainEncoder->encode('', self::FORMAT_3, ['foo' => 'bar'])); + + $this->assertTrue($this->chainEncoder->supportsEncoding(self::FORMAT_3, ['foo' => 'bar2'])); + $this->assertEquals('result2', $this->chainEncoder->encode('', self::FORMAT_3, ['foo' => 'bar2'])); } public function testEncode()