Skip to content

Commit

Permalink
Fix stdClass unmarshalling (#327)
Browse files Browse the repository at this point in the history
  • Loading branch information
roxblnfk authored Jul 10, 2023
1 parent 83fea7f commit 9aa30f8
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 6 deletions.
11 changes: 10 additions & 1 deletion src/Internal/Marshaller/Marshaller.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,17 @@ public function marshal(object $from): array
*/
public function unmarshal(array $from, object $to): object
{
$mapper = $this->getMapper(\get_class($to));
$class = $to::class;

if ($class === \stdClass::class) {
foreach ($from as $key => $value) {
$to->{$key} = $value;
}

return $to;
}

$mapper = $this->getMapper($class);
$result = $mapper->isCopyOnWrite() ? clone $to : $to;

foreach ($mapper->getSetters() as $field => $setter) {
Expand Down
24 changes: 19 additions & 5 deletions src/Internal/Marshaller/Type/ObjectType.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@

namespace Temporal\Internal\Marshaller\Type;

use stdClass;
use Temporal\Internal\Marshaller\MarshallerInterface;
use Temporal\Internal\Marshaller\MarshallingRule;

/**
* @template TClass
* @template TClass of object
*/
class ObjectType extends Type implements DetectableTypeInterface, RuleFactoryInterface
{
Expand All @@ -31,7 +32,7 @@ class ObjectType extends Type implements DetectableTypeInterface, RuleFactoryInt
*/
public function __construct(MarshallerInterface $marshaller, string $class = null)
{
$this->reflection = new \ReflectionClass($class ?? \stdClass::class);
$this->reflection = new \ReflectionClass($class ?? stdClass::class);

parent::__construct($marshaller);
}
Expand All @@ -41,7 +42,7 @@ public function __construct(MarshallerInterface $marshaller, string $class = nul
*/
public static function match(\ReflectionNamedType $type): bool
{
return !$type->isBuiltin();
return !$type->isBuiltin() || $type->getName() === 'object';
}

/**
Expand Down Expand Up @@ -71,6 +72,14 @@ public function parse($value, $current): object
$current = $this->emptyInstance();
}

if ($current::class === stdClass::class && $this->reflection->getName() === stdClass::class) {
foreach ($value as $key => $val) {
$current->$key = $val;
}

return $current;
}

return $this->marshaller->unmarshal($value, $current);
}

Expand All @@ -79,11 +88,14 @@ public function parse($value, $current): object
*/
public function serialize($value): array
{
return $this->marshaller->marshal($value);
return $this->reflection->getName() === stdClass::class
? (array)$value
: $this->marshaller->marshal($value);
}

/**
* @return TClass
*
* @throws \ReflectionException
*/
protected function emptyInstance(): object
Expand All @@ -101,6 +113,8 @@ protected function emptyInstance(): object
*/
protected function instance(array $data): object
{
return $this->marshaller->unmarshal($data, $this->reflection->newInstanceWithoutConstructor());
return $this->reflection->getName() === stdClass::class
? (object)$data
: $this->marshaller->unmarshal($data, $this->reflection->newInstanceWithoutConstructor());
}
}
28 changes: 28 additions & 0 deletions tests/Unit/DTO/Type/ObjectType/ObjectTypeTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Temporal\Tests\Unit\DTO\Type\ObjectType;

use ReflectionClass;
use stdClass;
use Temporal\Internal\Marshaller\Type\ObjectType;
use Temporal\Tests\Unit\DTO\Type\ObjectType\Stub\ChildDto;
use Temporal\Tests\Unit\DTO\Type\ObjectType\Stub\Nested1;
Expand All @@ -21,6 +22,7 @@
use Temporal\Tests\Unit\DTO\Type\ObjectType\Stub\ParentDto;
use Temporal\Tests\Unit\DTO\DTOMarshallingTestCase;
use Temporal\Tests\Unit\DTO\Type\ObjectType\Stub\ReadonlyProperty;
use Temporal\Tests\Unit\DTO\Type\ObjectType\Stub\StdClassObjectProp;

final class ObjectTypeTestCase extends DTOMarshallingTestCase
{
Expand Down Expand Up @@ -60,6 +62,32 @@ public function testReadonlyMarshal(): void
$this->assertEquals(['child' => ['foo' => 'foo']], $result);
}

public function testStdClassParamUnmarshal(): void
{
$dto = $this->unmarshal([
'object' => ['foo' => 'bar'],
'class' => ['foo' => 'bar'],
], (new ReflectionClass(StdClassObjectProp::class))->newInstanceWithoutConstructor());

self::assertEquals(new StdClassObjectProp(
(object)['foo' => 'bar'],
(object)['foo' => 'bar'],
), $dto);
}

public function testStdClassUnmarshal(): void
{
$dto = $this->unmarshal([
'object' => ['foo' => 'bar'],
'class' => ['foo' => 'bar'],
], new stdClass());

self::assertEquals((object)[
'object' => ['foo' => 'bar'],
'class' => ['foo' => 'bar'],
], $dto);
}

/**
* @requires PHP >= 8.1
*/
Expand Down
23 changes: 23 additions & 0 deletions tests/Unit/DTO/Type/ObjectType/Stub/StdClassObjectProp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/**
* This file is part of Temporal package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Temporal\Tests\Unit\DTO\Type\ObjectType\Stub;

use stdClass;

final class StdClassObjectProp
{
public function __construct(
public object $object,
public stdClass $class,
) {
}
}

0 comments on commit 9aa30f8

Please sign in to comment.