Skip to content

Commit

Permalink
Merge pull request #1571 from scyzoryck/union-properties-array
Browse files Browse the repository at this point in the history
feat(unions): Add support for simple arrays
  • Loading branch information
scyzoryck authored Dec 3, 2024
2 parents f8adbc4 + 18f462f commit b4285f4
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 37 deletions.
7 changes: 5 additions & 2 deletions src/Handler/UnionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,15 @@ private function matchSimpleType(mixed $data, array $type, Context $context): mi

private function isPrimitiveType(string $type): bool
{
return in_array($type, ['int', 'integer', 'float', 'double', 'bool', 'boolean', 'string'], true);
return in_array($type, ['int', 'integer', 'float', 'double', 'bool', 'boolean', 'string', 'array'], true);
}

private function testPrimitive(mixed $data, string $type, string $format): bool
{
switch ($type) {
case 'array':
return is_array($data);

case 'integer':
case 'int':
return (string) (int) $data === (string) $data;
Expand All @@ -134,7 +137,7 @@ private function testPrimitive(mixed $data, string $type, string $format): bool
return (string) (bool) $data === (string) $data;

case 'string':
return (string) $data === (string) $data;
return is_string($data);
}

return false;
Expand Down
13 changes: 9 additions & 4 deletions src/Metadata/Driver/TypedPropertiesDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ public function __construct(DriverInterface $delegate, ?ParserInterface $typePar
private function reorderTypes(array $types): array
{
uasort($types, static function ($a, $b) {
$order = ['null' => 0, 'true' => 1, 'false' => 2, 'bool' => 3, 'int' => 4, 'float' => 5, 'string' => 6];
$order = ['null' => 0, 'true' => 1, 'false' => 2, 'bool' => 3, 'int' => 4, 'float' => 5, 'array' => 6, 'string' => 7];

return ($order[$a['name']] ?? 7) <=> ($order[$b['name']] ?? 7);
return ($order[$a['name']] ?? 8) <=> ($order[$b['name']] ?? 8);
});

return $types;
Expand Down Expand Up @@ -119,7 +119,7 @@ public function loadMetadataForClass(ReflectionClass $class): ?ClassMetadata
$this->reorderTypes(
array_map(
fn (string $type) => $this->typeParser->parse($type),
array_filter($reflectionType->getTypes(), [$this, 'shouldTypeHint']),
array_filter($reflectionType->getTypes(), [$this, 'shouldTypeHintInsideUnion']),
),
),
],
Expand Down Expand Up @@ -181,11 +181,16 @@ private function shouldTypeHintUnion(?ReflectionType $reflectionType)
}

foreach ($reflectionType->getTypes() as $type) {
if ($this->shouldTypeHint($type)) {
if ($this->shouldTypeHintInsideUnion($type)) {
return true;
}
}

return false;
}

private function shouldTypeHintInsideUnion(ReflectionNamedType $reflectionType)
{
return $this->shouldTypeHint($reflectionType) || 'array' === $reflectionType->getName();
}
}
2 changes: 1 addition & 1 deletion tests/Fixtures/TypedProperties/UnionTypedProperties.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class UnionTypedProperties
{
private int|bool|float|string $data;
private int|bool|float|string|array $data;

private int|bool|float|string|null $nullableData;

Expand Down
4 changes: 4 additions & 0 deletions tests/Metadata/Driver/UnionTypedPropertiesDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ public function testInferUnionTypesShouldResultInManyTypes()
'params' =>
[
[
[
'name' => 'array',
'params' => [],
],
[
'name' => 'string',
'params' => [],
Expand Down
45 changes: 15 additions & 30 deletions tests/Serializer/JsonSerializationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ protected static function getContent($key)
$outputs['data_float'] = '{"data":1.236}';
$outputs['data_bool'] = '{"data":false}';
$outputs['data_string'] = '{"data":"foo"}';
$outputs['data_array'] = '{"data":[1,2,3]}';
$outputs['data_true'] = '{"data":true}';
$outputs['data_false'] = '{"data":false}';
$outputs['data_author'] = '{"data":{"full_name":"foo"}}';
Expand Down Expand Up @@ -443,46 +444,30 @@ public static function getTypeHintedArraysAndStdClass()
];
}

public function testDeserializingUnionProperties()
public static function getSimpleUnionProperties(): iterable
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped(sprintf('%s requires PHP 8.0', TypedPropertiesDriver::class));

return;
}

$object = new UnionTypedProperties(10000);
self::assertEquals($object, $this->deserialize(static::getContent('data_integer'), UnionTypedProperties::class));

$object = new UnionTypedProperties(1.236);
self::assertEquals($object, $this->deserialize(static::getContent('data_float'), UnionTypedProperties::class));

$object = new UnionTypedProperties(false);
self::assertEquals($object, $this->deserialize(static::getContent('data_bool'), UnionTypedProperties::class));

$object = new UnionTypedProperties('foo');
self::assertEquals($object, $this->deserialize(static::getContent('data_string'), UnionTypedProperties::class));
yield 'int' => [10000, 'data_integer'];
yield [1.236, 'data_float'];
yield [false, 'data_bool'];
yield ['foo', 'data_string'];
yield [[1, 2, 3], 'data_array'];
}

public function testSerializingUnionProperties()
/**
* @dataProvider getSimpleUnionProperties
*/
#[DataProvider('getSimpleUnionProperties')]
public function testUnionProperties($data, string $expected): void
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped(sprintf('%s requires PHP 8.0', TypedPropertiesDriver::class));

return;
}

$serialized = $this->serialize(new UnionTypedProperties(10000));
self::assertEquals(static::getContent('data_integer'), $serialized);

$serialized = $this->serialize(new UnionTypedProperties(1.236));
self::assertEquals(static::getContent('data_float'), $serialized);

$serialized = $this->serialize(new UnionTypedProperties(false));
self::assertEquals(static::getContent('data_bool'), $serialized);

$serialized = $this->serialize(new UnionTypedProperties('foo'));
self::assertEquals(static::getContent('data_string'), $serialized);
$object = new UnionTypedProperties($data);
self::assertEquals($object, $this->deserialize(static::getContent($expected), UnionTypedProperties::class));
self::assertEquals($this->serialize($object), static::getContent($expected));
}

public function testTrueDataType()
Expand Down

0 comments on commit b4285f4

Please sign in to comment.