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
40 changes: 21 additions & 19 deletions src/Encoder/ErrorHandlingEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,45 +30,47 @@ public function __construct(
public function iso(Context $context): Iso
{
$innerIso = $this->encoder->iso($context);
$buildPath = static function () use ($context): ?string {
$meta = $context->type->getMeta();
$isElement = $meta->isElement()->unwrapOr(false);
$isAttribute = $meta->isAttribute()->unwrapOr(false);
if (!$isElement && !$isAttribute) {
return null;
}

$path = $context->type->getXmlTargetNodeName();
if ($isAttribute) {
return '@' . $path;
}

return $path;
};

return new Iso(
/**
* @psalm-param TData $value
* @psalm-return TXml
*/
static function (mixed $value) use ($innerIso, $context, $buildPath): mixed {
static function (mixed $value) use ($innerIso, $context): mixed {
try {
return $innerIso->to($value);
} catch (Throwable $exception) {
throw EncodingException::encodingValue($value, $context->type, $exception, $buildPath());
throw EncodingException::encodingValue($value, $context->type, $exception, self::buildPath($context));
}
},
/**
* @psalm-param TXml $value
* @psalm-return TData
*/
static function (mixed $value) use ($innerIso, $context, $buildPath): mixed {
static function (mixed $value) use ($innerIso, $context): mixed {
try {
return $innerIso->from($value);
} catch (Throwable $exception) {
throw EncodingException::decodingValue($value, $context->type, $exception, $buildPath());
throw EncodingException::decodingValue($value, $context->type, $exception, self::buildPath($context));
}
}
);
}

private static function buildPath(Context $context): ?string
{
$meta = $context->type->getMeta();
$isElement = $meta->isElement()->unwrapOr(false);
$isAttribute = $meta->isAttribute()->unwrapOr(false);
if (!$isElement && !$isAttribute) {
return null;
}

$path = $context->type->getXmlTargetNodeName();
if ($isAttribute) {
return '@' . $path;
}

return $path;
}
}
99 changes: 40 additions & 59 deletions src/Encoder/ObjectEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Soap\Encoding\Encoder;

use Closure;
use Exception;
use Soap\Encoding\Normalizer\PhpPropertyNameNormalizer;
use Soap\Encoding\TypeInference\ComplexTypeBuilder;
use Soap\Encoding\TypeInference\XsiTypeDetector;
Expand All @@ -13,7 +14,7 @@
use Soap\Encoding\Xml\Writer\XsdTypeXmlElementWriter;
use Soap\Encoding\Xml\Writer\XsiAttributeBuilder;
use Soap\Engine\Metadata\Model\Property;
use Soap\Engine\Metadata\Model\XsdType;
use Soap\Engine\Metadata\Model\TypeMeta;
use VeeWee\Reflecta\Iso\Iso;
use VeeWee\Reflecta\Lens\Lens;
use function is_array;
Expand Down Expand Up @@ -100,30 +101,27 @@ private function to(Context $context, array $properties, object|array $data): st
$properties,
function (Property $property) use ($context, $data, $defaultAction) : Closure {
$type = $property->getType();
$lens = $this->decorateLensForType(
$meta = $type->getMeta();
$isAttribute = $meta->isAttribute()->unwrapOr(false);

/** @var mixed $value */
$value = $this->runLens(
property(PhpPropertyNameNormalizer::normalize($property->getName())),
$type
$meta,
$data,
null
);
/**
* @psalm-var mixed $value
* @psalm-suppress PossiblyInvalidArgument - Psalm gets lost in the lens.
*/
$value = $lens
->tryGet($data)
->catch(static fn () => null)
->getResult();

return $this->handleProperty(
$property,
onAttribute: fn (): Closure => $value ? (new AttributeBuilder(

return match(true) {
$isAttribute => $value ? (new AttributeBuilder(
$type,
$this->grabIsoForProperty($context, $property)->to($value)
))(...) : $defaultAction,
onValue: fn (): Closure => $value
$property->getName() === '_' => $value
? buildValue($this->grabIsoForProperty($context, $property)->to($value))
: (new NilAttributeBuilder())(...),
onElements: fn (): Closure => $value ? raw($this->grabIsoForProperty($context, $property)->to($value)) : $defaultAction,
);
default => $value ? raw($this->grabIsoForProperty($context, $property)->to($value)) : $defaultAction
};
}
)
]
Expand All @@ -149,23 +147,21 @@ private function from(Context $context, array $properties, string $data): object
function (Property $property) use ($context, $nodes): mixed {
$type = $property->getType();
$meta = $type->getMeta();
$isList = $meta->isList()->unwrapOr(false);
/** @psalm-var string|null $value */
$value = $this->decorateLensForType(

/** @var string|null $value */
$value = $this->runLens(
index($property->getName()),
$type
)
->tryGet($nodes)
->catch(static fn () => null)
->getResult();
$defaultValue = $isList ? [] : null;

return $this->handleProperty(
$property,
onAttribute: fn (): mixed => /** @psalm-suppress PossiblyNullArgument */$this->grabIsoForProperty($context, $property)->from($value),
onValue: fn (): mixed => $value !== null ? $this->grabIsoForProperty($context, $property)->from($value) : $defaultValue,
onElements: fn (): mixed => $value !== null ? $this->grabIsoForProperty($context, $property)->from($value) : $defaultValue,
$meta,
$nodes,
null
);
$defaultValue = $meta->isList()->unwrapOr(false) ? [] : null;

/** @psalm-suppress PossiblyNullArgument */
return match(true) {
$meta->isAttribute()->unwrapOr(false) => $this->grabIsoForProperty($context, $property)->from($value),
default => $value !== null ? $this->grabIsoForProperty($context, $property)->from($value) : $defaultValue,
};
},
static fn (Property $property) => PhpPropertyNameNormalizer::normalize($property->getName()),
)
Expand All @@ -183,27 +179,14 @@ private function grabIsoForProperty(Context $context, Property $property): Iso
return $encoder->iso($propertyContext);
}

/**
* @template X
*
* @param Closure(): X $onAttribute
* @param Closure(): X $onValue
* @param Closure(): X $onElements
* @return X
*/
private function handleProperty(
Property $property,
Closure $onAttribute,
Closure $onValue,
Closure $onElements,
) {
$meta = $property->getType()->getMeta();

return match(true) {
$meta->isAttribute()->unwrapOr(false) => $onAttribute(),
$property->getName() === '_' => $onValue(),
default => $onElements()
};
private function runLens(Lens $lens, TypeMeta $meta, mixed $data, mixed $default): mixed
{
try {
/** @var mixed */
return $this->decorateLensForType($lens, $meta)->get($data);
} catch (Exception $e) {
return $default;
}
}

/**
Expand All @@ -214,9 +197,8 @@ private function handleProperty(
*
* @return Lens<S, A>
*/
private function decorateLensForType(Lens $lens, XsdType $type): Lens
private function decorateLensForType(Lens $lens, TypeMeta $meta): Lens
{
$meta = $type->getMeta();
if ($meta->isNullable()->unwrapOr(false)) {
return optional($lens);
}
Expand All @@ -237,14 +219,13 @@ private function decorateLensForType(Lens $lens, XsdType $type): Lens
private function detectProperties(Context $context): array
{
$type = (new ComplexTypeBuilder())($context);
$properties = reindex(

return reindex(
sort_by(
$type->getProperties(),
static fn (Property $property): bool => !$property->getType()->getMeta()->isAttribute()->unwrapOr(false),
),
static fn (Property $property): string => $property->getName(),
);

return $properties;
}
}
8 changes: 2 additions & 6 deletions src/Encoder/OptionalElementEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,16 @@ public function iso(Context $context): Iso
$meta = $type->getMeta();
$elementIso = $this->elementEncoder->iso($context);

$isNullable = $meta->isNullable()->unwrapOr(false);
if (!$isNullable) {
if (!$meta->isNullable()->unwrapOr(false)) {
return $elementIso;
}

$isNillable = $meta->isNil()->unwrapOr(false);
$elementIso = $this->elementEncoder->iso($context);

return new Iso(
/**
* @param T|null $raw
*/
static fn (mixed $raw): string => match (true) {
$raw === null && $isNillable => (new XsdTypeXmlElementWriter())($context, new NilAttributeBuilder()),
$raw === null && $meta->isNil()->unwrapOr(false) => (new XsdTypeXmlElementWriter())($context, new NilAttributeBuilder()),
$raw === null => '',
default => $elementIso->to($raw),
},
Expand Down
5 changes: 0 additions & 5 deletions src/Encoder/SimpleType/EncoderDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Soap\Encoding\Encoder\Feature;
use Soap\Encoding\Encoder\OptionalElementEncoder;
use Soap\Encoding\Encoder\XmlEncoder;
use Soap\Encoding\Exception\InvalidArgumentException;
use Soap\Engine\Metadata\Model\XsdType;
use function Psl\Iter\any;

Expand All @@ -22,10 +21,6 @@ public function __invoke(Context $context): XmlEncoder
$type = $context->type;
$meta = $type->getMeta();

if (!$meta->isSimple()->unwrapOr(false)) {
throw new InvalidArgumentException('Unable to detect a complex type in the simple type detector');
}

$encoder = $this->detectSimpleTypeEncoder($type, $context);
if (!$encoder instanceof Feature\ListAware && $this->detectIsListType($type)) {
$encoder = new SimpleListEncoder($encoder);
Expand Down
8 changes: 0 additions & 8 deletions src/Exception/InvalidArgumentException.php

This file was deleted.

2 changes: 1 addition & 1 deletion src/Restriction/WhitespaceRestriction.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static function parseForContext(Context $context, string $value): string
return match ($whitespace) {
self::REPLACE => self::replace($value),
self::COLLAPSE => self::collapse($value),
default => self::preserve($value),
default => $value,
};
}

Expand Down