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

Detect nillable lists #33

Merged
merged 1 commit into from
Jan 10, 2025
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
22 changes: 18 additions & 4 deletions src/Encoder/OptionalElementEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
namespace Soap\Encoding\Encoder;

use Soap\Encoding\Xml\Node\Element;
use Soap\Encoding\Xml\Node\ElementList;
use Soap\Encoding\Xml\Writer\NilAttributeBuilder;
use Soap\Encoding\Xml\Writer\XsdTypeXmlElementWriter;
use VeeWee\Reflecta\Iso\Iso;
use VeeWee\Xml\Xmlns\Xmlns;
use function count;
use function is_string;

/**
* @template T of mixed
Expand All @@ -31,6 +34,7 @@ public function iso(Context $context): Iso
$type = $context->type;
$meta = $type->getMeta();
$elementIso = $this->elementEncoder->iso($context);
$isList = $meta->isList()->unwrapOr(false);

if (!$meta->isNullable()->unwrapOr(false)) {
return $elementIso;
Expand All @@ -48,17 +52,27 @@ public function iso(Context $context): Iso
/**
* @return T|null
*/
static function (Element|string $xml) use ($elementIso) : mixed {
static function (ElementList|Element|string $xml) use ($elementIso, $isList) : mixed {
if ($xml === '') {
return null;
}

$documentElement = ($xml instanceof Element ? $xml : Element::fromString($xml))->element();
if ($documentElement->getAttributeNS(Xmlns::xsi()->value(), 'nil') === 'true') {
$parsedXml = match(true) {
$isList && is_string($xml) => ElementList::fromString('<list>'.$xml.'</list>'),
is_string($xml) => Element::fromString($xml),
default => $xml,
};

$documentElement = match (true) {
$parsedXml instanceof ElementList && count($parsedXml) === 1 => $parsedXml->elements()[0]->element(),
$parsedXml instanceof Element => $parsedXml->element(),
default => null
};
if ($documentElement && $documentElement->getAttributeNS(Xmlns::xsi()->value(), 'nil') === 'true') {
return null;
}

/** @var Iso<T|null, Element|non-empty-string> $elementIso */
/** @var Iso<T|null, ElementList|Element|non-empty-string> $elementIso */
return $elementIso->from($xml);
}
);
Expand Down
9 changes: 8 additions & 1 deletion src/Xml/Node/ElementList.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@
namespace Soap\Encoding\Xml\Node;

use Closure;
use Countable;
use DOMElement;
use Soap\Encoding\Xml\Reader\DocumentToLookupArrayReader;
use Stringable;
use VeeWee\Xml\Dom\Document;
use function count;
use function Psl\Iter\reduce;
use function Psl\Vec\map;
use function VeeWee\Xml\Dom\Locator\Element\children as readChildren;

/**
* @psalm-import-type LookupArray from DocumentToLookupArrayReader
*/
final class ElementList implements Stringable
final class ElementList implements Countable, Stringable
{
/** @var list<Element> */
private array $elements;
Expand Down Expand Up @@ -111,4 +113,9 @@ public function __toString()
{
return $this->value();
}

public function count(): int
{
return count($this->elements);
}
}
66 changes: 66 additions & 0 deletions tests/PhpCompatibility/Implied/ImpliedSchema009Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);

namespace Soap\Encoding\Test\PhpCompatibility\Implied;

use PHPUnit\Framework\Attributes\CoversClass;
use Soap\Encoding\Decoder;
use Soap\Encoding\Driver;
use Soap\Encoding\Encoder;
use Soap\Encoding\Test\PhpCompatibility\AbstractCompatibilityTests;

#[CoversClass(Driver::class)]
#[CoversClass(Encoder::class)]
#[CoversClass(Decoder::class)]
#[CoversClass(Encoder\AnyElementEncoder::class)]
final class ImpliedSchema009Test extends AbstractCompatibilityTests
{
protected string $schema = <<<EOXML
<element name="testType" minOccurs="0" maxOccurs="1" type="tns:ArrayOfCompany" />
<complexType name="ArrayOfCompany">
<sequence>
<element minOccurs="0" maxOccurs="unbounded" name="Company" nillable="true" type="tns:Company" />
</sequence>
</complexType>
<complexType name="Company">
<sequence>
<element minOccurs="1" maxOccurs="1" name="ID" type="xsd:int" />
</sequence>
</complexType>
EOXML;
protected string $type = 'type="tns:testType"';

protected function calculateParam(): mixed
{
return (object)[
'Company' => [
(object)['ID' => 0],
(object)['ID' => 1],
],
];
}

protected function expectXml(): string
{
return <<<XML
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="http://test-uri/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:test>
<testParam xsi:type="tns:ArrayOfCompany">
<Company xsi:type="tns:Company">
<ID xsi:type="xsd:int">0</ID>
</Company>
<Company xsi:type="tns:Company">
<ID xsi:type="xsd:int">1</ID>
</Company>
</testParam>
</tns:test>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
XML;
}
}
57 changes: 57 additions & 0 deletions tests/PhpCompatibility/Implied/ImpliedSchema010Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);

