Skip to content

Commit

Permalink
feat(php): update serde for backwards compatible constructors (#4710)
Browse files Browse the repository at this point in the history
  • Loading branch information
dcb6 authored Sep 20, 2024
1 parent 9f96f7b commit 99045b3
Show file tree
Hide file tree
Showing 1,815 changed files with 50,239 additions and 17,830 deletions.
17 changes: 12 additions & 5 deletions generators/php/codegen/src/asIs/DateArrayTypeTest.Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,21 @@
class DateArrayType extends SerializableType
{
/**
* @param string[] $dates
* @var string[] $dates
*/
#[ArrayType(['date'])]
#[JsonProperty('dates')]
public array $dates;

/**
* @param array{
* dates: string[],
* } $values
*/
public function __construct(
// Array of dates
#[ArrayType(['date'])]
#[JsonProperty('dates')]
public array $dates
array $values,
) {
$this->dates = $values['dates'];
}
}

Expand Down
49 changes: 32 additions & 17 deletions generators/php/codegen/src/asIs/EmptyArraysTest.Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,39 @@
class EmptyArraysType extends SerializableType
{
/**
* @param string[] $emptyStringArray
* @param array<string, string|null> $emptyMapArray
* @param array<string|null> $emptyDatesArray
* @var string[] $emptyStringArray
*/
#[ArrayType(['string'])]
#[JsonProperty('empty_string_array')]
public array $emptyStringArray;

/**
* @var array<string, string|null> $emptyMapArray
*/
#[JsonProperty('empty_map_array')]
#[ArrayType(['integer' => new Union('string', 'null')])]
public array $emptyMapArray;

/**
* @var array<string|null> $emptyDatesArray
*/
#[ArrayType([new Union('date', 'null')])]
#[JsonProperty('empty_dates_array')]
public array $emptyDatesArray;

/**
* @param array{
* emptyStringArray: string[],
* emptyMapArray: array<string, string|null>,
* emptyDatesArray: array<string|null>,
* } $values
*/
public function __construct(
#[ArrayType(['string'])]
#[JsonProperty('empty_string_array')]
public array $emptyStringArray,

#[ArrayType(['integer' => new Union('string', 'null')])]
#[JsonProperty('empty_map_array')]
public array $emptyMapArray,

#[ArrayType([new Union('date', 'null')])]
#[JsonProperty('empty_dates_array')]
public array $emptyDatesArray
)
{
array $values,
) {
$this->emptyStringArray = $values['emptyStringArray'];
$this->emptyMapArray = $values['emptyMapArray'];
$this->emptyDatesArray = $values['emptyDatesArray'];
}
}

Expand All @@ -55,4 +70,4 @@ public function testEmptyArrays(): void
$this->assertEmpty($object->emptyMapArray, 'empty_map_array should be empty.');
$this->assertEmpty($object->emptyDatesArray, 'empty_dates_array should be empty.');
}
}
}
20 changes: 15 additions & 5 deletions generators/php/codegen/src/asIs/InvalidTypesTest.Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,21 @@

class InvalidType extends SerializableType
{
/**
* @var int $integerProperty
*/
#[JsonProperty('integer_property')]
public int $integerProperty;

/**
* @param array{
* integerProperty: int,
* } $values
*/
public function __construct(
#[JsonProperty('integer_property')]
public int $integerProperty
)
{
array $values,
) {
$this->integerProperty = $values['integerProperty'];
}
}

Expand All @@ -32,4 +42,4 @@ public function testInvalidTypesThrowExceptions(): void
// Attempt to deserialize invalid data
InvalidType::fromJson($json);
}
}
}
34 changes: 25 additions & 9 deletions generators/php/codegen/src/asIs/JsonDeserializer.Template.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace <%= namespace%>;
namespace <%= coreNamespace%>;

use DateTime;
use Exception;
Expand Down Expand Up @@ -69,11 +69,13 @@ private static function deserializeValue(mixed $data, mixed $type): mixed
foreach ($type->types as $unionType) {
try {
return self::deserializeSingleValue($data, $unionType);
} catch (Exception $e) {
} catch (Exception) {
continue;
}
}
throw new JsonException("Cannot deserialize value with any of the union types.");
$readableType = Utils::getReadableType($data);
throw new JsonException(
"Cannot deserialize value of type $readableType with any of the union types: " . $type);
}
if (is_array($type)) {
return self::deserializeArray((array)$data, $type);
Expand Down Expand Up @@ -112,10 +114,7 @@ private static function deserializeSingleValue(mixed $data, string $type): mixed
}

if (class_exists($type) && is_array($data)) {
if (!is_subclass_of($type, SerializableType::class)) {
throw new JsonException("$type is not a subclass of SerializableType.");
}
return $type::jsonDeserialize($data);
return self::deserializeObject($data, $type);
}

if (gettype($data) === $type) {
Expand All @@ -125,6 +124,23 @@ private static function deserializeSingleValue(mixed $data, string $type): mixed
throw new JsonException("Unable to deserialize value of type '" . gettype($data) . "' as '$type'.");
}

/**
* Deserializes an array into an object of the given type.
*
* @param array<string, mixed> $data The data to deserialize.
* @param string $type The class name of the object to deserialize into.
*
* @return object The deserialized object.
*
* @throws JsonException If the type does not implement SerializableType.
*/
public static function deserializeObject(array $data, string $type): object {
if (!is_subclass_of($type, SerializableType::class)) {
throw new JsonException("$type is not a subclass of SerializableType.");
}
return $type::jsonDeserialize($data);
}

