Skip to content

Commit

Permalink
Attempt to give more feedback when an object has a property typed wit…
Browse files Browse the repository at this point in the history
…h an abstract class or interface
  • Loading branch information
simensen committed Aug 24, 2023
1 parent ef62e28 commit 40f5c06
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 11 deletions.
33 changes: 22 additions & 11 deletions src/Serialization/EdgeAwareReflectionSerializing.php
Expand Up @@ -36,6 +36,10 @@ public function toPayload(): array
if (method_exists($this, $factoryMethodName)) {
$convertedValue = $this->$factoryMethodName($value);

$value = $convertedValue;
} elseif ($object->implementsInterface(CustomizedSerializablePayloadEdgeValue::class) && $this->hasCustomSerializablePayloadEdgeValue($targetClass->getName(), $key)) {
$convertedValue = $this->toCustomSerializablePayloadEdgeValue($targetClass->getName(), $key, $value);

$value = $convertedValue;
} elseif ($targetClass->implementsInterface(SerializablePayloadEdgeValue::class)) {
/** @var SerializablePayloadEdgeValue $value */
Expand All @@ -47,13 +51,6 @@ public function toPayload(): array
$convertedValue = $value->toPayload();

$value = $convertedValue;
} elseif ($object->implementsInterface(CustomizedSerializablePayloadEdgeValue::class)) {
/** @var CustomizedSerializablePayloadEdgeValue $this */
if ($this->hasCustomSerializablePayloadEdgeValue($targetClass->getName(), $key)) {
$convertedValue = $this->toCustomSerializablePayloadEdgeValue($targetClass->getName(), $key, $value);

$value = $convertedValue;
}
}

if ($targetClass->implementsInterface(DateTimeInterface::class) && $value) {
Expand Down Expand Up @@ -110,16 +107,30 @@ public static function fromPayload(array $payload): static

if ($class->hasMethod($factoryMethodName)) {
$payload[$key] = static::$factoryMethodName($payload[$key]);
} elseif ($class->implementsInterface(CustomizedSerializablePayloadEdgeValue::class) && static::hasCustomSerializablePayloadEdgeValue($targetClass->getName(), $key)) {
$payload[$key] = static::fromCustomSerializablePayloadEdgeValue($targetClass->getName(), $key, $payload[$key]);
} elseif (!is_null($payload[$key]) && $targetClass->implementsInterface(SerializablePayloadEdgeValue::class)) {
$method = $targetClass->hasMethod('fromPayloadValue')
? $targetClass->getMethod('fromPayloadValue')
: null;

if (!$method || $method->isAbstract()) {
throw new \RuntimeException(sprintf('Needed to call `%s::fromPayloadValue($payload[\'%s\'])` to unserialize %s, but the `$%s->%s` property is likely typed to an interface or abstract class. Consider using `CustomizedSerializablePayloadEdgeValue` to define a custom serialization strategy for this property.', $targetClass->getShortName(), $key, $class->getShortName(), lcfirst($class->getShortName()), $key));
}

/** @var SerializablePayloadEdgeValue $targetClassName */
$payload[$key] = $targetClassName::fromPayloadValue($payload[$key]);
} elseif (!is_null($payload[$key]) && $targetClass->implementsInterface(SerializablePayload::class)) {
$method = $targetClass->hasMethod('fromPayload')
? $targetClass->getMethod('fromPayload')
: null;

if (!$method || $method->isAbstract()) {
throw new \RuntimeException(sprintf('Needed to call `%s::fromPayload($payload[\'%s\'])` to unserialize %s, but the `$%s->%s` property is likely typed to an interface or abstract class. Consider using `CustomizedSerializablePayloadEdgeValue` to define a custom serialization strategy for this property.', $targetClass->getShortName(), $key, $class->getShortName(), lcfirst($class->getShortName()), $key));
}

/** @var SerializablePayload $targetClassName */
$payload[$key] = $targetClassName::fromPayload($payload[$key]);
} elseif ($class->implementsInterface(CustomizedSerializablePayloadEdgeValue::class)) {
if (static::hasCustomSerializablePayloadEdgeValue($targetClass->getName(), $key)) {
$payload[$key] = static::fromCustomSerializablePayloadEdgeValue($targetClass->getName(), $key, $payload[$key]);
}
}
}

Expand Down
19 changes: 19 additions & 0 deletions src/Testing/GivenWhenThen/Scenario.php
Expand Up @@ -11,6 +11,8 @@
use EventSauce\EventSourcing\AggregateRootId;
use EventSauce\EventSourcing\InMemoryMessageRepository;
use EventSauce\EventSourcing\Message;
use EventSauce\EventSourcing\Serialization\DefaultPayloadSerializer;
use EventSauce\EventSourcing\Serialization\PayloadSerializer;
use PHPUnit\Framework\Assert;

final class Scenario
Expand All @@ -26,6 +28,7 @@ final class Scenario
private mixed $handler;
private InMemoryMessageRepository $messageRepository;
private MessagePreparation $messagePreparation;
private PayloadSerializer $payloadSerializer;
private \Closure $visitMessages;
private \Closure $visitEvents;

Expand Down Expand Up @@ -213,8 +216,12 @@ public function assert(): self
}

Assert::assertEquals($expectedEvent, $recordedEvent);

$this->assertObjectSurvivesSerializationRoundTrip($recordedEvent);
}

$this->assertObjectSurvivesSerializationRoundTrip($instance->when->command);

return $instance;
}

Expand All @@ -233,4 +240,16 @@ public function usingMessageRepository(InMemoryMessageRepository $messageReposit

return $instance;
}

public function assertObjectSurvivesSerializationRoundTrip(mixed $input): void
{
$payloadSerializer = $this->payloadSerializer ?? DefaultPayloadSerializer::resolve();

$serializedPayload = $payloadSerializer->serializePayload($input);
$jsonEncodedSerializedPayload = json_encode($serializedPayload);
$jsonDecodedSerializedPayload = json_decode($jsonEncodedSerializedPayload, true);
$unserializedPayload = $payloadSerializer->unserializePayload(get_class($input), $jsonDecodedSerializedPayload);

Assert::assertEquals($input, $unserializedPayload, 'Payload serialization failed round trip.');
}
}

0 comments on commit 40f5c06

Please sign in to comment.