From f476f586767a1933fc1247cbf2183d2df442d36f Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 16 Jan 2024 17:26:03 +0100 Subject: [PATCH] Added support for coercing `stdClass` to `array` Fixes #436 This change is useful when decoding JSON hashmaps into PHP hashmaps, since PHP keeps JSON-decoded data as an `stdClass` unless explicitly told to do so. Also note that preserving JSON structures as `stdClass` is sometimes very much required, since an empty hashmap is a lossy conversion to `[]` performed by `json_decode()`, when using `associative: true`. This patch makes preserving JSON data structures a bit easier. This adds some very minimal overhead to `ShapeType#coerce()`, which should be imperceptible. --- src/Psl/Type/Internal/ShapeType.php | 5 +++++ tests/unit/Type/ShapeTypeTest.php | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/Psl/Type/Internal/ShapeType.php b/src/Psl/Type/Internal/ShapeType.php index bae26522..65b2aad6 100644 --- a/src/Psl/Type/Internal/ShapeType.php +++ b/src/Psl/Type/Internal/ShapeType.php @@ -8,6 +8,7 @@ use Psl\Type; use Psl\Type\Exception\AssertException; use Psl\Type\Exception\CoercionException; +use stdClass; use function array_diff_key; use function array_filter; @@ -51,6 +52,10 @@ public function __construct( */ public function coerce(mixed $value): array { + if ($value instanceof stdClass) { + $value = (array) $value; + } + // To whom reads this: yes, I hate this stuff as passionately as you do :-) if (! is_array($value)) { // Fallback to slow implementation - unhappy path diff --git a/tests/unit/Type/ShapeTypeTest.php b/tests/unit/Type/ShapeTypeTest.php index 7366dbbe..43809d17 100644 --- a/tests/unit/Type/ShapeTypeTest.php +++ b/tests/unit/Type/ShapeTypeTest.php @@ -120,6 +120,17 @@ private function validCoercions(): iterable ]], ]; + yield 'stdClass containing a valid shape' => [ + (object) ['name' => 'saif', 'articles' => new Collection\Vector([ + ['title' => 'Foo', 'content' => 'Bar', 'likes' => 0, 'dislikes' => 5], + ['title' => 'Baz', 'content' => 'Qux', 'likes' => 13, 'dislikes' => 3], + ])], + ['name' => 'saif', 'articles' => [ + ['title' => 'Foo', 'content' => 'Bar', 'likes' => 0], + ['title' => 'Baz', 'content' => 'Qux', 'likes' => 13], + ]], + ]; + yield [ ['name' => 'saif', 'articles' => new Collection\Vector([ ['title' => 'Foo', 'content' => 'Bar', 'likes' => 0, 'dislikes' => 5], @@ -150,6 +161,9 @@ public function getInvalidCoercions(): iterable yield [['name' => 'saif', 'articles' => [ ['title' => 'biz', 'content' => 'foo', 'upvotes' => 4] // 'likes' replaced by 'upvotes' ]]]; + yield [(object) ['name' => 'saif', 'articles' => [ + ['title' => 'biz', 'content' => 'foo', 'upvotes' => 4] // 'likes' replaced by 'upvotes' + ]]]; } public function getToStringExamples(): iterable