/**
* Deserializes a map (associative array) with defined key and value types.
*
Expand Down Expand Up @@ -158,6 +174,6 @@ private static function deserializeMap(array $data, array $type): array
private static function deserializeList(array $data, array $type): array
{
$valueType = $type[0];
return array_map(fn($item) => self::deserializeValue($item, $valueType), $data);
return array_map(fn ($item) => self::deserializeValue($item, $valueType), $data);
}
}
}
30 changes: 21 additions & 9 deletions generators/php/codegen/src/asIs/JsonSerializer.Template.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace <%= namespace%>;
namespace <%= coreNamespace%>;

use DateTime;
use Exception;
Expand Down Expand Up @@ -60,11 +60,11 @@ private static function serializeValue(mixed $data, mixed $type): mixed
foreach ($type->types as $unionType) {
try {
return self::serializeSingleValue($data, $unionType);
} catch (Exception $e) {
} catch (Exception) {
continue;
}
}
throw new \InvalidArgumentException("Cannot serialize value with any of the union types.");
throw new JsonException("Cannot serialize value with any of the union types.");
}

if (is_array($type)) {
Expand Down Expand Up @@ -101,10 +101,7 @@ private static function serializeSingleValue(mixed $data, string $type): mixed
}

if (class_exists($type) && $data instanceof $type) {
if (!is_subclass_of($data, JsonSerializable::class)) {
throw new JsonException("Class $type must implement JsonSerializable.");
}
return $data->jsonSerialize();
return self::serializeObject($data);
}

if (gettype($data) === $type) {
Expand All @@ -114,6 +111,21 @@ private static function serializeSingleValue(mixed $data, string $type): mixed
throw new JsonException("Unable to serialize value of type '" . gettype($data) . "' as '$type'.");
}

/**
* Serializes an object to a JSON-serializable format.
*
* @param object $data The object to serialize.
* @return mixed The serialized data.
* @throws JsonException If the object does not implement JsonSerializable.
*/
public static function serializeObject(object $data): mixed {
if (!is_subclass_of($data, JsonSerializable::class)) {
$type = get_class($data);
throw new JsonException("Class $type must implement JsonSerializable.");
}
return $data->jsonSerialize();
}

/**
* Serializes a map (associative array) with defined key and value types.
*
Expand Down Expand Up @@ -150,6 +162,6 @@ private static function serializeMap(array $data, array $type): array
private static function serializeList(array $data, array $type): array
{
$valueType = $type[0];
return array_map(fn($item) => self::serializeValue($item, $valueType), $data);
return array_map(fn ($item) => self::serializeValue($item, $valueType), $data);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,22 @@

class MixedDateArrayType extends SerializableType
{
/**
* @var array<int, datetime|string|null> $mixedDates
*/
#[ArrayType(['integer' => new Union('datetime', 'string', 'null')])]
#[JsonProperty('mixed_dates')]
public array $mixedDates;

/**
* @param array{
* mixedDates: array<int, datetime|string|null>,
* } $values
*/
public function __construct(
#[ArrayType(['integer' => new Union('datetime', 'string', 'null')])]
#[JsonProperty('mixed_dates')]
public array $mixedDates
)
{
array $values,
) {
$this->mixedDates = $values['mixedDates'];
}
}

Expand Down Expand Up @@ -47,4 +57,4 @@ public function testDateTimeTypesInUnionArrays(): void

$this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for mixed_dates.');
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,42 @@

class TestNestedType extends SerializableType
{
/**
* @var string $nestedProperty
*/
#[JsonProperty('nested_property')]
public string $nestedProperty;

/**
* @param array{
* nestedProperty: string,
* } $values
*/
public function __construct(
#[JsonProperty('nested_property')]
public string $nestedProperty
array $values,
) {
$this->nestedProperty = $values['nestedProperty'];
}
}

class NestedUnionArrayType extends SerializableType
{
/**
* @param array<integer, array<integer, TestNestedType|null|string>> $nestedArray
* @var array<int, array<int, TestNestedType|null|string>> $nestedArray
*/
#[ArrayType(['integer' => ['integer' => new Union(TestNestedType::class, 'null', 'date')]])]
#[JsonProperty('nested_array')]
public array $nestedArray;

/**
* @param array{
* nestedArray: array<int, array<int, TestNestedType|null|string>>,
* } $values
*/
public function __construct(
#[ArrayType(['integer' => ['integer' => new Union(TestNestedType::class, 'null', 'date')]])]
#[JsonProperty('nested_array')]
public array $nestedArray
array $values,
) {
$this->nestedArray = $values['nestedArray'];
}
}

Expand Down
27 changes: 22 additions & 5 deletions generators/php/codegen/src/asIs/NullPropertyTypeTest.Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,37 @@

class NullPropertyType extends SerializableType
{
/**
* @var string $nonNullProperty
*/
#[JsonProperty('non_null_property')]
public string $nonNullProperty;

/**
* @var string|null $nullProperty
*/
#[JsonProperty('null_property')]
public ?string $nullProperty;

/**
* @param array{
* nonNullProperty: string,
* nullProperty?: string|null,
* } $values
*/
public function __construct(
#[JsonProperty('non_null_property')]
public string $nonNullProperty,
#[JsonProperty('null_property')]
public ?string $nullProperty = null
array $values,
) {
$this->nonNullProperty = $values['nonNullProperty'];
$this->nullProperty = $values['nullProperty'] ?? null;
}
}

class NullPropertyTypeTest extends TestCase
{
public function testNullPropertiesAreOmitted(): void
{
$object = new NullPropertyType('Test String', null);
$object = new NullPropertyType(["nonNullProperty" => "Test String", "nullProperty" => null]);

$serializedObject = $object->jsonSerialize();

Expand Down
Loading

0 comments on commit 99045b3

Please sign in to comment.