Skip to content

Commit 8a4e77b

Browse files
authored
Added support for coercing stdClass to array<TKey, TValue> (#437)
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.
1 parent a8685b2 commit 8a4e77b

File tree

2 files changed

+19
-0
lines changed

2 files changed

+19
-0
lines changed

src/Psl/Type/Internal/ShapeType.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Psl\Type;
99
use Psl\Type\Exception\AssertException;
1010
use Psl\Type\Exception\CoercionException;
11+
use stdClass;
1112

1213
use function array_diff_key;
1314
use function array_filter;
@@ -51,6 +52,10 @@ public function __construct(
5152
*/
5253
public function coerce(mixed $value): array
5354
{
55+
if ($value instanceof stdClass) {
56+
$value = (array) $value;
57+
}
58+
5459
// To whom reads this: yes, I hate this stuff as passionately as you do :-)
5560
if (! is_array($value)) {
5661
// Fallback to slow implementation - unhappy path

tests/unit/Type/ShapeTypeTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,17 @@ private function validCoercions(): iterable
120120
]],
121121
];
122122

123+
yield 'stdClass containing a valid shape' => [
124+
(object) ['name' => 'saif', 'articles' => new Collection\Vector([
125+
['title' => 'Foo', 'content' => 'Bar', 'likes' => 0, 'dislikes' => 5],
126+
['title' => 'Baz', 'content' => 'Qux', 'likes' => 13, 'dislikes' => 3],
127+
])],
128+
['name' => 'saif', 'articles' => [
129+
['title' => 'Foo', 'content' => 'Bar', 'likes' => 0],
130+
['title' => 'Baz', 'content' => 'Qux', 'likes' => 13],
131+
]],
132+
];
133+
123134
yield [
124135
['name' => 'saif', 'articles' => new Collection\Vector([
125136
['title' => 'Foo', 'content' => 'Bar', 'likes' => 0, 'dislikes' => 5],
@@ -150,6 +161,9 @@ public function getInvalidCoercions(): iterable
150161
yield [['name' => 'saif', 'articles' => [
151162
['title' => 'biz', 'content' => 'foo', 'upvotes' => 4] // 'likes' replaced by 'upvotes'
152163
]]];
164+
yield [(object) ['name' => 'saif', 'articles' => [
165+
['title' => 'biz', 'content' => 'foo', 'upvotes' => 4] // 'likes' replaced by 'upvotes'
166+
]]];
153167
}
154168

155169
public function getToStringExamples(): iterable

0 commit comments

Comments
 (0)