Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,8 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par

{{#vars}}
{{#required}}
if ($this->container['{{name}}'] === null) {
$invalidProperties[] = "'{{name}}' can't be null";
if ($this->container['{{name}}'] === null{{#isNullable}} && !$this->isNullableSetToNull('{{name}}'){{/isNullable}}) {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Required+nullable validation now relies on nullable-null tracking, but ArrayAccess setters/unsetters bypass that tracker, causing inconsistent and incorrect validation results.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/php-nextgen/model_generic.mustache, line 277:

<comment>Required+nullable validation now relies on nullable-null tracking, but `ArrayAccess` setters/unsetters bypass that tracker, causing inconsistent and incorrect validation results.</comment>

<file context>
@@ -274,8 +274,8 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par
         {{#required}}
-        if ($this->container['{{name}}'] === null) {
-            $invalidProperties[] = "'{{name}}' can't be null";
+        if ($this->container['{{name}}'] === null{{#isNullable}} && !$this->isNullableSetToNull('{{name}}'){{/isNullable}}) {
+            $invalidProperties[] = "'{{name}}' {{^isNullable}}can't be null"{{/isNullable}}{{#isNullable}}is required"{{/isNullable}};
         }
</file context>
Fix with Cubic

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JulianVennen thanks for the PR

can you please review this feedback which seems to be valid?

Copy link
Copy Markdown
Contributor Author

@JulianVennen JulianVennen Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using $model['key'] = null does indeed not update the openAPINullablesSetToNull. It also doesn't do any other verification on whether the value is even nullable or of the correct type. I can update it in there, but I' not sure whether I should throw an exception if the variable is not nullable, as that might be considered a breaking change.

Using the normal model setters ($model->setKey(null)) does update it and also validates the parameter type.

$invalidProperties[] = "'{{name}}' {{^isNullable}}can't be null"{{/isNullable}}{{#isNullable}}is required"{{/isNullable}};
}
{{/required}}
{{#isEnum}}
Expand All @@ -293,43 +293,43 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par
{{/isEnum}}
{{#hasValidation}}
{{#maxLength}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(mb_strlen($this->container['{{name}}']) > {{maxLength}})) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}(mb_strlen($this->container['{{name}}']) > {{maxLength}})) {
$invalidProperties[] = "invalid value for '{{name}}', the character length must be smaller than or equal to {{{maxLength}}}.";
}

{{/maxLength}}
{{#minLength}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(mb_strlen($this->container['{{name}}']) < {{minLength}})) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}(mb_strlen($this->container['{{name}}']) < {{minLength}})) {
$invalidProperties[] = "invalid value for '{{name}}', the character length must be bigger than or equal to {{{minLength}}}.";
}

{{/minLength}}
{{#maximum}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}($this->container['{{name}}'] >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}})) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}($this->container['{{name}}'] >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}})) {
$invalidProperties[] = "invalid value for '{{name}}', must be smaller than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{maximum}}.";
}

{{/maximum}}
{{#minimum}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}($this->container['{{name}}'] <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}})) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}($this->container['{{name}}'] <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}})) {
$invalidProperties[] = "invalid value for '{{name}}', must be bigger than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{minimum}}.";
}

{{/minimum}}
{{#pattern}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}!preg_match("{{{pattern}}}", $this->container['{{name}}'])) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}!preg_match("{{{pattern}}}", $this->container['{{name}}'])) {
$invalidProperties[] = "invalid value for '{{name}}', must be conform to the pattern {{{pattern}}}.";
}

{{/pattern}}
{{#maxItems}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(count($this->container['{{name}}']) > {{maxItems}})) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}(count($this->container['{{name}}']) > {{maxItems}})) {
$invalidProperties[] = "invalid value for '{{name}}', number of items must be less than or equal to {{{maxItems}}}.";
}

{{/maxItems}}
{{#minItems}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(count($this->container['{{name}}']) < {{minItems}})) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}(count($this->container['{{name}}']) < {{minItems}})) {
$invalidProperties[] = "invalid value for '{{name}}', number of items must be greater than or equal to {{{minItems}}}.";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par
{{#vars}}
{{#required}}
if ($this->container['{{name}}'] === null{{#isNullable}} && !$this->isNullableSetToNull('{{name}}'){{/isNullable}}) {
$invalidProperties[] = "'{{name}}' can't be null";
$invalidProperties[] = "'{{name}}' {{^isNullable}}can't be null"{{/isNullable}}{{#isNullable}}is required"{{/isNullable}};
}
{{/required}}
{{#isEnum}}
Expand All @@ -303,37 +303,37 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}}{{/parentSchema}}{{^par
{{/isEnum}}
{{#hasValidation}}
{{#maxLength}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(mb_strlen($this->container['{{name}}']) > {{maxLength}})) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}(mb_strlen($this->container['{{name}}']) > {{maxLength}})) {
$invalidProperties[] = "invalid value for '{{name}}', the character length must be smaller than or equal to {{{maxLength}}}.";
}

{{/maxLength}}
{{#minLength}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(mb_strlen($this->container['{{name}}']) < {{minLength}})) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}(mb_strlen($this->container['{{name}}']) < {{minLength}})) {
$invalidProperties[] = "invalid value for '{{name}}', the character length must be bigger than or equal to {{{minLength}}}.";
}

{{/minLength}}
{{#maximum}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}($this->container['{{name}}'] >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}})) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}($this->container['{{name}}'] >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}})) {
$invalidProperties[] = "invalid value for '{{name}}', must be smaller than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{maximum}}.";
}

{{/maximum}}
{{#minimum}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}($this->container['{{name}}'] <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}})) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}($this->container['{{name}}'] <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}})) {
$invalidProperties[] = "invalid value for '{{name}}', must be bigger than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{minimum}}.";
}

{{/minimum}}
{{#pattern}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}!preg_match("{{{pattern}}}", $this->container['{{name}}'])) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}!preg_match("{{{pattern}}}", $this->container['{{name}}'])) {
$invalidProperties[] = "invalid value for '{{name}}', must be conform to the pattern {{{pattern}}}.";
}

{{/pattern}}
{{#maxItems}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(count($this->container['{{name}}']) > {{maxItems}})) {
if ({{#notRequiredOrIsNullable}}!is_null($this->container['{{name}}']) && {{/notRequiredOrIsNullable}}(count($this->container['{{name}}']) > {{maxItems}})) {
$invalidProperties[] = "invalid value for '{{name}}', number of items must be less than or equal to {{{maxItems}}}.";
}

Expand Down
Loading