Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullable values #54

Merged
merged 4 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 26 additions & 18 deletions src/Formatter/ArrayBasedDeformatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@
*/
trait ArrayBasedDeformatter
{
public function deserializeInt(mixed $decoded, Field $field): int|DeformatterResult
public function deserializeInt(mixed $decoded, Field $field): int|DeformatterResult|null
{
if (!isset($decoded[$field->serializedName])) {
if (!array_key_exists($field->serializedName, $decoded)) {
return DeformatterResult::Missing;
}

$value = $decoded[$field->serializedName];

if ($field->strict) {
if (!is_int($decoded[$field->serializedName])) {
if (!is_int($value) && !($field->nullable && is_null($value))) {
throw TypeMismatch::create($field->serializedName, 'int', \get_debug_type($decoded[$field->serializedName]));
}
return $decoded[$field->serializedName];
Expand All @@ -41,14 +43,16 @@ public function deserializeInt(mixed $decoded, Field $field): int|DeformatterRes
return (int)($decoded[$field->serializedName]);
}

public function deserializeFloat(mixed $decoded, Field $field): float|DeformatterResult
public function deserializeFloat(mixed $decoded, Field $field): float|DeformatterResult|null
{
if (!isset($decoded[$field->serializedName])) {
if (!array_key_exists($field->serializedName, $decoded)) {
return DeformatterResult::Missing;
}

$value = $decoded[$field->serializedName];

if ($field->strict) {
if (!(is_float($decoded[$field->serializedName]) || is_int($decoded[$field->serializedName]))) {
if (!is_int($value) && !is_float($value) && !($field->nullable && is_null($value))) {
throw TypeMismatch::create($field->serializedName, 'float', \get_debug_type($decoded[$field->serializedName]));
}
return $decoded[$field->serializedName];
Expand All @@ -58,14 +62,16 @@ public function deserializeFloat(mixed $decoded, Field $field): float|Deformatte
return (float)($decoded[$field->serializedName]);
}

public function deserializeBool(mixed $decoded, Field $field): bool|DeformatterResult
public function deserializeBool(mixed $decoded, Field $field): bool|DeformatterResult|null
{
if (!isset($decoded[$field->serializedName])) {
if (!array_key_exists($field->serializedName, $decoded)) {
return DeformatterResult::Missing;
}

$value = $decoded[$field->serializedName];

if ($field->strict) {
if (!is_bool($decoded[$field->serializedName])) {
if (!is_bool($value) && !($field->nullable && is_null($value))) {
throw TypeMismatch::create($field->serializedName, 'bool', \get_debug_type($decoded[$field->serializedName]));
}
return $decoded[$field->serializedName];
Expand All @@ -75,21 +81,23 @@ public function deserializeBool(mixed $decoded, Field $field): bool|DeformatterR
return (bool)($decoded[$field->serializedName]);
}

public function deserializeString(mixed $decoded, Field $field): string|DeformatterResult
public function deserializeString(mixed $decoded, Field $field): string|DeformatterResult|null
{
if (!isset($decoded[$field->serializedName])) {
if (!array_key_exists($field->serializedName, $decoded)) {
return DeformatterResult::Missing;
}

$value = $decoded[$field->serializedName];

if ($field->strict) {
if (!is_string($decoded[$field->serializedName])) {
throw TypeMismatch::create($field->serializedName, 'string', \get_debug_type($decoded[$field->serializedName]));
if (!is_string($value) && !($field->nullable && is_null($value))) {
throw TypeMismatch::create($field->serializedName, 'string', \get_debug_type($value));
}
return $decoded[$field->serializedName];
return $value;
}

// Weak mode.
return (string)($decoded[$field->serializedName]);
return (string)($value);
}

public function deserializeNull(mixed $decoded, Field $field): ?DeformatterResult
Expand Down Expand Up @@ -211,15 +219,15 @@ protected function upcastArray(array $data, Deserializer $deserializer, ?string
* @param Deserializer $deserializer
* @return array<string, mixed>|DeformatterResult
*/
public function deserializeObject(mixed $decoded, Field $field, Deserializer $deserializer): array|DeformatterResult
public function deserializeObject(mixed $decoded, Field $field, Deserializer $deserializer): array|DeformatterResult|null
{
$candidateNames = [$field->serializedName, ...$field->alias];

$key = pipe($candidateNames,
first(static fn (string $name): bool => isset($decoded[$name]))
);

if (!isset($decoded[$key])) {
if (!array_key_exists($key, $decoded)) {
return DeformatterResult::Missing;
}

Expand Down Expand Up @@ -252,7 +260,7 @@ public function deserializeObject(mixed $decoded, Field $field, Deserializer $de
} else {
$key = pipe(
$propField->alias,
first(fn(string $name): bool => isset($data[$name])),
first(fn(string $name): bool => array_key_exists($name, $data)),
);
$ret[$propField->serializedName] = $key
? $deserializer->deserialize($data, $propField->with(serializedName: $key))
Expand Down
14 changes: 7 additions & 7 deletions src/Formatter/Deformatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ public function deserializeInitialize(
Deserializer $deserializer
): mixed;

public function deserializeInt(mixed $decoded, Field $field): int|DeformatterResult;
public function deserializeInt(mixed $decoded, Field $field): int|DeformatterResult|null;

public function deserializeFloat(mixed $decoded, Field $field): float|DeformatterResult;
public function deserializeFloat(mixed $decoded, Field $field): float|DeformatterResult|null;

public function deserializeBool(mixed $decoded, Field $field): bool|DeformatterResult;
public function deserializeBool(mixed $decoded, Field $field): bool|DeformatterResult|null;

public function deserializeString(mixed $decoded, Field $field): string|DeformatterResult;
public function deserializeString(mixed $decoded, Field $field): string|DeformatterResult|null;

public function deserializeNull(mixed $decoded, Field $field): ?DeformatterResult;
public function deserializeNull(mixed $decoded, Field $field): DeformatterResult|null;

/**
* @param mixed $decoded
Expand All @@ -61,9 +61,9 @@ public function deserializeDictionary(mixed $decoded, Field $field, Deserializer
* @param mixed $decoded
* @param Field $field
* @param Deserializer $deserializer
* @return array<string, mixed>|DeformatterResult
* @return array<string, mixed>|DeformatterResult|null
*/
public function deserializeObject( mixed $decoded, Field $field, Deserializer $deserializer): array|DeformatterResult;
public function deserializeObject( mixed $decoded, Field $field, Deserializer $deserializer): array|DeformatterResult|null;

public function deserializeFinalize(mixed $decoded): void;
}
2 changes: 1 addition & 1 deletion src/PropertyHandler/DateTimeExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function importValue(Deserializer $deserializer, Field $field, mixed $sou
{
$string = $deserializer->deformatter->deserializeString($source, $field);

if ($string === DeformatterResult::Missing) {
if ($string === DeformatterResult::Missing || $string === null) {
return null;
}

Expand Down
2 changes: 1 addition & 1 deletion src/PropertyHandler/DateTimeZoneExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function importValue(Deserializer $deserializer, Field $field, mixed $sou
{
$string = $deserializer->deformatter->deserializeString($source, $field);

if ($string instanceof DeformatterResult) {
if ($string instanceof DeformatterResult || $string === null) {
return null;
}

Expand Down
2 changes: 1 addition & 1 deletion src/PropertyHandler/DictionaryExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public function importValue(Deserializer $deserializer, Field $field, mixed $sou
// We cannot easily tell them apart at the moment.
if ($typeField instanceof DictionaryField && $typeField->implodeOn) {
$val = $deserializer->deformatter->deserializeString($source, $field);
return $val === DeformatterResult::Missing ? null : $typeField->explode($val);
return $val === DeformatterResult::Missing || $val === null ? null : $typeField->explode($val);
}

return $deserializer->deformatter->deserializeDictionary($source, $field, $deserializer);
Expand Down
2 changes: 1 addition & 1 deletion src/PropertyHandler/ObjectImporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function importValue(Deserializer $deserializer, Field $field, mixed $sou
// Get the raw data as an array from the source.
$dict = $deserializer->deformatter->deserializeObject($source, $field, $deserializer);

if ($dict instanceof DeformatterResult) {
if ($dict instanceof DeformatterResult || $dict === null) {
return null;
}

Expand Down
2 changes: 1 addition & 1 deletion src/PropertyHandler/SequenceExporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public function importValue(Deserializer $deserializer, Field $field, mixed $sou
// We cannot easily tell them apart at the moment.
if ($typeField instanceof SequenceField && $typeField->implodeOn) {
$val = $deserializer->deformatter->deserializeString($source, $field);
return $val === DeformatterResult::Missing ? null : $typeField->explode($val);
return $val === DeformatterResult::Missing || $val === null ? null : $typeField->explode($val);
}

return $deserializer->deformatter->deserializeSequence($source, $field, $deserializer);
Expand Down
3 changes: 2 additions & 1 deletion tests/ArrayBasedFormatterTestCases.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ protected function empty_values_validate(mixed $serialized): void

self::assertEquals('narf', $toTest['nonConstructorDefault']);
self::assertEquals('beep', $toTest['required']);
self::assertNull($toTest['requiredNullable']);
self::assertEquals('boop', $toTest['withDefault']);
self::assertArrayNotHasKey('nullableUninitialized', $toTest);
self::assertArrayNotHasKey('uninitialized', $toTest);
Expand Down Expand Up @@ -427,7 +428,7 @@ public function class_level_renaming_applies_validate(mixed $serialized): void
self::assertArrayHasKey('the_number', $toTest);
}

public function null_stuff_validate(mixed $serialized): void
public function null_properties_are_allowed_validate(mixed $serialized): void
{
$toTest = $this->arrayify($serialized);

Expand Down
1 change: 1 addition & 0 deletions tests/Records/EmptyData.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class EmptyData

public function __construct(
public string $required,
public ?string $requiredNullable,
public string $withDefault = 'boop',
public ?string $nullable = null,
public readonly ?string $roNullable = null,
Expand Down
2 changes: 1 addition & 1 deletion tests/SerdeTestCases.php
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ public function empty_values(): void
{
$s = new SerdeCommon(formatters: $this->formatters);

$data = new EmptyData('beep');
$data = new EmptyData('beep', null);

$serialized = $s->serialize($data, $this->format);

Expand Down
Loading