Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Serializer] Add XmlEncoder::CDATA_WRAPPING_PATTERN context option #54663

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Symfony/Component/Serializer/CHANGELOG.md
Expand Up @@ -10,6 +10,7 @@ CHANGELOG
* Add `AbstractNormalizer::FILTER_BOOL` context option
* Add `CamelCaseToSnakeCaseNameConverter::REQUIRE_SNAKE_CASE_PROPERTIES` context option
* Deprecate `AbstractNormalizerContextBuilder::withDefaultContructorArguments(?array $defaultContructorArguments)`, use `withDefaultConstructorArguments(?array $defaultConstructorArguments)` instead (note the missing `s` character in Contructor word in deprecated method)
* Add `XmlEncoder::CDATA_WRAPPING_PATTERN` context option

7.0
---
Expand Down
Expand Up @@ -152,4 +152,12 @@ public function withCdataWrapping(?bool $cdataWrapping): static
{
return $this->with(XmlEncoder::CDATA_WRAPPING, $cdataWrapping);
}

/**
* Configures the pattern used to evaluate if a CDATA section should be added.
*/
public function withCdataWrappingPattern(?string $cdataWrappingPattern): static
{
return $this->with(XmlEncoder::CDATA_WRAPPING_PATTERN, $cdataWrappingPattern);
}
}
4 changes: 3 additions & 1 deletion src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
Expand Up @@ -59,6 +59,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface, NormalizationAwa
public const TYPE_CAST_ATTRIBUTES = 'xml_type_cast_attributes';
public const VERSION = 'xml_version';
public const CDATA_WRAPPING = 'cdata_wrapping';
public const CDATA_WRAPPING_PATTERN = 'cdata_wrapping_pattern';

private array $defaultContext = [
self::AS_COLLECTION => false,
Expand All @@ -70,6 +71,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface, NormalizationAwa
self::ROOT_NODE_NAME => 'response',
self::TYPE_CAST_ATTRIBUTES => true,
self::CDATA_WRAPPING => true,
self::CDATA_WRAPPING_PATTERN => '/[<>&]/',
];

public function __construct(array $defaultContext = [])
Expand Down Expand Up @@ -433,7 +435,7 @@ private function appendNode(\DOMNode $parentNode, mixed $data, string $format, a
*/
private function needsCdataWrapping(string $val, array $context): bool
{
return ($context[self::CDATA_WRAPPING] ?? $this->defaultContext[self::CDATA_WRAPPING]) && preg_match('/[<>&]/', $val);
return ($context[self::CDATA_WRAPPING] ?? $this->defaultContext[self::CDATA_WRAPPING]) && preg_match($context[self::CDATA_WRAPPING_PATTERN] ?? $this->defaultContext[self::CDATA_WRAPPING_PATTERN], $val);
}

/**
Expand Down
Expand Up @@ -46,6 +46,7 @@ public function testWithers(array $values)
->withTypeCastAttributes($values[XmlEncoder::TYPE_CAST_ATTRIBUTES])
->withVersion($values[XmlEncoder::VERSION])
->withCdataWrapping($values[XmlEncoder::CDATA_WRAPPING])
->withCdataWrappingPattern($values[XmlEncoder::CDATA_WRAPPING_PATTERN])
->toArray();

$this->assertSame($values, $context);
Expand All @@ -67,6 +68,7 @@ public static function withersDataProvider(): iterable
XmlEncoder::TYPE_CAST_ATTRIBUTES => true,
XmlEncoder::VERSION => '1.0',
XmlEncoder::CDATA_WRAPPING => false,
XmlEncoder::CDATA_WRAPPING_PATTERN => '/[<>&"\']/',
]];

yield 'With null values' => [[
Expand All @@ -83,6 +85,7 @@ public static function withersDataProvider(): iterable
XmlEncoder::TYPE_CAST_ATTRIBUTES => null,
XmlEncoder::VERSION => null,
XmlEncoder::CDATA_WRAPPING => null,
XmlEncoder::CDATA_WRAPPING_PATTERN => null,
]];
}
}
Expand Up @@ -231,16 +231,56 @@ public function testEncodeRootAttributes()
$this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
}

public function testEncodeCdataWrapping()
/**
* @dataProvider encodeCdataWrappingWithDefaultPattern
*/
public function testEncodeCdataWrappingWithDefaultPattern($input, $expected)
{
$array = [
'firstname' => 'Paul & Martha <or Me>',
$this->assertEquals($expected, $this->encoder->encode($input, 'xml'));
}

public static function encodeCdataWrappingWithDefaultPattern()
{
return [
[
['firstname' => 'Paul and Martha'],
'<?xml version="1.0"?>'."\n".'<response><firstname>Paul and Martha</firstname></response>'."\n",
],
[
['lastname' => 'O\'Donnel'],
'<?xml version="1.0"?>'."\n".'<response><lastname>O\'Donnel</lastname></response>'."\n",
],
[
['firstname' => 'Paul & Martha <or Me>'],
'<?xml version="1.0"?>'."\n".'<response><firstname><![CDATA[Paul & Martha <or Me>]]></firstname></response>'."\n",
],
];
}

$expected = '<?xml version="1.0"?>'."\n".
'<response><firstname><![CDATA[Paul & Martha <or Me>]]></firstname></response>'."\n";
/**
* @dataProvider encodeCdataWrappingWithCustomPattern
*/
public function testEncodeCdataWrappingWithCustomPattern($input, $expected)
{
$this->assertEquals($expected, $this->encoder->encode($input, 'xml', ['cdata_wrapping_pattern' => '/[<>&"\']/']));
}

$this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
public static function encodeCdataWrappingWithCustomPattern()
{
return [
[
['firstname' => 'Paul and Martha'],
'<?xml version="1.0"?>'."\n".'<response><firstname>Paul and Martha</firstname></response>'."\n",
],
[
['lastname' => 'O\'Donnel'],
'<?xml version="1.0"?>'."\n".'<response><lastname><![CDATA[O\'Donnel]]></lastname></response>'."\n",
],
[
['firstname' => 'Paul & Martha <or Me>'],
'<?xml version="1.0"?>'."\n".'<response><firstname><![CDATA[Paul & Martha <or Me>]]></firstname></response>'."\n",
],
];
}

public function testEnableCdataWrapping()
Expand Down