Skip to content

Commit

Permalink
Merge pull request #9 from veewee/perf
Browse files Browse the repository at this point in the history
Performance optimizations
  • Loading branch information
veewee authored Jun 12, 2024
2 parents 1b25f2b + e87d790 commit 3a1f3b5
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 98 deletions.
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

0 comments on commit 3a1f3b5

Please sign in to comment.