Skip to content

Commit cd48db4

Browse files
[PHP] Add support to nullable (based on #3493) (#12794)
* [PHP] Add support to nullable (based on PR 3493) * [AUTOGENERATED] update samples
1 parent 20420e5 commit cd48db4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+4258
-129
lines changed

modules/openapi-generator/src/main/resources/php/ModelInterface.mustache

+16
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,20 @@ interface ModelInterface
8383
* @return bool
8484
*/
8585
public function valid();
86+
87+
/**
88+
* Checks if a property is nullable
89+
*
90+
* @param string $property
91+
* @return bool
92+
*/
93+
public static function isNullable(string $property): bool;
94+
95+
/**
96+
* Checks if a nullable property is set to null.
97+
*
98+
* @param string $property
99+
* @return bool
100+
*/
101+
public function isNullableSetToNull(string $property): bool;
86102
}

modules/openapi-generator/src/main/resources/php/ObjectSerializer.mustache

+10-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class ObjectSerializer
8989
}
9090
}
9191
}
92-
if ($value !== null) {
92+
if (($data::isNullable($property) && $data->isNullableSetToNull($property)) || $value !== null) {
9393
$values[$data::attributeMap()[$property]] = self::sanitizeForSerialization($value, $openAPIType, $formats[$property]);
9494
}
9595
}
@@ -460,7 +460,15 @@ class ObjectSerializer
460460
foreach ($instance::openAPITypes() as $property => $type) {
461461
$propertySetter = $instance::setters()[$property];
462462
463-
if (!isset($propertySetter) || !isset($data->{$instance::attributeMap()[$property]})) {
463+
if (!isset($propertySetter)) {
464+
continue;
465+
}
466+
467+
if (!isset($data->{$instance::attributeMap()[$property]})) {
468+
if ($instance::isNullable($property)) {
469+
$instance->$propertySetter(null);
470+
}
471+
464472
continue;
465473
}
466474

modules/openapi-generator/src/main/resources/php/model_generic.mustache

+97-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par
3131
{{/-last}}{{/vars}}
3232
];
3333

34+
/**
35+
* Array of nullable properties. Used for (de)serialization
36+
*
37+
* @var boolean[]
38+
*/
39+
protected static array $openAPINullables = [
40+
{{#vars}}'{{name}}' => {{#isNullable}}true{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{^-last}},
41+
{{/-last}}{{/vars}}
42+
];
43+
44+
/**
45+
* If a nullable field gets set to null, insert it here
46+
*
47+
* @var boolean[]
48+
*/
49+
protected array $openAPINullablesSetToNull = [];
50+
3451
/**
3552
* Array of property to type mappings. Used for (de)serialization
3653
*
@@ -51,6 +68,48 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par
5168
return self::$openAPIFormats{{#parentSchema}} + parent::openAPIFormats(){{/parentSchema}};
5269
}
5370

71+
/**
72+
* Array of nullable properties
73+
*
74+
* @return array
75+
*/
76+
protected static function openAPINullables(): array
77+
{
78+
return self::$openAPINullables{{#parentSchema}} + parent::openAPINullables(){{/parentSchema}};
79+
}
80+
81+
/**
82+
* Array of nullable field names deliberately set to null
83+
*
84+
* @return boolean[]
85+
*/
86+
private function getOpenAPINullablesSetToNull(): array
87+
{
88+
return $this->openAPINullablesSetToNull;
89+
}
90+
91+
/**
92+
* Checks if a property is nullable
93+
*
94+
* @param string $property
95+
* @return bool
96+
*/
97+
public static function isNullable(string $property): bool
98+
{
99+
return self::openAPINullables()[$property] ?? false;
100+
}
101+
102+
/**
103+
* Checks if a nullable property is set to null.
104+
*
105+
* @param string $property
106+
* @return bool
107+
*/
108+
public function isNullableSetToNull(string $property): bool
109+
{
110+
return in_array($property, $this->getOpenAPINullablesSetToNull(), true);
111+
}
112+
54113
/**
55114
* Array of attributes where the key is the local name,
56115
* and the value is the original name
@@ -172,7 +231,7 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par
172231

173232
{{/parentSchema}}
174233
{{#vars}}
175-
$this->container['{{name}}'] = $data['{{name}}'] ?? {{{defaultValue}}}{{^defaultValue}}null{{/defaultValue}};
234+
$this->setIfExists('{{name}}', $data ?? [], {{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}});
176235
{{/vars}}
177236
{{#discriminator}}
178237

@@ -181,6 +240,24 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par
181240
{{/discriminator}}
182241
}
183242

243+
/**
244+
* Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName
245+
* is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the
246+
* $this->openAPINullablesSetToNull array
247+
*
248+
* @param string $variableName
249+
* @param array $fields
250+
* @param mixed $defaultValue
251+
*/
252+
private function setIfExists(string $variableName, array $fields, $defaultValue): void
253+
{
254+
if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) {
255+
$this->openAPINullablesSetToNull[] = $variableName;
256+
}
257+
258+
$this->container[$variableName] = $fields[$variableName] ?? $defaultValue;
259+
}
260+
184261
/**
185262
* Show all the invalid properties with reasons.
186263
*
@@ -359,6 +436,25 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par
359436
}
360437
{{/minItems}}
361438
{{/hasValidation}}
439+
440+
{{#isNullable}}
441+
if (is_null(${{name}})) {
442+
array_push($this->openAPINullablesSetToNull, '{{name}}');
443+
} else {
444+
$nullablesSetToNull = $this->getOpenAPINullablesSetToNull();
445+
$index = array_search('{{name}}', $nullablesSetToNull);
446+
if ($index !== FALSE) {
447+
unset($nullablesSetToNull[$index]);
448+
$this->setOpenAPINullablesSetToNull($nullablesSetToNull);
449+
}
450+
}
451+
{{/isNullable}}
452+
{{^isNullable}}
453+
if (is_null(${{name}})) {
454+
throw new \InvalidArgumentException('non-nullable {{name}} cannot be null');
455+
}
456+
{{/isNullable}}
457+
362458
$this->container['{{name}}'] = ${{name}};
363459

364460
return $this;

samples/client/petstore/php/OpenAPIClient-php/lib/Model/AdditionalPropertiesClass.php

+89-2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,23 @@ class AdditionalPropertiesClass implements ModelInterface, ArrayAccess, \JsonSer
7373
'map_of_map_property' => null
7474
];
7575

76+
/**
77+
* Array of nullable properties. Used for (de)serialization
78+
*
79+
* @var boolean[]
80+
*/
81+
protected static array $openAPINullables = [
82+
'map_property' => false,
83+
'map_of_map_property' => false
84+
];
85+
86+
/**
87+
* If a nullable field gets set to null, insert it here
88+
*
89+
* @var boolean[]
90+
*/
91+
protected array $openAPINullablesSetToNull = [];
92+
7693
/**
7794
* Array of property to type mappings. Used for (de)serialization
7895
*
@@ -93,6 +110,48 @@ public static function openAPIFormats()
93110
return self::$openAPIFormats;
94111
}
95112

113+
/**
114+
* Array of nullable properties
115+
*
116+
* @return array
117+
*/
118+
protected static function openAPINullables(): array
119+
{
120+
return self::$openAPINullables;
121+
}
122+
123+
/**
124+
* Array of nullable field names deliberately set to null
125+
*
126+
* @return boolean[]
127+
*/
128+
private function getOpenAPINullablesSetToNull(): array
129+
{
130+
return $this->openAPINullablesSetToNull;
131+
}
132+
133+
/**
134+
* Checks if a property is nullable
135+
*
136+
* @param string $property
137+
* @return bool
138+
*/
139+
public static function isNullable(string $property): bool
140+
{
141+
return self::openAPINullables()[$property] ?? false;
142+
}
143+
144+
/**
145+
* Checks if a nullable property is set to null.
146+
*
147+
* @param string $property
148+
* @return bool
149+
*/
150+
public function isNullableSetToNull(string $property): bool
151+
{
152+
return in_array($property, $this->getOpenAPINullablesSetToNull(), true);
153+
}
154+
96155
/**
97156
* Array of attributes where the key is the local name,
98157
* and the value is the original name
@@ -181,8 +240,26 @@ public function getModelName()
181240
*/
182241
public function __construct(array $data = null)
183242
{
184-
$this->container['map_property'] = $data['map_property'] ?? null;
185-
$this->container['map_of_map_property'] = $data['map_of_map_property'] ?? null;
243+
$this->setIfExists('map_property', $data ?? [], null);
244+
$this->setIfExists('map_of_map_property', $data ?? [], null);
245+
}
246+
247+
/**
248+
* Sets $this->container[$variableName] to the given data or to the given default Value; if $variableName
249+
* is nullable and its value is set to null in the $fields array, then mark it as "set to null" in the
250+
* $this->openAPINullablesSetToNull array
251+
*
252+
* @param string $variableName
253+
* @param array $fields
254+
* @param mixed $defaultValue
255+
*/
256+
private function setIfExists(string $variableName, array $fields, $defaultValue): void
257+
{
258+
if (self::isNullable($variableName) && array_key_exists($variableName, $fields) && is_null($fields[$variableName])) {
259+
$this->openAPINullablesSetToNull[] = $variableName;
260+
}
261+
262+
$this->container[$variableName] = $fields[$variableName] ?? $defaultValue;
186263
}
187264

188265
/**
@@ -228,6 +305,11 @@ public function getMapProperty()
228305
*/
229306
public function setMapProperty($map_property)
230307
{
308+
309+
if (is_null($map_property)) {
310+
throw new \InvalidArgumentException('non-nullable map_property cannot be null');
311+
}
312+
231313
$this->container['map_property'] = $map_property;
232314

233315
return $this;
@@ -252,6 +334,11 @@ public function getMapOfMapProperty()
252334
*/
253335
public function setMapOfMapProperty($map_of_map_property)
254336
{
337+
338+
if (is_null($map_of_map_property)) {
339+
throw new \InvalidArgumentException('non-nullable map_of_map_property cannot be null');
340+
}
341+
255342
$this->container['map_of_map_property'] = $map_of_map_property;
256343

257344
return $this;

0 commit comments

Comments
 (0)