namespace Soap\Encoding\Test\PhpCompatibility\Implied;

use PHPUnit\Framework\Attributes\CoversClass;
use Soap\Encoding\Decoder;
use Soap\Encoding\Driver;
use Soap\Encoding\Encoder;
use Soap\Encoding\Test\PhpCompatibility\AbstractCompatibilityTests;

#[CoversClass(Driver::class)]
#[CoversClass(Encoder::class)]
#[CoversClass(Decoder::class)]
#[CoversClass(Encoder\AnyElementEncoder::class)]
final class ImpliedSchema010Test extends AbstractCompatibilityTests
{
protected string $schema = <<<EOXML
<element name="testType" minOccurs="0" maxOccurs="1" type="tns:ArrayOfCompany" />
<complexType name="ArrayOfCompany">
<sequence>
<element minOccurs="0" maxOccurs="unbounded" name="Company" nillable="true" type="tns:Company" />
</sequence>
</complexType>
<complexType name="Company">
<sequence>
<element minOccurs="1" maxOccurs="1" name="ID" type="xsd:int" />
</sequence>
</complexType>
EOXML;
protected string $type = 'type="tns:testType"';

protected function calculateParam(): mixed
{
return (object) [
'Company' => null,
];
}

protected function expectXml(): string
{
return <<<XML
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="http://test-uri/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:test>
<testParam xsi:type="tns:ArrayOfCompany">
<Company xsi:nil="true" />
</testParam>
</tns:test>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
XML;
}
}
55 changes: 55 additions & 0 deletions tests/PhpCompatibility/Implied/ImpliedSchema011Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);

namespace Soap\Encoding\Test\PhpCompatibility\Implied;

use PHPUnit\Framework\Attributes\CoversClass;
use Soap\Encoding\Decoder;
use Soap\Encoding\Driver;
use Soap\Encoding\Encoder;
use Soap\Encoding\Test\PhpCompatibility\AbstractCompatibilityTests;

#[CoversClass(Driver::class)]
#[CoversClass(Encoder::class)]
#[CoversClass(Decoder::class)]
#[CoversClass(Encoder\AnyElementEncoder::class)]
final class ImpliedSchema011Test extends AbstractCompatibilityTests
{
protected string $schema = <<<EOXML
<element name="testType" minOccurs="0" maxOccurs="1" type="tns:ArrayOfCompany" />
<complexType name="ArrayOfCompany">
<sequence>
<element minOccurs="0" maxOccurs="unbounded" name="Company" nillable="true" type="tns:Company" />
</sequence>
</complexType>
<complexType name="Company">
<sequence>
<element minOccurs="1" maxOccurs="1" name="ID" type="xsd:int" />
</sequence>
</complexType>
EOXML;
protected string $type = 'type="tns:testType"';

protected function calculateParam(): mixed
{
return (object) [
'Company' => [],
];
}

protected function expectXml(): string
{
return <<<XML
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="http://test-uri/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:test>
<testParam xsi:type="tns:ArrayOfCompany" />
</tns:test>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
XML;
}
}
62 changes: 62 additions & 0 deletions tests/PhpCompatibility/Implied/ImpliedSchema012Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);

namespace Soap\Encoding\Test\PhpCompatibility\Implied;

use PHPUnit\Framework\Attributes\CoversClass;
use Soap\Encoding\Decoder;
use Soap\Encoding\Driver;
use Soap\Encoding\Encoder;
use Soap\Encoding\Test\PhpCompatibility\AbstractCompatibilityTests;

#[CoversClass(Driver::class)]
#[CoversClass(Encoder::class)]
#[CoversClass(Decoder::class)]
#[CoversClass(Encoder\AnyElementEncoder::class)]
final class ImpliedSchema012Test extends AbstractCompatibilityTests
{
protected string $schema = <<<EOXML
<element name="testType" minOccurs="0" maxOccurs="1" type="tns:ArrayOfCompany" />
<complexType name="ArrayOfCompany">
<sequence>
<element minOccurs="0" maxOccurs="unbounded" name="Company" nillable="true" type="tns:Company" />
</sequence>
</complexType>
<complexType name="Company">
<sequence>
<element minOccurs="1" maxOccurs="1" name="ID" type="xsd:int" />
</sequence>
</complexType>
EOXML;
protected string $type = 'type="tns:testType"';

protected function calculateParam(): mixed
{
return (object) [
'Company' => [
(object)['ID' => 0],
],
];
}

protected function expectXml(): string
{
return <<<XML
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="http://test-uri/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:test>
<testParam xsi:type="tns:ArrayOfCompany">
<Company xsi:type="tns:Company">
<ID xsi:type="xsd:int">0</ID>
</Company>
</testParam>
</tns:test>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
XML;
}
}
8 changes: 8 additions & 0 deletions tests/Unit/Xml/Node/ElementListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,12 @@ public function test_it_can_load_nested_list(): void

static::assertSame([$hello, $list1, $list2], $list->elements());
}

public function test_it_can_be_countded(): void
{
$list = new ElementList(Element::fromString('<hello>world</hello>'));

static::assertCount(1, $list->elements());
static::assertCount(1, $list);
}
}
Loading