[php-nextgen] Fix Nested oneOf/anyOf Behavior#24318
Conversation
…into php-nextgen-anyof
…tSerializer.mustache Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
…i-generator into php-nextgen-nested-composed
There was a problem hiding this comment.
3 issues found across 10 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpNextgenClientCodegen.java">
<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpNextgenClientCodegen.java:213">
P2: Cyclic composed schemas are not fully flattened: revisiting a composed type adds that dispatcher to the generated union as though it were a leaf. Skipping an already-visiting node while adding only non-composed members would preserve cycle safety and the leaf-only contract.</violation>
</file>
<file name="samples/client/petstore/php-nextgen/OpenAPIClient-php/docs/Model/Creature.md">
<violation number="1" location="samples/client/petstore/php-nextgen/OpenAPIClient-php/docs/Model/Creature.md:8">
P2: The documentation directs users to `Mammal` and `Reptile` as concrete values, but those classes are composed-schema wrappers; nested `Creature` values are flattened to `Whale`, `Zebra`, `Lizard`, or `Snake` in the generated API. Listing the leaf models here would make the documented usage match the generated type signatures.</violation>
</file>
<file name="samples/client/petstore/php-nextgen/OpenAPIClient-php/src/Model/Creature.php">
<violation number="1" location="samples/client/petstore/php-nextgen/OpenAPIClient-php/src/Model/Creature.php:59">
P2: `Creature::getAnyOfTypes()` remains nested, so consumers of the generated anyOf metadata see `Mammal|Reptile` instead of the promised leaf union `Whale|Zebra|Lizard|Snake`. Returning the transitive leaf types here keeps direct `Creature` deserialization/introspection consistent with the flattened property types generated elsewhere.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| if (!composedTypeHints.containsKey(type) || !visiting.add(type)) { | ||
| leaves.add(type); | ||
| return; | ||
| } |
There was a problem hiding this comment.
P2: Cyclic composed schemas are not fully flattened: revisiting a composed type adds that dispatcher to the generated union as though it were a leaf. Skipping an already-visiting node while adding only non-composed members would preserve cycle safety and the leaf-only contract.
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/java/org/openapitools/codegen/languages/PhpNextgenClientCodegen.java, line 213:
<comment>Cyclic composed schemas are not fully flattened: revisiting a composed type adds that dispatcher to the generated union as though it were a leaf. Skipping an already-visiting node while adding only non-composed members would preserve cycle safety and the leaf-only contract.</comment>
<file context>
@@ -187,6 +188,38 @@ private void collectComposedTypeHint(CodegenModel model, Map<String, String> com
+ * recursively; {@code visiting} guards against cycles in self-referential schemas.
+ */
+ private void collectLeafTypes(String type, Map<String, String> composedTypeHints, Set<String> visiting, Set<String> leaves) {
+ if (!composedTypeHints.containsKey(type) || !visiting.add(type)) {
+ leaves.add(type);
+ return;
</file context>
| if (!composedTypeHints.containsKey(type) || !visiting.add(type)) { | |
| leaves.add(type); | |
| return; | |
| } | |
| if (!composedTypeHints.containsKey(type)) { | |
| leaves.add(type); | |
| return; | |
| } | |
| if (!visiting.add(type)) { | |
| return; | |
| } |
|
|
||
| ## anyOf | ||
|
|
||
| - [**\OpenAPI\Client\Model\Mammal**](Mammal.md) |
There was a problem hiding this comment.
P2: The documentation directs users to Mammal and Reptile as concrete values, but those classes are composed-schema wrappers; nested Creature values are flattened to Whale, Zebra, Lizard, or Snake in the generated API. Listing the leaf models here would make the documented usage match the generated type signatures.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/php-nextgen/OpenAPIClient-php/docs/Model/Creature.md, line 8:
<comment>The documentation directs users to `Mammal` and `Reptile` as concrete values, but those classes are composed-schema wrappers; nested `Creature` values are flattened to `Whale`, `Zebra`, `Lizard`, or `Snake` in the generated API. Listing the leaf models here would make the documented usage match the generated type signatures.</comment>
<file context>
@@ -0,0 +1,11 @@
+
+## anyOf
+
+- [**\OpenAPI\Client\Model\Mammal**](Mammal.md)
+- [**\OpenAPI\Client\Model\Reptile**](Reptile.md)
+
</file context>
| public static function getAnyOfTypes(): array | ||
| { | ||
| return [ | ||
| '\OpenAPI\Client\Model\Mammal', |
There was a problem hiding this comment.
P2: Creature::getAnyOfTypes() remains nested, so consumers of the generated anyOf metadata see Mammal|Reptile instead of the promised leaf union Whale|Zebra|Lizard|Snake. Returning the transitive leaf types here keeps direct Creature deserialization/introspection consistent with the flattened property types generated elsewhere.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/php-nextgen/OpenAPIClient-php/src/Model/Creature.php, line 59:
<comment>`Creature::getAnyOfTypes()` remains nested, so consumers of the generated anyOf metadata see `Mammal|Reptile` instead of the promised leaf union `Whale|Zebra|Lizard|Snake`. Returning the transitive leaf types here keeps direct `Creature` deserialization/introspection consistent with the flattened property types generated elsewhere.</comment>
<file context>
@@ -0,0 +1,83 @@
+ public static function getAnyOfTypes(): array
+ {
+ return [
+ '\OpenAPI\Client\Model\Mammal',
+ '\OpenAPI\Client\Model\Reptile'
+ ];
</file context>
This PR builds off of work done in #24306. It flattens composed union type-hints transitively so a nested composed property resolves to its leaf members. Previously, composed property type-hints only listed their direct members, so when a composed type nested another composed type, the deserializer returned a leaf instance the property's union rejected and threw a TypeError.
Once #24306 is merged, I'll update this and mark as ready for review.
PR checklist
Commit all changed files.
This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
These must match the expectations made by your contribution.
You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example
./bin/generate-samples.sh bin/configs/java*.IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
@jebentier (2017/07), @dkarlovi (2017/07), @mandrean (2017/08), @jfastnacht (2017/09), @ybelenko (2018/07), @renepardon (2018/12)
Summary by cubic
Adds proper
anyOfsupport and fixes nestedoneOf/anyOfhandling inphp-nextgenby flattening composed unions to leaf types. Adds a cycle-safe flattening pass for models and operations, plus samples (Creature/Zoo) and tests.Bug Fixes
OneOfInterfaceandAnyOfInterface;ObjectSerializerdispatches viadeserializeOneOf/deserializeAnyOfafterjson_decode, preserves raw strings on decode failure, and improves discriminator/array resolution.oneOf/anyOfunion type-hints transitively (applied in model and operation post-processing) so properties accept leaf instances; generate wrappers and update templates, docs, samples, and tests (e.g.,Creature→Whale|Zebra|Lizard|Snake).Migration
OneOfInterface;AnyOfInterfaceis new. Regenerate clients.Written for commit c0c811e. Summary will update on new commits.