Skip to content

Commit 36c78e3

Browse files
authored
Merge pull request #19 from veewee/element-context-switcher
Allow child encoders to enhance the context for the element encoder.
2 parents a9610b6 + 092e2b1 commit 36c78e3

File tree

10 files changed

+185
-5
lines changed

10 files changed

+185
-5
lines changed

.php-cs-fixer.dist.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
\Symfony\Component\Finder\Finder::create()
66
->in([
77
__DIR__ . '/src',
8+
__DIR__ . '/examples',
89
__DIR__ . '/tests',
910
])
1011
->name('*.php')

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,6 @@ class MySpecificTypeCEncoder implements XmlEncoder
126126

127127
**Note:** An encoder is considered to be isomorphic : When calling `from` and `to` on the `Iso` object, the data should be the same.
128128
More information about the concept [can be found here](https://github.com/veewee/reflecta/blob/main/docs/isomorphisms.md).
129+
130+
For a full list of available encoders, you can check the [Soap\Encoding\Encoder](src/Encoder) namespace.
131+
There are also some examples of common problems you can solve with these encoders in the [examples/encoders](examples/encoders) directory.

examples/calc-encode.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
<?php
1+
<?php declare(strict_types=1);
22

3-
require_once dirname(__DIR__) . '/vendor/autoload.php';
3+
require_once \dirname(__DIR__) . '/vendor/autoload.php';
44

55
use Soap\Encoding\Driver;
66
use Soap\Encoding\EncoderRegistry;
77
use Soap\Engine\HttpBinding\SoapResponse;
88
use Soap\Wsdl\Loader\StreamWrapperLoader;
99
use Soap\WsdlReader\Locator\ServiceSelectionCriteria;
10-
use Soap\WsdlReader\Metadata\Wsdl1MetadataProvider;
1110
use Soap\WsdlReader\Model\Definitions\SoapVersion;
1211
use Soap\WsdlReader\Wsdl1Reader;
1312

examples/calc-http.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
<?php
1+
<?php declare(strict_types=1);
22

3-
require_once dirname(__DIR__) . '/vendor/autoload.php';
3+
require_once \dirname(__DIR__) . '/vendor/autoload.php';
44

55
use GuzzleHttp\Client;
66
use Soap\Encoding\Driver;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php declare(strict_types=1);
2+
3+
require_once \dirname(__DIR__, 3) . '/vendor/autoload.php';
4+
5+
use Soap\Encoding\Encoder\Context;
6+
use Soap\Encoding\Encoder\XmlEncoder;
7+
use Soap\Encoding\EncoderRegistry;
8+
use Soap\Encoding\Xml\Node\Element;
9+
use VeeWee\Reflecta\Iso\Iso;
10+
use function VeeWee\Xml\Encoding\document_encode;
11+
use function VeeWee\Xml\Encoding\xml_decode;
12+
13+
/**
14+
* This encoder can deal with dynamic XML element structures:
15+
*
16+
* <complexType name="yourTypeUsingTheAnyType">
17+
* <sequence>
18+
* <any processContents="lax" />
19+
* </sequence>
20+
* </complexType>
21+
*
22+
* This encoder will use veewee/xml to encode and decode the whole XML structure so that it can be used by you.
23+
*
24+
* The result looks like this:
25+
*
26+
* <customerData>
27+
* <foo />
28+
* <bar />
29+
* <hello>world</hello>
30+
* </customerData>
31+
*
32+
* <=>
33+
*
34+
* ^ {#1761
35+
* +"customerName": "John Doe"
36+
* +"customerEmail": "[email protected]"
37+
* +"customerData": array:3 [
38+
* "foo" => ""
39+
* "bar" => ""
40+
* "hello" => "world"
41+
* ]
42+
* }
43+
*/
44+
45+
EncoderRegistry::default()
46+
->addComplexTypeConverter(
47+
'http://yournamespace',
48+
'yourTypeUsingTheAnyType',
49+
new class implements XmlEncoder {
50+
/**
51+
* @return Iso<array, string>
52+
*/
53+
public function iso(Context $context): Iso
54+
{
55+
$typeName = $context->type->getName();
56+
57+
return new Iso(
58+
to: static fn (array $data): string => document_encode([$typeName => $data])->stringifyDocumentElement(),
59+
from: static fn (Element|string $xml): array => xml_decode(
60+
($xml instanceof Element ? $xml : Element::fromString($xml))->value()
61+
)[$typeName],
62+
);
63+
}
64+
}
65+
);
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php declare(strict_types=1);
2+
require_once \dirname(__DIR__, 3) . '/vendor/autoload.php';
3+
4+
use Soap\Encoding\Encoder\Context;
5+
use Soap\Encoding\Encoder\Feature\ElementContextEnhancer;
6+
use Soap\Encoding\Encoder\SimpleType\ScalarTypeEncoder;
7+
use Soap\Encoding\Encoder\XmlEncoder;
8+
use Soap\Encoding\EncoderRegistry;
9+
use Soap\Engine\Metadata\Model\TypeMeta;
10+
use Soap\WsdlReader\Model\Definitions\BindingUse;
11+
use VeeWee\Reflecta\Iso\Iso;
12+
13+
/**
14+
* This encoder can add xsi:type information to the XML element on xsd:anyType simpleTypes on literal encoded documents.
15+
*
16+
* <xsd:element minOccurs="0" maxOccurs="1" name="value" type="xsd:anyType" />
17+
*
18+
* Will Result in for example:
19+
*
20+
* <value
21+
* xmlns:xsd="http://www.w3.org/2001/XMLSchema"
22+
* xsi:type="xsds:int"
23+
* xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
24+
* >
25+
* 789
26+
* </value>
27+
*/
28+
29+
EncoderRegistry::default()
30+
->addSimpleTypeConverter(
31+
'http://www.w3.org/2001/XMLSchema',
32+
'anyType',
33+
new class implements
34+
ElementContextEnhancer,
35+
XmlEncoder {
36+
public function iso(Context $context): Iso
37+
{
38+
return (new ScalarTypeEncoder())->iso($context);
39+
}
40+
41+
/**
42+
* This method allows to change the context on the wrapping elementEncoder.
43+
* By forcing the bindingUse to `ENCODED`, we can make sure the xsi:type attribute is added.
44+
* We also make sure the type is not qualified so that the xsi:type prefix xmlns is imported as well.
45+
*/
46+
public function enhanceElementContext(Context $context): Context
47+
{
48+
return $context
49+
->withBindingUse(BindingUse::ENCODED)
50+
->withType(
51+
$context->type->withMeta(
52+
static fn (TypeMeta $meta): TypeMeta => $meta->withIsQualified(false)
53+
)
54+
);
55+
}
56+
}
57+
);

src/Encoder/Context.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,15 @@ public function withType(XsdType $type): self
3030
$this->bindingUse,
3131
);
3232
}
33+
34+
public function withBindingUse(BindingUse $bindingUse): self
35+
{
36+
return new self(
37+
$this->type,
38+
$this->metadata,
39+
$this->registry,
40+
$this->namespaces,
41+
$bindingUse,
42+
);
43+
}
3344
}

