Skip to content

Commit d0ac7fd

Browse files
committed
Introduce a way to encode+decode CDATA
1 parent 5a86b18 commit d0ac7fd

File tree

6 files changed

+129
-7
lines changed

6 files changed

+129
-7
lines changed

src/Encoder/ElementEncoder.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@ public function __construct(
2828
public function iso(Context $context): Iso
2929
{
3030
$typeEncoder = $this->typeEncoder;
31-
$typeIso = $typeEncoder->iso($context);
3231

3332
return new Iso(
3433
/**
3534
* @psalm-param mixed $raw
3635
*/
3736
static fn (mixed $raw): string => (new XsdTypeXmlElementWriter())(
3837
$context,
39-
(new ElementValueBuilder($context, $typeIso, $raw))
38+
(new ElementValueBuilder($context, $typeEncoder, $raw))
4039
),
4140
/**
4241
* @psalm-param non-empty-string $xml

src/Encoder/Feature/CData.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Soap\Encoding\Encoder\Feature;
5+
6+
interface CData
7+
{
8+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Soap\Encoding\Encoder\SimpleType;
5+
6+
use Soap\Encoding\Encoder\Context;
7+
use Soap\Encoding\Encoder\Feature;
8+
use Soap\Encoding\Encoder\XmlEncoder;
9+
use VeeWee\Reflecta\Iso\Iso;
10+
11+
/**
12+
* This encoder works exactly the same as the string encoder.
13+
* However, it implements the CData feature.
14+
* When writing the data to the XML element, it will be wrapped in a CDATA section.
15+
*
16+
* @psalm-suppress UnusedClass
17+
* @implements XmlEncoder<string, string>
18+
*/
19+
final class CDataTypeEncoder implements Feature\CData, XmlEncoder
20+
{
21+
/**
22+
* @return Iso<string, string>
23+
*/
24+
public function iso(Context $context): Iso
25+
{
26+
return (new StringTypeEncoder())->iso($context);
27+
}
28+
}

src/Xml/Writer/ElementValueBuilder.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,24 @@
55

66
use Generator;
77
use Soap\Encoding\Encoder\Context;
8+
use Soap\Encoding\Encoder\Feature\CData;
9+
use Soap\Encoding\Encoder\XmlEncoder;
810
use Soap\Encoding\TypeInference\XsiTypeDetector;
911
use Soap\WsdlReader\Model\Definitions\BindingUse;
10-
use VeeWee\Reflecta\Iso\Iso;
1112
use XMLWriter;
13+
use function VeeWee\Xml\Writer\Builder\cdata;
1214
use function VeeWee\Xml\Writer\Builder\children;
1315
use function VeeWee\Xml\Writer\Builder\value;
1416

1517
final class ElementValueBuilder
1618
{
1719
/**
18-
* @param Iso<mixed, string> $iso
20+
* @param XmlEncoder<mixed, string> $encoder
1921
* @psalm-param mixed $value
2022
*/
2123
public function __construct(
2224
private readonly Context $context,
23-
private readonly Iso $iso,
25+
private readonly XmlEncoder $encoder,
2426
private readonly mixed $value
2527
) {
2628
}
@@ -32,7 +34,7 @@ public function __invoke(XMLWriter $writer): Generator
3234
{
3335
yield from children([
3436
$this->buildXsiType(...),
35-
value($this->iso->to($this->value))
37+
$this->buildValue(...),
3638
])($writer);
3739
}
3840

@@ -51,4 +53,19 @@ private function buildXsiType(XMLWriter $writer): Generator
5153
includeXsiTargetNamespace: !$this->context->type->getMeta()->isQualified()->unwrapOr(false)
5254
))($writer);
5355
}
56+
57+
/**
58+
* @return Generator<bool>
59+
*/
60+
private function buildValue(XMLWriter $writer): Generator
61+
{
62+
$encoded = $this->encoder->iso($this->context)->to($this->value);
63+
64+
$builder = match (true) {
65+
$this->encoder instanceof CData => cdata(value($encoded)),
66+
default => value($encoded)
67+
};
68+
69+
yield from $builder($writer);
70+
}
5471
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Soap\Encoding\Test\Unit\Encoder\SimpleType;
5+
6+
use PHPUnit\Framework\Attributes\CoversClass;
7+
use Soap\Encoding\Encoder\ElementEncoder;
8+
use Soap\Encoding\Encoder\SimpleType\CDataTypeEncoder;
9+
use Soap\Encoding\Test\Unit\Encoder\AbstractEncoderTests;
10+
use Soap\Engine\Metadata\Model\XsdType;
11+
12+
#[CoversClass(CDataTypeEncoder::class)]
13+
final class CDataTypeEncoderTest extends AbstractEncoderTests
14+
{
15+
public static function provideIsomorphicCases(): iterable
16+
{
17+
$baseConfig = [
18+
'encoder' => $encoder = new CDataTypeEncoder(),
19+
'context' => $context = self::createContext(
20+
XsdType::guess('string')
21+
->withXmlTargetNodeName('root')
22+
),
23+
];
24+
25+
yield 'simple' => [
26+
...$baseConfig,
27+
'xml' => 'hello',
28+
'data' => 'hello',
29+
];
30+
yield 'special-chars' => [
31+
...$baseConfig,
32+
'xml' => 'hëllo\'"<>',
33+
'data' => 'hëllo\'"<>',
34+
];
35+
36+
$elementEncoder = new ElementEncoder($encoder);
37+
yield 'element-wrapped' => [
38+
...$baseConfig,
39+
'encoder' => $elementEncoder,
40+
'xml' => '<root><![CDATA[hello]]></root>',
41+
'data' => 'hello',
42+
43+
];
44+
yield 'element-wrapped-special-chars' => [
45+
...$baseConfig,
46+
'encoder' => $elementEncoder,
47+
'xml' => '<root><![CDATA[hëllo\'"<>]]></root>',
48+
'data' => 'hëllo\'"<>',
49+
];
50+
}
51+
}

tests/Unit/Encoder/SimpleType/StringTypeEncoderTest.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
namespace Soap\Encoding\Test\Unit\Encoder\SimpleType;
55

66
use PHPUnit\Framework\Attributes\CoversClass;
7+
use Soap\Encoding\Encoder\ElementEncoder;
78
use Soap\Encoding\Encoder\SimpleType\StringTypeEncoder;
89
use Soap\Encoding\Test\Unit\Encoder\AbstractEncoderTests;
910
use Soap\Engine\Metadata\Model\XsdType;
@@ -15,7 +16,10 @@ public static function provideIsomorphicCases(): iterable
1516
{
1617
$baseConfig = [
1718
'encoder' => $encoder = new StringTypeEncoder(),
18-
'context' => $context = self::createContext(XsdType::guess('string')),
19+
'context' => $context = self::createContext(
20+
XsdType::guess('string')
21+
->withXmlTargetNodeName('root')
22+
),
1923
];
2024

2125
yield 'simple' => [
@@ -28,5 +32,20 @@ public static function provideIsomorphicCases(): iterable
2832
'xml' => 'hëllo\'"<>',
2933
'data' => 'hëllo\'"<>',
3034
];
35+
36+
$elementEncoder = new ElementEncoder($encoder);
37+
yield 'element-wrapped' => [
38+
...$baseConfig,
39+
'encoder' => $elementEncoder,
40+
'xml' => '<root>hello</root>',
41+
'data' => 'hello',
42+
43+
];
44+
yield 'element-wrapped-special-chars' => [
45+
...$baseConfig,
46+
'encoder' => $elementEncoder,
47+
'xml' => '<root>hëllo\'&quot;&lt;&gt;</root>',
48+
'data' => 'hëllo\'"<>',
49+
];
3150
}
3251
}

0 commit comments

Comments
 (0)