diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 15aee29e06e56..19e57fc59af50 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * Allow the `Groups` attribute/annotation on classes * JsonDecode: Add `json_decode_detailed_errors` option * Make `ProblemNormalizer` give details about Messenger's `ValidationFailedException` + * Add `XmlEncoder::CDATA_WRAPPING` context option 6.3 --- diff --git a/src/Symfony/Component/Serializer/Context/Encoder/XmlEncoderContextBuilder.php b/src/Symfony/Component/Serializer/Context/Encoder/XmlEncoderContextBuilder.php index 78617a2bbc816..34cf78198ca42 100644 --- a/src/Symfony/Component/Serializer/Context/Encoder/XmlEncoderContextBuilder.php +++ b/src/Symfony/Component/Serializer/Context/Encoder/XmlEncoderContextBuilder.php @@ -144,4 +144,12 @@ public function withVersion(?string $version): static { return $this->with(XmlEncoder::VERSION, $version); } + + /** + * Configures whether to wrap strings within CDATA sections. + */ + public function withCdataWrapping(?bool $cdataWrapping): static + { + return $this->with(XmlEncoder::CDATA_WRAPPING, $cdataWrapping); + } } diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php index 80a3a932131d3..24d786e38bee0 100644 --- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php +++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php @@ -58,6 +58,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface, NormalizationAwa public const STANDALONE = 'xml_standalone'; public const TYPE_CAST_ATTRIBUTES = 'xml_type_cast_attributes'; public const VERSION = 'xml_version'; + public const CDATA_WRAPPING = 'cdata_wrapping'; private array $defaultContext = [ self::AS_COLLECTION => false, @@ -68,6 +69,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface, NormalizationAwa self::REMOVE_EMPTY_TAGS => false, self::ROOT_NODE_NAME => 'response', self::TYPE_CAST_ATTRIBUTES => true, + self::CDATA_WRAPPING => true, ]; public function __construct(array $defaultContext = []) @@ -424,9 +426,9 @@ private function appendNode(\DOMNode $parentNode, mixed $data, string $format, a /** * Checks if a value contains any characters which would require CDATA wrapping. */ - private function needsCdataWrapping(string $val): bool + private function needsCdataWrapping(string $val, array $context): bool { - return preg_match('/[<>&]/', $val); + return ($context[self::CDATA_WRAPPING] ?? $this->defaultContext[self::CDATA_WRAPPING]) && preg_match('/[<>&]/', $val); } /** @@ -454,7 +456,7 @@ private function selectNodeType(\DOMNode $node, mixed $val, string $format, arra return $this->selectNodeType($node, $this->serializer->normalize($val, $format, $context), $format, $context); } elseif (is_numeric($val)) { return $this->appendText($node, (string) $val); - } elseif (\is_string($val) && $this->needsCdataWrapping($val)) { + } elseif (\is_string($val) && $this->needsCdataWrapping($val, $context)) { return $this->appendCData($node, $val); } elseif (\is_string($val)) { return $this->appendText($node, $val); diff --git a/src/Symfony/Component/Serializer/Tests/Context/Encoder/XmlEncoderContextBuilderTest.php b/src/Symfony/Component/Serializer/Tests/Context/Encoder/XmlEncoderContextBuilderTest.php index 1701733a89402..d1ea307a9205e 100644 --- a/src/Symfony/Component/Serializer/Tests/Context/Encoder/XmlEncoderContextBuilderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Context/Encoder/XmlEncoderContextBuilderTest.php @@ -29,8 +29,6 @@ protected function setUp(): void /** * @dataProvider withersDataProvider - * - * @param array $values */ public function testWithers(array $values) { @@ -47,14 +45,12 @@ public function testWithers(array $values) ->withStandalone($values[XmlEncoder::STANDALONE]) ->withTypeCastAttributes($values[XmlEncoder::TYPE_CAST_ATTRIBUTES]) ->withVersion($values[XmlEncoder::VERSION]) + ->withCdataWrapping($values[XmlEncoder::CDATA_WRAPPING]) ->toArray(); $this->assertSame($values, $context); } - /** - * @return iterable|}> - */ public static function withersDataProvider(): iterable { yield 'With values' => [[ @@ -70,6 +66,7 @@ public static function withersDataProvider(): iterable XmlEncoder::STANDALONE => false, XmlEncoder::TYPE_CAST_ATTRIBUTES => true, XmlEncoder::VERSION => '1.0', + XmlEncoder::CDATA_WRAPPING => false, ]]; yield 'With null values' => [[ @@ -85,6 +82,7 @@ public static function withersDataProvider(): iterable XmlEncoder::STANDALONE => null, XmlEncoder::TYPE_CAST_ATTRIBUTES => null, XmlEncoder::VERSION => null, + XmlEncoder::CDATA_WRAPPING => null, ]]; } } diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php index 151db3f54e37e..7dbd3519c8490 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php @@ -234,15 +234,39 @@ public function testEncodeRootAttributes() public function testEncodeCdataWrapping() { $array = [ - 'firstname' => 'Paul ', + 'firstname' => 'Paul & Martha ', ]; $expected = ''."\n". - ']]>'."\n"; + ']]>'."\n"; $this->assertEquals($expected, $this->encoder->encode($array, 'xml')); } + public function testEnableCdataWrapping() + { + $array = [ + 'firstname' => 'Paul & Martha ', + ]; + + $expected = ''."\n". + ']]>'."\n"; + + $this->assertEquals($expected, $this->encoder->encode($array, 'xml', ['cdata_wrapping' => true])); + } + + public function testDisableCdataWrapping() + { + $array = [ + 'firstname' => 'Paul & Martha ', + ]; + + $expected = ''."\n". + 'Paul & Martha <or Me>'."\n"; + + $this->assertEquals($expected, $this->encoder->encode($array, 'xml', ['cdata_wrapping' => false])); + } + public function testEncodeScalarWithAttribute() { $array = [