From da8ce2aee887658756f2b13e7ff3fec3f8e093ed Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Dec 2024 15:41:44 -0500 Subject: [PATCH 01/12] Only keep necessary type specs --- .../src/Core/Json/JsonSerializableType.php | 69 +++++++++++-- .../php-sdk/unions/src/Core/Json/JsonSkip.php | 8 ++ .../unions/src/Core/Types/Discriminant.php | 19 ++++ .../src/Core/Types/DiscriminatedUnion.php | 20 ++++ seed/php-sdk/unions/src/Union/Types/Shape.php | 99 +++++++++++++++++++ .../Seed/Core/Json/DiscriminatedUnionTest.php | 50 ++++++++++ 6 files changed, 258 insertions(+), 7 deletions(-) create mode 100644 seed/php-sdk/unions/src/Core/Json/JsonSkip.php create mode 100644 seed/php-sdk/unions/src/Core/Types/Discriminant.php create mode 100644 seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php create mode 100644 seed/php-sdk/unions/src/Union/Types/Shape.php create mode 100644 seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php diff --git a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php index b777a31a1fb..dcdb7037554 100644 --- a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php +++ b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php @@ -7,8 +7,11 @@ use JsonException; use ReflectionNamedType; use ReflectionProperty; +use Seed\Core\Json\JsonSkip; use Seed\Core\Types\ArrayType; use Seed\Core\Types\Date; +use Seed\Core\Types\Discriminant; +use Seed\Core\Types\DiscriminatedUnion; use Seed\Core\Types\Union; /** @@ -121,13 +124,58 @@ public static function jsonDeserialize(array $data): static $args = []; foreach ($reflectionClass->getProperties() as $property) { + $jsonSkipAttr = $property->getAttributes(JsonSkip::class)[0] ?? null; + if ($jsonSkipAttr) { + continue; + } + $jsonKey = self::getJsonKey($property) ?? $property->getName(); + $propertyName = $property->getName(); if (array_key_exists($jsonKey, $data)) { $value = $data[$jsonKey]; + $dateTypeAttr = null; + $arrayTypeAttr = null; + $unionTypeAttr = null; + $typeName = null; + + // Handle discriminated union + if ($reflectionClass->isSubclassOf(DiscriminatedUnion::class)) { + $discriminantAttr = $property->getAttributes(Discriminant::class)[0] ?? null; + $propertyName = 'value'; + + if ($discriminantAttr && count($discriminantAttr->getArguments()) === 1) { + $existingTypes = $discriminantAttr->getArguments()[0]; + if (array_key_exists($value, $existingTypes)) { + $args['type'] = $value; + $discriminatedType = $existingTypes[$value]; + + if (array_key_exists($value, $data)) { + if ($discriminatedType instanceof Date) { + $dateTypeAttr = $discriminatedType; + } elseif ($discriminatedType instanceof ArrayType) { + $dateTypeAttr = $discriminatedType; + } elseif ($discriminatedType instanceof Union) { + $dateTypeAttr = $discriminatedType; + } + + $value = $data[$value]; + $typeName = $discriminatedType; + } else { + // Inline properties are only possible with objects + $value = JsonDeserializer::deserializeObject($data, $discriminatedType); + } + } else { + $args['type'] = '_unknown'; + } + } + } + // Handle Date annotation - $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if (is_null($dateTypeAttr)) { + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + } if ($dateTypeAttr) { $dateType = $dateTypeAttr->newInstance()->type; if (!is_string($value)) { @@ -139,14 +187,18 @@ public static function jsonDeserialize(array $data): static } // Handle Array annotation - $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if (is_null($arrayTypeAttr)) { + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + } if (is_array($value) && $arrayTypeAttr) { $arrayType = $arrayTypeAttr->newInstance()->type; $value = JsonDeserializer::deserializeArray($value, $arrayType); } // Handle Union annotations - $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if (is_null($unionTypeAttr)) { + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + } if ($unionTypeAttr) { $unionType = $unionTypeAttr->newInstance(); $value = JsonDeserializer::deserializeUnion($value, $unionType); @@ -154,14 +206,17 @@ public static function jsonDeserialize(array $data): static // Handle object $type = $property->getType(); - if (is_array($value) && $type instanceof ReflectionNamedType && !$type->isBuiltin()) { - $value = JsonDeserializer::deserializeObject($value, $type->getName()); + if (!is_null($typeName) || is_array($value) && $type instanceof ReflectionNamedType && !$type->isBuiltin()) { + if (is_null($typeName)) { + $typeName = $type->getName(); + } + $value = JsonDeserializer::deserializeObject($value, $typeName); } - $args[$property->getName()] = $value; + $args[$propertyName] = $value; } else { $defaultValue = $property->getDefaultValue() ?? null; - $args[$property->getName()] = $defaultValue; + $args[$propertyName] = $defaultValue; } } // @phpstan-ignore-next-line diff --git a/seed/php-sdk/unions/src/Core/Json/JsonSkip.php b/seed/php-sdk/unions/src/Core/Json/JsonSkip.php new file mode 100644 index 00000000000..cb950bca072 --- /dev/null +++ b/seed/php-sdk/unions/src/Core/Json/JsonSkip.php @@ -0,0 +1,8 @@ + types + */ + public readonly array $types; + + public function __construct($types) + { + $this->types = $types; + } +} diff --git a/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php b/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php new file mode 100644 index 00000000000..8a85c63009f --- /dev/null +++ b/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php @@ -0,0 +1,20 @@ + Circle::class, 'square' => Square::class,])] + public string $type; + + /** + * @var Circle|Square|mixed + */ + #[JsonSkip] + public mixed $value; + + /** + * @param ?array{ + * type?: 'circle'|'square'|'_unknown', + * value?: Circle|Square|mixed, + * } $options + */ + public function __construct( + private readonly ?array $options = null, + ) { + $this->type = $this->options['type'] ?? '_unknown'; + $this->value = $this->options['value'] ?? null; + } + + public static function circle( + Circle $circle + ): Shape { + return new Shape([ + 'type' => 'circle', + 'circle' => $circle + ]); + } + + public static function square( + Square $square + ): Shape { + return new Shape([ + 'type' => 'square', + 'square' => $square + ]); + } + + public static function _unknown( + mixed $_unknown + ): Shape { + return new Shape([ + '_unknown' => $_unknown + ]); + } + + public function asCircle(): Circle + { + if ($this->type != 'circle') { + throw new \Exception( + "Expected type to be 'circle'; got '$this->type.'" + ); + } + + if (!($this->value instanceof Circle)) { + throw new \Exception( + "Expected value to be instance of Circle." + ); + } + + return $this->value; + } + + public function asSquare(): Square + { + if ($this->type != 'square') { + throw new \Exception( + "Expected type to be 'square'; got '$this->type.'" + ); + } + + if (!($this->value instanceof Square)) { + throw new \Exception( + "Expected value to be instance of Square." + ); + } + + return $this->value; + } +} diff --git a/seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php b/seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php new file mode 100644 index 00000000000..f626227e297 --- /dev/null +++ b/seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php @@ -0,0 +1,50 @@ + 'circle', + 'circle' => [ + 'radius' => 1.0 + ] + ], + JSON_THROW_ON_ERROR + ); + + $shape = Shape::fromJson($expected_json); + + $this->assertTrue($shape instanceof Shape, "Deserialized object must be of type Shape"); + $this->assertEquals($shape->type, 'circle', "Type property must be circle"); + $this->assertNotNull($shape->value, "Shape value must not be null"); + $this->assertTrue($shape->value instanceof Circle); + $this->assertEquals($shape->value->radius, 1.0, "Circle radius must match definition"); + } + + public function testRoundtripSerdeFlat(): void + { + $expected_json = json_encode( + [ + 'type' => 'circle', + 'radius' => 1.0 + ], + JSON_THROW_ON_ERROR + ); + + $shape = Shape::fromJson($expected_json); + + $this->assertTrue($shape instanceof Shape, "Deserialized object must be of type Shape"); + $this->assertEquals($shape->type, 'circle', "Type property must be circle"); + $this->assertNotNull($shape->value, "Shape value must not be null"); + $this->assertTrue($shape->value instanceof Circle); + $this->assertEquals($shape->value->radius, 1.0, "Circle radius must match definition"); + } +} From 14eb0b2ba14791fcb6a4f3288a468206252abfee Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Dec 2024 16:08:46 -0500 Subject: [PATCH 02/12] Serde roundtrip --- .../src/Core/Json/JsonSerializableType.php | 43 +++++++++++++++++-- .../Seed/Core/Json/DiscriminatedUnionTest.php | 16 ++++++- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php index dcdb7037554..74826d3dee4 100644 --- a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php +++ b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php @@ -47,14 +47,47 @@ public function jsonSerialize(): array $reflectionClass = new \ReflectionClass($this); foreach ($reflectionClass->getProperties() as $property) { + $jsonSkipAttr = $property->getAttributes(JsonSkip::class)[0] ?? null; + if ($jsonSkipAttr) { + continue; + } + $jsonKey = self::getJsonKey($property); if ($jsonKey == null) { continue; } $value = $property->getValue($this); + $dateTypeAttr = null; + $arrayTypeAttr = null; + $unionTypeAttr = null; + + // Handle discriminated union + if ($this instanceof DiscriminatedUnion) { + $discriminantAttr = $property->getAttributes(Discriminant::class)[0] ?? null; + + if ($discriminantAttr && count($discriminantAttr->getArguments()) === 1) { + $result[$jsonKey] = $value; + $existingTypes = $discriminantAttr->getArguments()[0]; + $discriminatedType = $existingTypes[$value] ?? null; + + if ($discriminatedType instanceof Date) { + $dateTypeAttr = $discriminatedType; + } elseif ($discriminatedType instanceof ArrayType) { + $dateTypeAttr = $discriminatedType; + } elseif ($discriminatedType instanceof Union) { + $dateTypeAttr = $discriminatedType; + } + + $jsonKey = $value; + $value = $this->value; + } + } + // Handle DateTime properties - $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if (is_null($dateTypeAttr)) { + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + } if ($dateTypeAttr && $value instanceof DateTime) { $dateType = $dateTypeAttr->newInstance()->type; $value = ($dateType === Date::TYPE_DATE) @@ -63,14 +96,18 @@ public function jsonSerialize(): array } // Handle Union annotations - $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if (is_null($unionTypeAttr)) { + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + } if ($unionTypeAttr) { $unionType = $unionTypeAttr->newInstance(); $value = JsonSerializer::serializeUnion($value, $unionType); } // Handle arrays with type annotations - $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if (is_null($arrayTypeAttr)) { + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + } if ($arrayTypeAttr && is_array($value)) { $arrayType = $arrayTypeAttr->newInstance()->type; $value = JsonSerializer::serializeArray($value, $arrayType); diff --git a/seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php b/seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php index f626227e297..7ce81fb21ca 100644 --- a/seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php +++ b/seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php @@ -27,11 +27,12 @@ public function testRoundtripSerde(): void $this->assertNotNull($shape->value, "Shape value must not be null"); $this->assertTrue($shape->value instanceof Circle); $this->assertEquals($shape->value->radius, 1.0, "Circle radius must match definition"); + $this->assertJsonStringEqualsJsonString($shape->toJson(), $expected_json); } public function testRoundtripSerdeFlat(): void { - $expected_json = json_encode( + $flat_json = json_encode( [ 'type' => 'circle', 'radius' => 1.0 @@ -39,12 +40,23 @@ public function testRoundtripSerdeFlat(): void JSON_THROW_ON_ERROR ); - $shape = Shape::fromJson($expected_json); + $expected_json = json_encode( + [ + 'type' => 'circle', + 'circle' => [ + 'radius' => 1.0 + ] + ], + JSON_THROW_ON_ERROR + ); + + $shape = Shape::fromJson($flat_json); $this->assertTrue($shape instanceof Shape, "Deserialized object must be of type Shape"); $this->assertEquals($shape->type, 'circle', "Type property must be circle"); $this->assertNotNull($shape->value, "Shape value must not be null"); $this->assertTrue($shape->value instanceof Circle); $this->assertEquals($shape->value->radius, 1.0, "Circle radius must match definition"); + $this->assertJsonStringEqualsJsonString($shape->toJson(), $expected_json); } } From 19dd04f452ea03b7310a3a5f5e79a01203c7c971 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Dec 2024 17:24:34 -0500 Subject: [PATCH 03/12] More serde tests --- .../src/Core/Json/JsonSerializableType.php | 80 ++++++----- .../src/Types/Types/UnionWithLiteral.php | 85 ++++++++++++ .../src/Types/Types/UnionWithPrimitive.php | 97 +++++++++++++ .../unions/src/Types/Types/UnionWithTime.php | 129 ++++++++++++++++++ .../Seed/Core/Json/DiscriminatedUnionTest.php | 65 +++++++++ 5 files changed, 414 insertions(+), 42 deletions(-) create mode 100644 seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php create mode 100644 seed/php-sdk/unions/src/Types/Types/UnionWithPrimitive.php create mode 100644 seed/php-sdk/unions/src/Types/Types/UnionWithTime.php diff --git a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php index 74826d3dee4..0b174d06b8a 100644 --- a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php +++ b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php @@ -58,9 +58,9 @@ public function jsonSerialize(): array } $value = $property->getValue($this); - $dateTypeAttr = null; - $arrayTypeAttr = null; - $unionTypeAttr = null; + $dateType = null; + $arrayType = null; + $unionType = null; // Handle discriminated union if ($this instanceof DiscriminatedUnion) { @@ -72,11 +72,11 @@ public function jsonSerialize(): array $discriminatedType = $existingTypes[$value] ?? null; if ($discriminatedType instanceof Date) { - $dateTypeAttr = $discriminatedType; + $dateType = $discriminatedType->type; } elseif ($discriminatedType instanceof ArrayType) { - $dateTypeAttr = $discriminatedType; + $arrayType = $discriminatedType->type; } elseif ($discriminatedType instanceof Union) { - $dateTypeAttr = $discriminatedType; + $unionType = $discriminatedType->types; } $jsonKey = $value; @@ -85,30 +85,26 @@ public function jsonSerialize(): array } // Handle DateTime properties - if (is_null($dateTypeAttr)) { - $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; - } - if ($dateTypeAttr && $value instanceof DateTime) { - $dateType = $dateTypeAttr->newInstance()->type; + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if (($dateType || $dateTypeAttr) && $value instanceof DateTime) { + if (is_null($dateType)) { + $dateType = $dateTypeAttr->newInstance()->type; + } $value = ($dateType === Date::TYPE_DATE) ? JsonSerializer::serializeDate($value) : JsonSerializer::serializeDateTime($value); } // Handle Union annotations - if (is_null($unionTypeAttr)) { - $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; - } - if ($unionTypeAttr) { + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if ($unionType || $unionTypeAttr) { $unionType = $unionTypeAttr->newInstance(); $value = JsonSerializer::serializeUnion($value, $unionType); } // Handle arrays with type annotations - if (is_null($arrayTypeAttr)) { - $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; - } - if ($arrayTypeAttr && is_array($value)) { + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if (($arrayType || $arrayTypeAttr) && is_array($value)) { $arrayType = $arrayTypeAttr->newInstance()->type; $value = JsonSerializer::serializeArray($value, $arrayType); } @@ -172,17 +168,17 @@ public static function jsonDeserialize(array $data): static if (array_key_exists($jsonKey, $data)) { $value = $data[$jsonKey]; - $dateTypeAttr = null; - $arrayTypeAttr = null; - $unionTypeAttr = null; + $dateType = null; + $arrayType = null; + $unionType = null; $typeName = null; // Handle discriminated union if ($reflectionClass->isSubclassOf(DiscriminatedUnion::class)) { $discriminantAttr = $property->getAttributes(Discriminant::class)[0] ?? null; - $propertyName = 'value'; if ($discriminantAttr && count($discriminantAttr->getArguments()) === 1) { + $propertyName = 'value'; $existingTypes = $discriminantAttr->getArguments()[0]; if (array_key_exists($value, $existingTypes)) { $args['type'] = $value; @@ -190,11 +186,11 @@ public static function jsonDeserialize(array $data): static if (array_key_exists($value, $data)) { if ($discriminatedType instanceof Date) { - $dateTypeAttr = $discriminatedType; + $dateType = $discriminatedType->type; } elseif ($discriminatedType instanceof ArrayType) { - $dateTypeAttr = $discriminatedType; + $arrayType = $discriminatedType->type; } elseif ($discriminatedType instanceof Union) { - $dateTypeAttr = $discriminatedType; + $unionType = $discriminatedType->types; } $value = $data[$value]; @@ -210,11 +206,11 @@ public static function jsonDeserialize(array $data): static } // Handle Date annotation - if (is_null($dateTypeAttr)) { - $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; - } - if ($dateTypeAttr) { - $dateType = $dateTypeAttr->newInstance()->type; + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if ($dateType || $dateTypeAttr) { + if (is_null($dateType)) { + $dateType = $dateTypeAttr->newInstance()->type; + } if (!is_string($value)) { throw new JsonException("Unexpected non-string type for date."); } @@ -224,26 +220,26 @@ public static function jsonDeserialize(array $data): static } // Handle Array annotation - if (is_null($arrayTypeAttr)) { - $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; - } - if (is_array($value) && $arrayTypeAttr) { - $arrayType = $arrayTypeAttr->newInstance()->type; + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if (is_array($value) && ($arrayTypeAttr || $arrayType)) { + if (is_null($arrayType)) { + $arrayType = $arrayTypeAttr->newInstance()->type; + } $value = JsonDeserializer::deserializeArray($value, $arrayType); } // Handle Union annotations - if (is_null($unionTypeAttr)) { - $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; - } - if ($unionTypeAttr) { - $unionType = $unionTypeAttr->newInstance(); + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if ($unionType || $unionTypeAttr) { + if (is_null($unionType)) { + $unionType = $unionTypeAttr->newInstance(); + } $value = JsonDeserializer::deserializeUnion($value, $unionType); } // Handle object $type = $property->getType(); - if (!is_null($typeName) || is_array($value) && $type instanceof ReflectionNamedType && !$type->isBuiltin()) { + if (is_array($value) && ($typeName || $type instanceof ReflectionNamedType && !$type->isBuiltin())) { if (is_null($typeName)) { $typeName = $type->getName(); } diff --git a/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php b/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php new file mode 100644 index 00000000000..71ffea98e80 --- /dev/null +++ b/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php @@ -0,0 +1,85 @@ + 'fern'])] + public string $type; + + /** + * @var 'fern'|mixed + */ + #[JsonSkip] + public mixed $value; + + /** + * @param ?array{ + * base?: 'base', + * type?: 'fern'|'_unknown', + * value?: 'fern'|mixed, + * } $options + */ + public function __construct( + private readonly ?array $options = null, + ) { + if (is_null($options)) { + throw new \Exception("Missing all required properties"); + } + if (!array_key_exists('base', $options)) { + throw new \Exception("Missing required property 'base'"); + } + $this->base = $this->options['base']; + $this->type = $this->options['type'] ?? '_unknown'; + $this->value = $this->options['value'] ?? null; + } + + public static function fern(): UnionWithLiteral + { + return new UnionWithLiteral([ + 'base' => 'base', + 'type' => 'fern', + 'fern' => 'fern' + ]); + } + + public static function _unknown( + mixed $_unknown + ): UnionWithLiteral { + return new UnionWithLiteral([ + '_unknown' => $_unknown + ]); + } + + public function asFern(): string + { + if ($this->type != 'fern') { + throw new \Exception( + "Expected type to be 'fern'; got '$this->type.'" + ); + } + + if ($this->type === 'fern') { + throw new \Exception( + "Expected value to be \"fern\"." + ); + } + + return $this->value; + } +} diff --git a/seed/php-sdk/unions/src/Types/Types/UnionWithPrimitive.php b/seed/php-sdk/unions/src/Types/Types/UnionWithPrimitive.php new file mode 100644 index 00000000000..9fdb6ab0b75 --- /dev/null +++ b/seed/php-sdk/unions/src/Types/Types/UnionWithPrimitive.php @@ -0,0 +1,97 @@ + 'integer', 'string' => 'string'])] + public string $type; + + /** + * @var int|string|mixed + */ + #[JsonSkip] + public mixed $value; + + /** + * @param ?array{ + * type?: 'integer'|'string'|'_unknown', + * value?: int|string|mixed, + * } $options + */ + public function __construct( + private readonly ?array $options = null, + ) { + $this->type = $this->options['type'] ?? '_unknown'; + $this->value = $this->options['value'] ?? null; + } + + public static function integer( + int $integer + ): UnionWithPrimitive { + return new UnionWithPrimitive([ + 'type' => 'integer', + 'integer' => $integer + ]); + } + + public static function string( + string $string + ): UnionWithPrimitive { + return new UnionWithPrimitive([ + 'type' => 'string', + 'string' => $string + ]); + } + + public static function _unknown( + mixed $_unknown + ): UnionWithPrimitive { + return new UnionWithPrimitive([ + '_unknown' => $_unknown + ]); + } + + public function asInteger(): int + { + if ($this->type != 'circle') { + throw new \Exception( + "Expected type to be 'integer'; got '$this->type.'" + ); + } + + if (!is_int($this->value)) { + throw new \Exception( + "Expected value to be int." + ); + } + + return $this->value; + } + + public function asString(): string + { + if ($this->type != 'string') { + throw new \Exception( + "Expected type to be 'string'; got '$this->type.'" + ); + } + + if (!is_string($this->value)) { + throw new \Exception( + "Expected value to be string." + ); + } + + return $this->value; + } +} diff --git a/seed/php-sdk/unions/src/Types/Types/UnionWithTime.php b/seed/php-sdk/unions/src/Types/Types/UnionWithTime.php new file mode 100644 index 00000000000..8fa6e879a81 --- /dev/null +++ b/seed/php-sdk/unions/src/Types/Types/UnionWithTime.php @@ -0,0 +1,129 @@ + 'integer', + 'date' => new Date(Date::TYPE_DATE), + 'datetime' => new Date(Date::TYPE_DATETIME), + ])] + public string $type; + + /** + * @var int|DateTime|mixed + */ + #[JsonSkip] + public mixed $value; + + /** + * @param ?array{ + * type?: 'value'|'date'|'datetime'|'_unknown', + * value?: int|DateTime|mixed, + * } $options + */ + public function __construct( + private readonly ?array $options = null, + ) { + $this->type = $this->options['type'] ?? '_unknown'; + $this->value = $this->options['value'] ?? null; + } + + public static function value( + int $value + ): UnionWithTime { + return new UnionWithTime([ + 'type' => 'value', + 'value' => $value + ]); + } + + public static function date( + DateTime $date + ): UnionWithTime { + return new UnionWithTime([ + 'type' => 'date', + 'date' => $date + ]); + } + + public static function datetime( + DateTime $datetime + ): UnionWithTime { + return new UnionWithTime([ + 'type' => 'datetime', + 'datetime' => $datetime + ]); + } + + public static function _unknown( + mixed $_unknown + ): UnionWithTime { + return new UnionWithTime([ + '_unknown' => $_unknown + ]); + } + + public function asValue(): int + { + if ($this->type != 'value') { + throw new \Exception( + "Expected type to be 'value'; got '$this->type.'" + ); + } + + if (!is_int($this->value)) { + throw new \Exception( + "Expected value to be int." + ); + } + + return $this->value; + } + + public function asDate(): DateTime + { + if ($this->type != 'date') { + throw new \Exception( + "Expected type to be 'date'; got '$this->type.'" + ); + } + + if (!($this->value instanceof DateTime)) { + throw new \Exception( + "Expected value to be instance of DateTime." + ); + } + + return $this->value; + } + + public function asDatetime(): DateTime + { + if ($this->type != 'datetime') { + throw new \Exception( + "Expected type to be 'datetime'; got '$this->type.'" + ); + } + + if (!($this->value instanceof DateTime)) { + throw new \Exception( + "Expected value to be instance of DateTime." + ); + } + + return $this->value; + } +} diff --git a/seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php b/seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php index 7ce81fb21ca..cea3dd1c17b 100644 --- a/seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php +++ b/seed/php-sdk/unions/tests/Seed/Core/Json/DiscriminatedUnionTest.php @@ -2,7 +2,11 @@ namespace Seed\Tests\Core\Json; +use DateTime; use PHPUnit\Framework\TestCase; +use Seed\Types\Types\UnionWithLiteral; +use Seed\Types\Types\UnionWithPrimitive; +use Seed\Types\Types\UnionWithTime; use Seed\Union\Types\Circle; use Seed\Union\Types\Shape; @@ -59,4 +63,65 @@ public function testRoundtripSerdeFlat(): void $this->assertEquals($shape->value->radius, 1.0, "Circle radius must match definition"); $this->assertJsonStringEqualsJsonString($shape->toJson(), $expected_json); } + + public function testRoundTripSerdeWithTime(): void + { + $expected_json = json_encode( + [ + 'type' => 'date', + 'date' => '2024-01-01' + ], + JSON_THROW_ON_ERROR + ); + + $dateUnion = UnionWithTime::fromJson($expected_json); + + $this->assertTrue($dateUnion instanceof UnionWithTime, "Deserialized object must be of type UnionWithTime"); + $this->assertEquals($dateUnion->type, 'date', "Type property must be date"); + $this->assertNotNull($dateUnion->value, "UnionWithTime value must not be null"); + $this->assertTrue($dateUnion->value instanceof DateTime); + $this->assertEquals($dateUnion->value->format('Y-m-d'), '2024-01-01', "DateTime field must match definition"); + $this->assertJsonStringEqualsJsonString($dateUnion->toJson(), $expected_json); + } + + public function testRoundTripSerdeWithPrimitive(): void + { + $expected_json = json_encode( + [ + 'type' => 'integer', + 'integer' => 5 + ], + JSON_THROW_ON_ERROR + ); + + $primitiveUnion = UnionWithPrimitive::fromJson($expected_json); + + $this->assertTrue($primitiveUnion instanceof UnionWithPrimitive, "Deserialized object must be of type UnionWithPrimitive"); + $this->assertEquals($primitiveUnion->type, 'integer', "Type property must be integer"); + $this->assertNotNull($primitiveUnion->value, "UnionWithPrimitive value must not be null"); + $this->assertTrue(is_integer($primitiveUnion->value)); + $this->assertEquals($primitiveUnion->value, 5, "primitive field must match definition"); + $this->assertJsonStringEqualsJsonString($primitiveUnion->toJson(), $expected_json); + } + + public function testRoundTripSerdeWithLiteral(): void + { + $expected_json = json_encode( + [ + 'base' => 'base', + 'type' => 'fern', + 'fern' => 'fern', + ], + JSON_THROW_ON_ERROR + ); + + $literalUnion = UnionWithLiteral::fromJson($expected_json); + + $this->assertTrue($literalUnion instanceof UnionWithLiteral, "Deserialized object must be of type UnionWithLiteral"); + $this->assertEquals($literalUnion->type, 'fern', "Type property must be fern"); + $this->assertNotNull($literalUnion->value, "UnionWithLiteral value must not be null"); + $this->assertEquals($literalUnion->value, 'fern', "literal field must match definition"); + $this->assertEquals($literalUnion->base, 'base', "base field must match definition"); + $this->assertJsonStringEqualsJsonString($literalUnion->toJson(), $expected_json); + } } From ecbd4752c907bb34d102ce0bf31eb7a02adcfe11 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Dec 2024 17:39:08 -0500 Subject: [PATCH 04/12] Fix analyze a little bit --- .../src/Core/Json/JsonSerializableType.php | 18 +++++++++++------- .../unions/src/Core/Types/Discriminant.php | 9 ++------- .../src/Types/Types/UnionWithLiteral.php | 9 +++++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php index 0b174d06b8a..fed798076b7 100644 --- a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php +++ b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php @@ -87,7 +87,7 @@ public function jsonSerialize(): array // Handle DateTime properties $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; if (($dateType || $dateTypeAttr) && $value instanceof DateTime) { - if (is_null($dateType)) { + if ($dateTypeAttr && is_null($dateType)) { $dateType = $dateTypeAttr->newInstance()->type; } $value = ($dateType === Date::TYPE_DATE) @@ -98,14 +98,18 @@ public function jsonSerialize(): array // Handle Union annotations $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; if ($unionType || $unionTypeAttr) { - $unionType = $unionTypeAttr->newInstance(); + if ($unionTypeAttr && is_null($unionType)) { + $unionType = $unionTypeAttr->newInstance(); + } $value = JsonSerializer::serializeUnion($value, $unionType); } // Handle arrays with type annotations $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; if (($arrayType || $arrayTypeAttr) && is_array($value)) { - $arrayType = $arrayTypeAttr->newInstance()->type; + if ($arrayTypeAttr && is_null($arrayType)) { + $arrayType = $arrayTypeAttr->newInstance()->type; + } $value = JsonSerializer::serializeArray($value, $arrayType); } @@ -208,7 +212,7 @@ public static function jsonDeserialize(array $data): static // Handle Date annotation $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; if ($dateType || $dateTypeAttr) { - if (is_null($dateType)) { + if ($dateTypeAttr && is_null($dateType)) { $dateType = $dateTypeAttr->newInstance()->type; } if (!is_string($value)) { @@ -222,7 +226,7 @@ public static function jsonDeserialize(array $data): static // Handle Array annotation $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; if (is_array($value) && ($arrayTypeAttr || $arrayType)) { - if (is_null($arrayType)) { + if ($arrayTypeAttr && is_null($arrayType)) { $arrayType = $arrayTypeAttr->newInstance()->type; } $value = JsonDeserializer::deserializeArray($value, $arrayType); @@ -231,7 +235,7 @@ public static function jsonDeserialize(array $data): static // Handle Union annotations $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; if ($unionType || $unionTypeAttr) { - if (is_null($unionType)) { + if ($unionTypeAttr && is_null($unionType)) { $unionType = $unionTypeAttr->newInstance(); } $value = JsonDeserializer::deserializeUnion($value, $unionType); @@ -240,7 +244,7 @@ public static function jsonDeserialize(array $data): static // Handle object $type = $property->getType(); if (is_array($value) && ($typeName || $type instanceof ReflectionNamedType && !$type->isBuiltin())) { - if (is_null($typeName)) { + if (($type instanceof ReflectionNamedType && !$type->isBuiltin()) && is_null($typeName)) { $typeName = $type->getName(); } $value = JsonDeserializer::deserializeObject($value, $typeName); diff --git a/seed/php-sdk/unions/src/Core/Types/Discriminant.php b/seed/php-sdk/unions/src/Core/Types/Discriminant.php index c30f1896837..e05b9c54940 100644 --- a/seed/php-sdk/unions/src/Core/Types/Discriminant.php +++ b/seed/php-sdk/unions/src/Core/Types/Discriminant.php @@ -8,12 +8,7 @@ class Discriminant { /** - * @var array types + * @param array $types */ - public readonly array $types; - - public function __construct($types) - { - $this->types = $types; - } + public function __construct(public readonly array $types) {} } diff --git a/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php b/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php index 71ffea98e80..5406e76fe75 100644 --- a/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php +++ b/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php @@ -38,13 +38,14 @@ class UnionWithLiteral extends DiscriminatedUnion public function __construct( private readonly ?array $options = null, ) { - if (is_null($options)) { + if (is_null($this->options)) { throw new \Exception("Missing all required properties"); } - if (!array_key_exists('base', $options)) { + if (array_key_exists('base', $this->options)) { + $this->base = $this->options['base']; + } else { throw new \Exception("Missing required property 'base'"); } - $this->base = $this->options['base']; $this->type = $this->options['type'] ?? '_unknown'; $this->value = $this->options['value'] ?? null; } @@ -74,7 +75,7 @@ public function asFern(): string ); } - if ($this->type === 'fern') { + if ($this->value !== 'fern') { throw new \Exception( "Expected value to be \"fern\"." ); From 2843d3b84389bf1e9f69422dfcb1fc68e2d38e0d Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 17 Dec 2024 17:50:02 -0500 Subject: [PATCH 05/12] Fix analyze --- .../src/Core/Json/JsonSerializableType.php | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php index fed798076b7..a128ce37d13 100644 --- a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php +++ b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php @@ -76,7 +76,7 @@ public function jsonSerialize(): array } elseif ($discriminatedType instanceof ArrayType) { $arrayType = $discriminatedType->type; } elseif ($discriminatedType instanceof Union) { - $unionType = $discriminatedType->types; + $unionType = $discriminatedType; } $jsonKey = $value; @@ -101,7 +101,9 @@ public function jsonSerialize(): array if ($unionTypeAttr && is_null($unionType)) { $unionType = $unionTypeAttr->newInstance(); } - $value = JsonSerializer::serializeUnion($value, $unionType); + if ($unionType) { + $value = JsonSerializer::serializeUnion($value, $unionType); + } } // Handle arrays with type annotations @@ -110,7 +112,9 @@ public function jsonSerialize(): array if ($arrayTypeAttr && is_null($arrayType)) { $arrayType = $arrayTypeAttr->newInstance()->type; } - $value = JsonSerializer::serializeArray($value, $arrayType); + if ($arrayType) { + $value = JsonSerializer::serializeArray($value, $arrayType); + } } // Handle object @@ -184,17 +188,17 @@ public static function jsonDeserialize(array $data): static if ($discriminantAttr && count($discriminantAttr->getArguments()) === 1) { $propertyName = 'value'; $existingTypes = $discriminantAttr->getArguments()[0]; - if (array_key_exists($value, $existingTypes)) { + if (is_string($value) && array_key_exists($value, $existingTypes)) { $args['type'] = $value; $discriminatedType = $existingTypes[$value]; - if (array_key_exists($value, $data)) { + if (is_string($value) && array_key_exists($value, $data)) { if ($discriminatedType instanceof Date) { $dateType = $discriminatedType->type; } elseif ($discriminatedType instanceof ArrayType) { $arrayType = $discriminatedType->type; } elseif ($discriminatedType instanceof Union) { - $unionType = $discriminatedType->types; + $unionType = $discriminatedType; } $value = $data[$value]; @@ -229,7 +233,9 @@ public static function jsonDeserialize(array $data): static if ($arrayTypeAttr && is_null($arrayType)) { $arrayType = $arrayTypeAttr->newInstance()->type; } - $value = JsonDeserializer::deserializeArray($value, $arrayType); + if ($arrayType) { + $value = JsonDeserializer::deserializeArray($value, $arrayType); + } } // Handle Union annotations @@ -238,7 +244,9 @@ public static function jsonDeserialize(array $data): static if ($unionTypeAttr && is_null($unionType)) { $unionType = $unionTypeAttr->newInstance(); } - $value = JsonDeserializer::deserializeUnion($value, $unionType); + if ($unionType) { + $value = JsonDeserializer::deserializeUnion($value, $unionType); + } } // Handle object From 9c2a08452326526cd66980f530a9ffe8e4cd7478 Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 19 Dec 2024 17:55:26 -0500 Subject: [PATCH 06/12] Throw instead of checking with an if and serializing nothing if subclass of disc union doesn't have the appropriate arguments --- .../src/Core/Json/JsonSerializableType.php | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php index a128ce37d13..f1ee029987a 100644 --- a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php +++ b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php @@ -66,22 +66,26 @@ public function jsonSerialize(): array if ($this instanceof DiscriminatedUnion) { $discriminantAttr = $property->getAttributes(Discriminant::class)[0] ?? null; - if ($discriminantAttr && count($discriminantAttr->getArguments()) === 1) { - $result[$jsonKey] = $value; - $existingTypes = $discriminantAttr->getArguments()[0]; - $discriminatedType = $existingTypes[$value] ?? null; - - if ($discriminatedType instanceof Date) { - $dateType = $discriminatedType->type; - } elseif ($discriminatedType instanceof ArrayType) { - $arrayType = $discriminatedType->type; - } elseif ($discriminatedType instanceof Union) { - $unionType = $discriminatedType; - } + if (is_null($discriminantAttr) || count($discriminantAttr->getArguments()) !== 1) { + throw new \InvalidArgumentException( + "Discriminant attribute must be defined and have exactly one argument for a subclass of DiscriminatedUnion" + ); + } - $jsonKey = $value; - $value = $this->value; + $result[$jsonKey] = $value; + $existingTypes = $discriminantAttr->getArguments()[0]; + $discriminatedType = $existingTypes[$value] ?? null; + + if ($discriminatedType instanceof Date) { + $dateType = $discriminatedType->type; + } elseif ($discriminatedType instanceof ArrayType) { + $arrayType = $discriminatedType->type; + } elseif ($discriminatedType instanceof Union) { + $unionType = $discriminatedType; } + + $jsonKey = $value; + $value = $this->value; } // Handle DateTime properties From c81db477cfb1f203fbb836dc36ea266d8ece73d0 Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 19 Dec 2024 18:07:34 -0500 Subject: [PATCH 07/12] fix test --- .../src/Core/Json/JsonSerializableType.php | 62 ++++++++++--------- .../src/Core/Types/DiscriminatedUnion.php | 1 - 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php index f1ee029987a..76e638993a7 100644 --- a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php +++ b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php @@ -63,12 +63,11 @@ public function jsonSerialize(): array $unionType = null; // Handle discriminated union - if ($this instanceof DiscriminatedUnion) { - $discriminantAttr = $property->getAttributes(Discriminant::class)[0] ?? null; - - if (is_null($discriminantAttr) || count($discriminantAttr->getArguments()) !== 1) { + $discriminantAttr = $property->getAttributes(Discriminant::class)[0] ?? null; + if ($discriminantAttr && $this instanceof DiscriminatedUnion) { + if (count($discriminantAttr->getArguments()) !== 1) { throw new \InvalidArgumentException( - "Discriminant attribute must be defined and have exactly one argument for a subclass of DiscriminatedUnion" + "Discriminant attribute must have exactly one argument for a subclass of DiscriminatedUnion" ); } @@ -186,34 +185,37 @@ public static function jsonDeserialize(array $data): static $typeName = null; // Handle discriminated union - if ($reflectionClass->isSubclassOf(DiscriminatedUnion::class)) { - $discriminantAttr = $property->getAttributes(Discriminant::class)[0] ?? null; - - if ($discriminantAttr && count($discriminantAttr->getArguments()) === 1) { - $propertyName = 'value'; - $existingTypes = $discriminantAttr->getArguments()[0]; - if (is_string($value) && array_key_exists($value, $existingTypes)) { - $args['type'] = $value; - $discriminatedType = $existingTypes[$value]; - - if (is_string($value) && array_key_exists($value, $data)) { - if ($discriminatedType instanceof Date) { - $dateType = $discriminatedType->type; - } elseif ($discriminatedType instanceof ArrayType) { - $arrayType = $discriminatedType->type; - } elseif ($discriminatedType instanceof Union) { - $unionType = $discriminatedType; - } - - $value = $data[$value]; - $typeName = $discriminatedType; - } else { - // Inline properties are only possible with objects - $value = JsonDeserializer::deserializeObject($data, $discriminatedType); + $discriminantAttr = $property->getAttributes(Discriminant::class)[0] ?? null; + if ($discriminantAttr && $reflectionClass->isSubclassOf(DiscriminatedUnion::class)) { + if (count($discriminantAttr->getArguments()) !== 1) { + throw new \InvalidArgumentException( + "Discriminant attribute must have exactly one argument for a subclass of DiscriminatedUnion" + ); + } + + $propertyName = 'value'; + $existingTypes = $discriminantAttr->getArguments()[0]; + if (is_string($value) && array_key_exists($value, $existingTypes)) { + $args['type'] = $value; + $discriminatedType = $existingTypes[$value]; + + if (is_string($value) && array_key_exists($value, $data)) { + if ($discriminatedType instanceof Date) { + $dateType = $discriminatedType->type; + } elseif ($discriminatedType instanceof ArrayType) { + $arrayType = $discriminatedType->type; + } elseif ($discriminatedType instanceof Union) { + $unionType = $discriminatedType; } + + $value = $data[$value]; + $typeName = $discriminatedType; } else { - $args['type'] = '_unknown'; + // Inline properties are only possible with objects + $value = JsonDeserializer::deserializeObject($data, $discriminatedType); } + } else { + $args['type'] = '_unknown'; } } diff --git a/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php b/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php index 8a85c63009f..25d9c229c9d 100644 --- a/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php +++ b/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php @@ -10,7 +10,6 @@ abstract class DiscriminatedUnion extends JsonSerializableType /** * @var string type */ - #[JsonProperty('type')] public string $type; /** From 4eb48634655809ffad46a152ddb1906188b641ce Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 19 Dec 2024 19:56:05 -0500 Subject: [PATCH 08/12] Change discriminant declaration --- seed/php-sdk/unions/src/Core/Types/Discriminant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/php-sdk/unions/src/Core/Types/Discriminant.php b/seed/php-sdk/unions/src/Core/Types/Discriminant.php index e05b9c54940..5095ccfeb9c 100644 --- a/seed/php-sdk/unions/src/Core/Types/Discriminant.php +++ b/seed/php-sdk/unions/src/Core/Types/Discriminant.php @@ -8,7 +8,7 @@ class Discriminant { /** - * @param array $types + * @param array $types */ public function __construct(public readonly array $types) {} } From 9820b5c592c3a49cdd8fe192820c206534f5e0e9 Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 19 Dec 2024 19:57:36 -0500 Subject: [PATCH 09/12] Fix analyze --- seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php | 1 - 1 file changed, 1 deletion(-) diff --git a/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php b/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php index 25d9c229c9d..df9d77dcf7d 100644 --- a/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php +++ b/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php @@ -2,7 +2,6 @@ namespace Seed\Core\Types; -use Seed\Core\Json\JsonProperty; use Seed\Core\Json\JsonSerializableType; abstract class DiscriminatedUnion extends JsonSerializableType From 86214aaaaf047813ff108304ba3cbb924e9ba069 Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 19 Dec 2024 20:02:56 -0500 Subject: [PATCH 10/12] Make base mandatory in options array --- .../unions/src/Types/Types/UnionWithLiteral.php | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php b/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php index 5406e76fe75..79eb8c18925 100644 --- a/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php +++ b/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php @@ -29,23 +29,16 @@ class UnionWithLiteral extends DiscriminatedUnion public mixed $value; /** - * @param ?array{ - * base?: 'base', + * @param array{ + * base: 'base', * type?: 'fern'|'_unknown', * value?: 'fern'|mixed, * } $options */ public function __construct( - private readonly ?array $options = null, + private readonly array $options, ) { - if (is_null($this->options)) { - throw new \Exception("Missing all required properties"); - } - if (array_key_exists('base', $this->options)) { - $this->base = $this->options['base']; - } else { - throw new \Exception("Missing required property 'base'"); - } + $this->base = $this->options['base']; $this->type = $this->options['type'] ?? '_unknown'; $this->value = $this->options['value'] ?? null; } @@ -63,6 +56,7 @@ public static function _unknown( mixed $_unknown ): UnionWithLiteral { return new UnionWithLiteral([ + 'base' => 'base', '_unknown' => $_unknown ]); } From 39ad19f24714968c439d8c9ea16ee1e51b0291b7 Mon Sep 17 00:00:00 2001 From: Alberto Date: Thu, 19 Dec 2024 20:16:39 -0500 Subject: [PATCH 11/12] Remove trailing comma --- seed/php-sdk/unions/src/Union/Types/Shape.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/php-sdk/unions/src/Union/Types/Shape.php b/seed/php-sdk/unions/src/Union/Types/Shape.php index e52f2916ff0..8c75b806dd4 100644 --- a/seed/php-sdk/unions/src/Union/Types/Shape.php +++ b/seed/php-sdk/unions/src/Union/Types/Shape.php @@ -15,7 +15,7 @@ class Shape extends DiscriminatedUnion * @var 'circle'|'square'|'_unknown' $type */ #[JsonProperty('type')] - #[Discriminant(['circle' => Circle::class, 'square' => Square::class,])] + #[Discriminant(['circle' => Circle::class, 'square' => Square::class])] public string $type; /** From d4d29aeba4f8ff100799dd678ef830cd62f4c6ba Mon Sep 17 00:00:00 2001 From: Alberto Date: Fri, 20 Dec 2024 09:43:26 -0500 Subject: [PATCH 12/12] Make sure we pass in the right value variables --- seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php | 6 +++--- seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php | 3 +++ seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php | 4 ++-- seed/php-sdk/unions/src/Types/Types/UnionWithPrimitive.php | 6 +++--- seed/php-sdk/unions/src/Union/Types/Shape.php | 6 +++--- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php index 76e638993a7..5d6c39af49e 100644 --- a/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php +++ b/seed/php-sdk/unions/src/Core/Json/JsonSerializableType.php @@ -193,10 +193,10 @@ public static function jsonDeserialize(array $data): static ); } - $propertyName = 'value'; + $propertyName = DiscriminatedUnion::VALUE_KEY; $existingTypes = $discriminantAttr->getArguments()[0]; if (is_string($value) && array_key_exists($value, $existingTypes)) { - $args['type'] = $value; + $args[DiscriminatedUnion::TYPE_KEY] = $value; $discriminatedType = $existingTypes[$value]; if (is_string($value) && array_key_exists($value, $data)) { @@ -215,7 +215,7 @@ public static function jsonDeserialize(array $data): static $value = JsonDeserializer::deserializeObject($data, $discriminatedType); } } else { - $args['type'] = '_unknown'; + $args[DiscriminatedUnion::TYPE_KEY] = '_unknown'; } } diff --git a/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php b/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php index df9d77dcf7d..a29c2036a46 100644 --- a/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php +++ b/seed/php-sdk/unions/src/Core/Types/DiscriminatedUnion.php @@ -6,6 +6,9 @@ abstract class DiscriminatedUnion extends JsonSerializableType { + public const string TYPE_KEY = 'type'; + public const string VALUE_KEY = 'value'; + /** * @var string type */ diff --git a/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php b/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php index 79eb8c18925..7929e226848 100644 --- a/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php +++ b/seed/php-sdk/unions/src/Types/Types/UnionWithLiteral.php @@ -48,7 +48,7 @@ public static function fern(): UnionWithLiteral return new UnionWithLiteral([ 'base' => 'base', 'type' => 'fern', - 'fern' => 'fern' + 'value' => 'fern' ]); } @@ -57,7 +57,7 @@ public static function _unknown( ): UnionWithLiteral { return new UnionWithLiteral([ 'base' => 'base', - '_unknown' => $_unknown + 'value' => $_unknown ]); } diff --git a/seed/php-sdk/unions/src/Types/Types/UnionWithPrimitive.php b/seed/php-sdk/unions/src/Types/Types/UnionWithPrimitive.php index 9fdb6ab0b75..701b28a58ec 100644 --- a/seed/php-sdk/unions/src/Types/Types/UnionWithPrimitive.php +++ b/seed/php-sdk/unions/src/Types/Types/UnionWithPrimitive.php @@ -40,7 +40,7 @@ public static function integer( ): UnionWithPrimitive { return new UnionWithPrimitive([ 'type' => 'integer', - 'integer' => $integer + 'value' => $integer ]); } @@ -49,7 +49,7 @@ public static function string( ): UnionWithPrimitive { return new UnionWithPrimitive([ 'type' => 'string', - 'string' => $string + 'value' => $string ]); } @@ -57,7 +57,7 @@ public static function _unknown( mixed $_unknown ): UnionWithPrimitive { return new UnionWithPrimitive([ - '_unknown' => $_unknown + 'value' => $_unknown ]); } diff --git a/seed/php-sdk/unions/src/Union/Types/Shape.php b/seed/php-sdk/unions/src/Union/Types/Shape.php index 8c75b806dd4..682f723f653 100644 --- a/seed/php-sdk/unions/src/Union/Types/Shape.php +++ b/seed/php-sdk/unions/src/Union/Types/Shape.php @@ -42,7 +42,7 @@ public static function circle( ): Shape { return new Shape([ 'type' => 'circle', - 'circle' => $circle + 'value' => $circle ]); } @@ -51,7 +51,7 @@ public static function square( ): Shape { return new Shape([ 'type' => 'square', - 'square' => $square + 'value' => $square ]); } @@ -59,7 +59,7 @@ public static function _unknown( mixed $_unknown ): Shape { return new Shape([ - '_unknown' => $_unknown + 'value' => $_unknown ]); }