[PHP] Accept 0 as value for query parameters (#13868)

This commit is contained in:
Thomas Hansen 2022-11-02 07:08:21 +01:00 committed by GitHub
parent 89fc631125
commit 0d1e31324b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 189 additions and 8 deletions

View File

@ -148,6 +148,49 @@ class ObjectSerializer
return rawurlencode(self::toString($value));
}
/**
* Checks if a value is empty, based on its OpenAPI type.
*
* @param mixed $value
* @param string $openApiType
*
* @return bool true if $value is empty
*/
private static function isEmptyValue($value, string $openApiType): bool
{
# If empty() returns false, it is not empty regardless of its type.
if (!empty($value)) {
return false;
}
# Null is always empty, as we cannot send a real "null" value in a query parameter.
if ($value === null) {
return true;
}
switch ($openApiType) {
# For numeric values, false and '' are considered empty.
# This comparison is safe for floating point values, since the previous call to empty() will
# filter out values that don't match 0.
case 'int':
case 'integer':
return $value !== 0;
case 'number':
case 'float':
return $value !== 0 && $value !== 0.0;
# For boolean values, '' is considered empty
case 'bool':
case 'boolean':
return !in_array($value, [false, 0], true);
# For all the other types, any value at this point can be considered empty.
default:
return true;
}
}
/**
* Take query parameter properties and turn it into an array suitable for
* native http_build_query or GuzzleHttp\Psr7\Query::build.
@ -169,10 +212,12 @@ class ObjectSerializer
bool $explode = true,
bool $required = true
): array {
if (
empty($value)
&& ($value !== false || $openApiType !== 'boolean') // if $value === false and $openApiType ==='boolean' it isn't empty
) {
# Check if we should omit this parameter from the query. This should only happen when:
# - Parameter is NOT required; AND
# - its value is set to a value that is equivalent to "empty", depending on its OpenAPI type. For
# example, 0 as "int" or "boolean" is NOT an empty value.
if (self::isEmptyValue($value, $openApiType)) {
if ($required) {
return ["{$paramName}" => ''];
} else {

View File

@ -157,6 +157,49 @@ class ObjectSerializer
return rawurlencode(self::toString($value));
}
/**
* Checks if a value is empty, based on its OpenAPI type.
*
* @param mixed $value
* @param string $openApiType
*
* @return bool true if $value is empty
*/
private static function isEmptyValue($value, string $openApiType): bool
{
# If empty() returns false, it is not empty regardless of its type.
if (!empty($value)) {
return false;
}
# Null is always empty, as we cannot send a real "null" value in a query parameter.
if ($value === null) {
return true;
}
switch ($openApiType) {
# For numeric values, false and '' are considered empty.
# This comparison is safe for floating point values, since the previous call to empty() will
# filter out values that don't match 0.
case 'int':
case 'integer':
return $value !== 0;
case 'number':
case 'float':
return $value !== 0 && $value !== 0.0;
# For boolean values, '' is considered empty
case 'bool':
case 'boolean':
return !in_array($value, [false, 0], true);
# For all the other types, any value at this point can be considered empty.
default:
return true;
}
}
/**
* Take query parameter properties and turn it into an array suitable for
* native http_build_query or GuzzleHttp\Psr7\Query::build.
@ -178,10 +221,12 @@ class ObjectSerializer
bool $explode = true,
bool $required = true
): array {
if (
empty($value)
&& ($value !== false || $openApiType !== 'boolean') // if $value === false and $openApiType ==='boolean' it isn't empty
) {
# Check if we should omit this parameter from the query. This should only happen when:
# - Parameter is NOT required; AND
# - its value is set to a value that is equivalent to "empty", depending on its OpenAPI type. For
# example, 0 as "int" or "boolean" is NOT an empty value.
if (self::isEmptyValue($value, $openApiType)) {
if ($required) {
return ["{$paramName}" => ''];
} else {

View File

@ -336,6 +336,97 @@ class ObjectSerializerTest extends TestCase
'form null DateTime object, explode on, required false' => [
null, 'dateTime', '\DateTime', 'form', true, false, '',
],
'form 1 int, explode on, required false' => [
1, 'field', 'int', 'form', true, false, 'field=1',
],
'form 0 int, explode on, required false' => [
0, 'field', 'int', 'form', true, false, 'field=0',
],
'form 0 int, explode on, required true' => [
0, 'field', 'int', 'form', true, true, 'field=0',
],
'form null int, explode on, required false' => [
null, 'field', 'int', 'form', true, false, '',
],
'form null int, explode on, required true' => [
null, 'field', 'int', 'form', true, true, 'field=',
],
'form 1 integer, explode on, required false' => [
1, 'field', 'integer', 'form', true, false, 'field=1',
],
'form 0 integer, explode on, required false' => [
0, 'field', 'integer', 'form', true, false, 'field=0',
],
'form 0 integer, explode on, required true' => [
0, 'field', 'integer', 'form', true, true, 'field=0',
],
'form null integer, explode on, required false' => [
null, 'field', 'integer', 'form', true, false, '',
],
'form null integer, explode on, required true' => [
null, 'field', 'integer', 'form', true, true, 'field=',
],
'form 1.1 float, explode on, required false' => [
1.1, 'field', 'float', 'form', true, false, 'field=1.1',
],
'form 0 float, explode on, required false' => [
0, 'field', 'float', 'form', true, false, 'field=0',
],
'form 0.0 float, explode on, required false' => [
0.0, 'field', 'float', 'form', true, false, 'field=0',
],
'form 0 float, explode on, required true' => [
0, 'field', 'float', 'form', true, true, 'field=0',
],
'form 0.0 float, explode on, required true' => [
0.0, 'field', 'float', 'form', true, true, 'field=0',
],
'form null float, explode on, required false' => [
null, 'field', 'float', 'form', true, false, '',
],
'form null float, explode on, required true' => [
null, 'field', 'float', 'form', true, true, 'field=',
],
'form 1.1 number, explode on, required false' => [
1.1, 'field', 'number', 'form', true, false, 'field=1.1',
],
'form 0 number, explode on, required false' => [
0, 'field', 'number', 'form', true, false, 'field=0',
],
'form 0.0 number, explode on, required false' => [
0.0, 'field', 'number', 'form', true, false, 'field=0',
],
'form 0 number, explode on, required true' => [
0, 'field', 'number', 'form', true, true, 'field=0',
],
'form 0.0 number, explode on, required true' => [
0.0, 'field', 'number', 'form', true, true, 'field=0',
],
'form null number, explode on, required false' => [
null, 'field', 'number', 'form', true, false, '',
],
'form null number, explode on, required true' => [
null, 'field', 'number', 'form', true, true, 'field=',
],
'form true bool, explode on, required false' => [
true, 'field', 'bool', 'form', true, false, 'field=1',
],
'form false bool, explode on, required false' => [
false, 'field', 'bool', 'form', true, false, 'field=0',
],
'form empty bool, explode on, required false' => [
null, 'field', 'bool', 'form', true, false, '',
],
'form empty bool, explode on, required true' => [
null, 'field', 'bool', 'form', true, true, 'field=',
],
# Entries for "boolean" type are already covered in the beginning of this provider
'form 1 bool, explode on, required false' => [
1, 'field', 'bool', 'form', true, false, 'field=1',
],
'form 0 bool, explode on, required false' => [
0, 'field', 'bool', 'form', true, false, 'field=0',
],
];
}