From 14244e5683ea572dafeda6c80d16b4914376d178 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Thu, 26 Mar 2026 09:13:28 +0100 Subject: [PATCH 1/4] Cache inner iso in XsiTypeEncoder to avoid redundant iso() calls Captures $this->encoder->iso($context) once in iso() and passes the result to the private to() and from() methods. This avoids rebuilding the inner iso on every encode/decode invocation. In from(), when xsi:type detection resolves to the same encoder, the cached iso is reused instead of calling iso() again. --- src/Encoder/XsiTypeEncoder.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Encoder/XsiTypeEncoder.php b/src/Encoder/XsiTypeEncoder.php index b2d2954..91ebff2 100644 --- a/src/Encoder/XsiTypeEncoder.php +++ b/src/Encoder/XsiTypeEncoder.php @@ -25,28 +25,28 @@ public function __construct( */ public function iso(Context $context): Iso { + $innerIso = $this->encoder->iso($context); + return new Iso( - function (mixed $value) use ($context) : string { - return $this->to($context, $value); + function (mixed $value) use ($innerIso) : string { + return $this->to($innerIso, $value); }, - function (string|Element $value) use ($context) : mixed { + function (string|Element $value) use ($context, $innerIso) : mixed { return $this->from( $context, + $innerIso, ($value instanceof Element ? $value : Element::fromString(non_empty_string()->assert($value))) ); } ); } - private function to(Context $context, mixed $value): string + private function to(Iso $innerIso, mixed $value): string { - // There is no way to know what xsi:type to use when encoding any type. - // The type defined in the wsdl will always be used to encode the value. - // If you want more control over the encoded type, please control how to encode by using the MatchingValueEncoder. - return $this->encoder->iso($context)->to($value); + return $innerIso->to($value); } - private function from(Context $context, Element $value): mixed + private function from(Context $context, Iso $innerIso, Element $value): mixed { /** @var XmlEncoder $encoder */ $encoder = match (true) { @@ -54,6 +54,8 @@ private function from(Context $context, Element $value): mixed default => XsiTypeDetector::detectEncoderFromXmlElement($context, $value->element())->unwrapOr($this->encoder) }; - return $encoder->iso($context)->from($value); + $iso = $encoder === $this->encoder ? $innerIso : $encoder->iso($context); + + return $iso->from($value); } } From 0cf3ef4f3670a71cce2bec2a846ccbd48994bac3 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Thu, 26 Mar 2026 09:16:22 +0100 Subject: [PATCH 2/4] Restore comments and simplify from() to return Iso from match --- src/Encoder/XsiTypeEncoder.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Encoder/XsiTypeEncoder.php b/src/Encoder/XsiTypeEncoder.php index 91ebff2..a9f3558 100644 --- a/src/Encoder/XsiTypeEncoder.php +++ b/src/Encoder/XsiTypeEncoder.php @@ -43,19 +43,21 @@ function (string|Element $value) use ($context, $innerIso) : mixed { private function to(Iso $innerIso, mixed $value): string { + // There is no way to know what xsi:type to use when encoding any type. + // The type defined in the wsdl will always be used to encode the value. + // If you want more control over the encoded type, please control how to encode by using the MatchingValueEncoder. return $innerIso->to($value); } private function from(Context $context, Iso $innerIso, Element $value): mixed { - /** @var XmlEncoder $encoder */ - $encoder = match (true) { - $this->encoder instanceof Feature\DisregardXsiInformation => $this->encoder, - default => XsiTypeDetector::detectEncoderFromXmlElement($context, $value->element())->unwrapOr($this->encoder) + $iso = match (true) { + $this->encoder instanceof Feature\DisregardXsiInformation => $innerIso, + default => XsiTypeDetector::detectEncoderFromXmlElement($context, $value->element()) + ->map(static fn (XmlEncoder $encoder): Iso => $encoder->iso($context)) + ->unwrapOr($innerIso), }; - $iso = $encoder === $this->encoder ? $innerIso : $encoder->iso($context); - return $iso->from($value); } } From 0b2ef0c98dddde1c6f6c1dbba4ce9708133f4023 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Thu, 26 Mar 2026 09:20:19 +0100 Subject: [PATCH 3/4] Add psalm param annotations for Iso generics --- src/Encoder/XsiTypeEncoder.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Encoder/XsiTypeEncoder.php b/src/Encoder/XsiTypeEncoder.php index a9f3558..45be847 100644 --- a/src/Encoder/XsiTypeEncoder.php +++ b/src/Encoder/XsiTypeEncoder.php @@ -41,6 +41,9 @@ function (string|Element $value) use ($context, $innerIso) : mixed { ); } + /** + * @param Iso $innerIso + */ private function to(Iso $innerIso, mixed $value): string { // There is no way to know what xsi:type to use when encoding any type. @@ -49,6 +52,9 @@ private function to(Iso $innerIso, mixed $value): string return $innerIso->to($value); } + /** + * @param Iso $innerIso + */ private function from(Context $context, Iso $innerIso, Element $value): mixed { $iso = match (true) { From 0d7914dfd268ab36b4abbf47b666c6c5a10862d0 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Thu, 26 Mar 2026 09:23:10 +0100 Subject: [PATCH 4/4] Suppress psalm ImplicitToStringCast in from() Encoders accept string|Element in from() by design, but the Iso generic is typed as Iso. --- src/Encoder/XsiTypeEncoder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Encoder/XsiTypeEncoder.php b/src/Encoder/XsiTypeEncoder.php index 45be847..e060ac5 100644 --- a/src/Encoder/XsiTypeEncoder.php +++ b/src/Encoder/XsiTypeEncoder.php @@ -64,6 +64,7 @@ private function from(Context $context, Iso $innerIso, Element $value): mixed ->unwrapOr($innerIso), }; + /** @psalm-suppress ImplicitToStringCast - Encoders accept string|Element in from() */ return $iso->from($value); } }