src/Encoder/ElementEncoder.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public function __construct(
2828
public function iso(Context $context): Iso
2929
{
3030
$typeEncoder = $this->typeEncoder;
31+
$context = $this->typeEncoder instanceof Feature\ElementContextEnhancer
32+
? $this->typeEncoder->enhanceElementContext($context)
33+
: $context;
3134

3235
return new Iso(
3336
/**
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Soap\Encoding\Encoder\Feature;
5+
6+
use Soap\Encoding\Encoder\Context;
7+
8+
/**
9+
* By implementing this feature on your encoder, you can change the context of the wrapping element.
10+
* This can be used on simpleType encoders to dictate how the wrapped element should be built.
11+
*
12+
* Example usages:
13+
* - Opt-in on xsi:type information for literal documents
14+
* - ... ? :)
15+
*/
16+
interface ElementContextEnhancer
17+
{
18+
public function enhanceElementContext(Context $context): Context;
19+
}

tests/Unit/Encoder/ElementEncoderTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44
namespace Soap\Encoding\Test\Unit\Encoder;
55

66
use PHPUnit\Framework\Attributes\CoversClass;
7+
use Soap\Encoding\Encoder\Context;
78
use Soap\Encoding\Encoder\ElementEncoder;
9+
use Soap\Encoding\Encoder\Feature\ElementContextEnhancer;
810
use Soap\Encoding\Encoder\SimpleType\IntTypeEncoder;
911
use Soap\Encoding\Encoder\SimpleType\StringTypeEncoder;
12+
use Soap\Encoding\Encoder\XmlEncoder;
1013
use Soap\Encoding\Xml\Node\Element;
1114
use Soap\Engine\Metadata\Model\TypeMeta;
1215
use Soap\Engine\Metadata\Model\XsdType;
16+
use VeeWee\Reflecta\Iso\Iso;
1317

1418
#[CoversClass(ElementEncoder::class)]
1519
final class ElementEncoderTest extends AbstractEncoderTests
@@ -57,6 +61,24 @@ public static function provideIsomorphicCases(): iterable
5761
'xml' => '<hello>32</hello>',
5862
'data' => 32,
5963
];
64+
yield 'context-enhancing-child-encoder' => [
65+
...$baseConfig,
66+
'encoder' => $encoder = new ElementEncoder(new class implements ElementContextEnhancer, XmlEncoder {
67+
public function iso(Context $context): Iso
68+
{
69+
return (new IntTypeEncoder())->iso($context);
70+
}
71+
72+
public function enhanceElementContext(Context $context): Context
73+
{
74+
return $context->withType(
75+
$context->type->withXmlTargetNodeName('bonjour')
76+
);
77+
}
78+
}),
79+
'xml' => '<bonjour>32</bonjour>',
80+
'data' => 32,
81+
];
6082
}
6183

6284
public function test_it_can_decode_from_xml_item(): void

0 commit comments

Comments
 (0)