|null}
* @psalm-type MatchingEncoderDetector = \Closure(Context, mixed): MatchedEncoderInfo
*
* @psalm-suppress UnusedClass
*
* @implements XmlEncoder
*/
final readonly class MatchingValueEncoder implements XmlEncoder
{
/**
* @param MatchingEncoderDetector $encoderDetector
* @param XmlEncoder $defaultEncoder
*/
public function __construct(
private Closure $encoderDetector,
private XmlEncoder $defaultEncoder,
) {
}
public function iso(Context $context): Iso
{
$defaultIso = $this->defaultEncoder->iso($context);
return new Iso(
to: fn (mixed $value): string => $this->to($context, $value),
/**
* @param string|Element $value
*/
from: static fn (string|Element $value): mixed => $defaultIso->from($value),
);
}
private function to(Context $context, mixed $value): string
{
$matchedEncoderInfo = ($this->encoderDetector)($context, $value);
[$context, $encoder] = match(true) {
$matchedEncoderInfo instanceof Context => [$matchedEncoderInfo, $this->defaultEncoder],
default => [$matchedEncoderInfo[0], $matchedEncoderInfo[1] ?? $this->defaultEncoder],
};
/** @psalm-suppress RedundantConditionGivenDocblockType - This gives better feedback to people using this encoder */
// Ensure that the encoderDetector returns valid data.
invariant($context instanceof Context, 'The MatchingValueEncoder::$encoderDetector callable must return a Context or an array with a Context as first element.');
invariant($encoder instanceof XmlEncoder, 'The MatchingValueEncoder::$encoderDetector callable must return a Context or an array with a Context as first element and an optional XmlEncoder as second element.');
return $encoder->iso($context)->to($value);
}
}