diff --git a/src/Encoder/XsiTypeEncoder.php b/src/Encoder/XsiTypeEncoder.php index e063e09..ae77281 100644 --- a/src/Encoder/XsiTypeEncoder.php +++ b/src/Encoder/XsiTypeEncoder.php @@ -78,7 +78,9 @@ private function to(IsoInterface $innerIso, mixed $value): string */ private function from(Context $context, IsoInterface $innerIso, ?Closure $detectEncoder, Element $value): mixed { - $iso = ($detectEncoder ? $detectEncoder($value->element()) : null)?->iso($context) ?? $innerIso; + // xsi:type is an attribute on the element itself, so an element without attributes cannot carry one. + $element = $value->element(); + $iso = ($detectEncoder && $element->hasAttributes() ? $detectEncoder($element) : null)?->iso($context) ?? $innerIso; /** @psalm-suppress ImplicitToStringCast - Encoders accept string|Element in from() */ return $iso->from($value); diff --git a/tests/Unit/Encoder/XsiTypeEncoderTest.php b/tests/Unit/Encoder/XsiTypeEncoderTest.php index 0483dc9..918dc15 100644 --- a/tests/Unit/Encoder/XsiTypeEncoderTest.php +++ b/tests/Unit/Encoder/XsiTypeEncoderTest.php @@ -6,7 +6,9 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use Soap\Encoding\Encoder\Context; +use Soap\Encoding\Encoder\ElementEncoder; use Soap\Encoding\Encoder\Feature\DisregardXsiInformation; +use Soap\Encoding\Encoder\SimpleType\StringTypeEncoder; use Soap\Encoding\Encoder\XmlEncoder; use Soap\Encoding\Encoder\XsiTypeEncoder; use Soap\Encoding\Test\Unit\ContextCreatorTrait; @@ -64,6 +66,46 @@ public function test_it_ignores_xsi_type_for_encoders_that_disregard_it(): void static::assertSame('inner', $iso->from($this->element('1'))); } + public function test_it_decodes_through_the_detected_xsi_type(): void + { + $actual = $this->decode( + '32' + ); + + static::assertSame(32, $actual); + } + + public function test_it_decodes_an_element_without_attributes_through_the_inner_encoder(): void + { + $actual = $this->decode('32'); + + static::assertSame('32', $actual); + } + + public function test_it_decodes_an_element_with_attributes_but_no_xsi_type_through_the_inner_encoder(): void + { + $actual = $this->decode('32'); + + static::assertSame('32', $actual); + } + + private function decode(string $xml): mixed + { + $encoder = new XsiTypeEncoder(new ElementEncoder(new StringTypeEncoder())); + + return $encoder->iso($this->createStringContext())->from(Element::fromString($xml)); + } + + private function createStringContext(): Context + { + return self::createContext( + XsdType::guess('string') + ->withXmlNamespace('http://www.w3.org/2001/XMLSchema') + ->withXmlTargetNodeName('hello') + ->withMeta(static fn (TypeMeta $meta): TypeMeta => $meta->withIsQualified(true)->withIsSimple(true)->withIsElement(true)) + ); + } + private function element(string $xml): Element { return Element::fromString($xml);