Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
\Symfony\Component\Finder\Finder::create()
->in([
__DIR__ . '/src',
__DIR__ . '/examples',
__DIR__ . '/tests',
])
->name('*.php')
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,6 @@ class MySpecificTypeCEncoder implements XmlEncoder

**Note:** An encoder is considered to be isomorphic : When calling `from` and `to` on the `Iso` object, the data should be the same.
More information about the concept [can be found here](https://github.com/veewee/reflecta/blob/main/docs/isomorphisms.md).

For a full list of available encoders, you can check the [Soap\Encoding\Encoder](src/Encoder) namespace.
There are also some examples of common problems you can solve with these encoders in the [examples/encoders](examples/encoders) directory.
5 changes: 2 additions & 3 deletions examples/calc-encode.php
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
<?php
<?php declare(strict_types=1);

require_once dirname(__DIR__) . '/vendor/autoload.php';
require_once \dirname(__DIR__) . '/vendor/autoload.php';

use Soap\Encoding\Driver;
use Soap\Encoding\EncoderRegistry;
use Soap\Engine\HttpBinding\SoapResponse;
use Soap\Wsdl\Loader\StreamWrapperLoader;
use Soap\WsdlReader\Locator\ServiceSelectionCriteria;
use Soap\WsdlReader\Metadata\Wsdl1MetadataProvider;
use Soap\WsdlReader\Model\Definitions\SoapVersion;
use Soap\WsdlReader\Wsdl1Reader;

Expand Down
4 changes: 2 additions & 2 deletions examples/calc-http.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
<?php declare(strict_types=1);

require_once dirname(__DIR__) . '/vendor/autoload.php';
require_once \dirname(__DIR__) . '/vendor/autoload.php';

use GuzzleHttp\Client;
use Soap\Encoding\Driver;
Expand Down
65 changes: 65 additions & 0 deletions examples/encoders/complexType/any-elements.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php declare(strict_types=1);

require_once \dirname(__DIR__, 3) . '/vendor/autoload.php';

use Soap\Encoding\Encoder\Context;
use Soap\Encoding\Encoder\XmlEncoder;
use Soap\Encoding\EncoderRegistry;
use Soap\Encoding\Xml\Node\Element;
use VeeWee\Reflecta\Iso\Iso;
use function VeeWee\Xml\Encoding\document_encode;
use function VeeWee\Xml\Encoding\xml_decode;

/**
* This encoder can deal with dynamic XML element structures:
*
* <complexType name="yourTypeUsingTheAnyType">
* <sequence>
* <any processContents="lax" />
* </sequence>
* </complexType>
*
* This encoder will use veewee/xml to encode and decode the whole XML structure so that it can be used by you.
*
* The result looks like this:
*
* <customerData>
* <foo />
* <bar />
* <hello>world</hello>
* </customerData>
*
* <=>
*
* ^ {#1761
* +"customerName": "John Doe"
* +"customerEmail": "[email protected]"
* +"customerData": array:3 [
* "foo" => ""
* "bar" => ""
* "hello" => "world"
* ]
* }
*/

EncoderRegistry::default()
->addComplexTypeConverter(
'http://yournamespace',
'yourTypeUsingTheAnyType',
new class implements XmlEncoder {
/**
* @return Iso<array, string>
*/
public function iso(Context $context): Iso
{
$typeName = $context->type->getName();

return new Iso(
to: static fn (array $data): string => document_encode([$typeName => $data])->stringifyDocumentElement(),
from: static fn (Element|string $xml): array => xml_decode(
($xml instanceof Element ? $xml : Element::fromString($xml))->value()
)[$typeName],
);
}
}
);
57 changes: 57 additions & 0 deletions examples/encoders/simpleType/anyType-with-xsi-info.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php declare(strict_types=1);
require_once \dirname(__DIR__, 3) . '/vendor/autoload.php';

use Soap\Encoding\Encoder\Context;
use Soap\Encoding\Encoder\Feature\ElementContextEnhancer;
use Soap\Encoding\Encoder\SimpleType\ScalarTypeEncoder;
use Soap\Encoding\Encoder\XmlEncoder;
use Soap\Encoding\EncoderRegistry;
use Soap\Engine\Metadata\Model\TypeMeta;
use Soap\WsdlReader\Model\Definitions\BindingUse;
use VeeWee\Reflecta\Iso\Iso;

/**
* This encoder can add xsi:type information to the XML element on xsd:anyType simpleTypes on literal encoded documents.
*
* <xsd:element minOccurs="0" maxOccurs="1" name="value" type="xsd:anyType" />
*
* Will Result in for example:
*
* <value
* xmlns:xsd="http://www.w3.org/2001/XMLSchema"
* xsi:type="xsds:int"
* xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
* >
* 789
* </value>
*/

EncoderRegistry::default()
->addSimpleTypeConverter(
'http://www.w3.org/2001/XMLSchema',
'anyType',
new class implements
ElementContextEnhancer,
XmlEncoder {
public function iso(Context $context): Iso
{
return (new ScalarTypeEncoder())->iso($context);
}

/**
* This method allows to change the context on the wrapping elementEncoder.
* By forcing the bindingUse to `ENCODED`, we can make sure the xsi:type attribute is added.
* We also make sure the type is not qualified so that the xsi:type prefix xmlns is imported as well.
*/
public function enhanceElementContext(Context $context): Context
{
return $context
->withBindingUse(BindingUse::ENCODED)
->withType(
$context->type->withMeta(
static fn (TypeMeta $meta): TypeMeta => $meta->withIsQualified(false)
)
);
}
}
);
11 changes: 11 additions & 0 deletions src/Encoder/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,15 @@ public function withType(XsdType $type): self
$this->bindingUse,
);
}

public function withBindingUse(BindingUse $bindingUse): self
{
return new self(
$this->type,
$this->metadata,
$this->registry,
$this->namespaces,
$bindingUse,
);
}
}
3 changes: 3 additions & 0 deletions src/Encoder/ElementEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public function __construct(
public function iso(Context $context): Iso
{
$typeEncoder = $this->typeEncoder;
$context = $this->typeEncoder instanceof Feature\ElementContextEnhancer
? $this->typeEncoder->enhanceElementContext($context)
: $context;

return new Iso(
/**
Expand Down
19 changes: 19 additions & 0 deletions src/Encoder/Feature/ElementContextEnhancer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);

namespace Soap\Encoding\Encoder\Feature;

use Soap\Encoding\Encoder\Context;

/**
* By implementing this feature on your encoder, you can change the context of the wrapping element.
* This can be used on simpleType encoders to dictate how the wrapped element should be built.
*
* Example usages:
* - Opt-in on xsi:type information for literal documents
* - ... ? :)
*/
interface ElementContextEnhancer
{
public function enhanceElementContext(Context $context): Context;
}
22 changes: 22 additions & 0 deletions tests/Unit/Encoder/ElementEncoderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
namespace Soap\Encoding\Test\Unit\Encoder;

use PHPUnit\Framework\Attributes\CoversClass;
use Soap\Encoding\Encoder\Context;
use Soap\Encoding\Encoder\ElementEncoder;
use Soap\Encoding\Encoder\Feature\ElementContextEnhancer;
use Soap\Encoding\Encoder\SimpleType\IntTypeEncoder;
use Soap\Encoding\Encoder\SimpleType\StringTypeEncoder;
use Soap\Encoding\Encoder\XmlEncoder;
use Soap\Encoding\Xml\Node\Element;
use Soap\Engine\Metadata\Model\TypeMeta;
use Soap\Engine\Metadata\Model\XsdType;
use VeeWee\Reflecta\Iso\Iso;

#[CoversClass(ElementEncoder::class)]
final class ElementEncoderTest extends AbstractEncoderTests
Expand Down Expand Up @@ -57,6 +61,24 @@ public static function provideIsomorphicCases(): iterable
'xml' => '<hello>32</hello>',
'data' => 32,
];
yield 'context-enhancing-child-encoder' => [
...$baseConfig,
'encoder' => $encoder = new ElementEncoder(new class implements ElementContextEnhancer, XmlEncoder {
public function iso(Context $context): Iso
{
return (new IntTypeEncoder())->iso($context);
}

public function enhanceElementContext(Context $context): Context
{
return $context->withType(
$context->type->withXmlTargetNodeName('bonjour')
);
}
}),
'xml' => '<bonjour>32</bonjour>',
'data' => 32,
];
}

public function test_it_can_decode_from_xml_item(): void
Expand Down