-
-
Notifications
You must be signed in to change notification settings - Fork 7.6k
[php-nextgen] Add anyof Support #24306
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
base: master
Are you sure you want to change the base?
Changes from all commits
43b94b1
f69799d
3843c5c
64d4a0f
17bcfc7
c788027
24f6b9b
8161b24
72feca0
3c6eb0f
da12fc3
0a3d632
9a21acf
b86c6fc
d95922f
fe2f126
c31c074
a3fad7b
457f0f4
cbaf8c7
b2a7e37
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| <?php | ||
| /** | ||
| * AnyOfInterface | ||
| * | ||
| * PHP version 8.1 | ||
| * | ||
| * @package {{modelPackage}} | ||
| * @author OpenAPI Generator team | ||
| * @link https://openapi-generator.tech | ||
| */ | ||
|
|
||
| {{>partial_header}} | ||
|
|
||
| /** | ||
| * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). | ||
| * https://openapi-generator.tech | ||
| * Do not edit the class manually. | ||
| */ | ||
|
|
||
| namespace {{modelPackage}}; | ||
|
|
||
| /** | ||
| * Interface implemented by models generated from an `anyOf` schema. | ||
| * | ||
| * An `anyOf` schema is not represented by a value object; instead a value is one of the | ||
| * member types. Classes implementing this interface only carry the metadata that | ||
| * {@see ObjectSerializer::deserialize()} needs to resolve the concrete member type. | ||
| * | ||
| * @package {{modelPackage}} | ||
| * @author OpenAPI Generator team | ||
| */ | ||
| interface AnyOfInterface | ||
| { | ||
| /** | ||
| * List of the types a value of this `anyOf` schema may be. Each entry uses the same | ||
| * notation as the values of {@see ModelInterface::openAPITypes()}. | ||
| * | ||
| * @return string[] | ||
| */ | ||
| public static function getAnyOfTypes(): array; | ||
|
|
||
| /** | ||
| * Name of the discriminator property used to resolve the concrete member type, or null | ||
| * when the schema has no discriminator. | ||
| * | ||
| * @return string|null | ||
| */ | ||
| public static function getAnyOfDiscriminator(): ?string; | ||
|
|
||
| /** | ||
| * Mapping of discriminator values to the concrete member type. Empty when the schema has | ||
| * no discriminator. | ||
| * | ||
| * @return array<string,string> | ||
| */ | ||
| public static function getAnyOfDiscriminatorMappings(): array; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -490,16 +490,24 @@ class ObjectSerializer | |||||||||
| } | ||||||||||
| return $data; | ||||||||||
| } else { | ||||||||||
| $data = is_string($data) ? json_decode($data) : $data; | ||||||||||
|
|
||||||||||
| if (is_array($data)) { | ||||||||||
| $data = (object) $data; | ||||||||||
| if (is_string($data)) { | ||||||||||
| $decoded = json_decode($data); | ||||||||||
| // Keep the original string if decode fails (nested values are already PHP strings). | ||||||||||
| $data = ($decoded !== null || $data === 'null') ? $decoded : $data; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // A oneOf schema is not a value object: resolve the data to one of its member types. | ||||||||||
| // A oneOf/anyOf schema is not a value object: dispatch here, after json_decode (so | ||||||||||
| // members get the decoded value) but before the array-to-object cast (which breaks arrays). | ||||||||||
| if (is_subclass_of($class, '\{{modelPackage}}\OneOfInterface')) { | ||||||||||
| return self::deserializeOneOf($data, $class, $httpHeaders); | ||||||||||
| } | ||||||||||
| if (is_subclass_of($class, '\{{modelPackage}}\AnyOfInterface')) { | ||||||||||
| return self::deserializeAnyOf($data, $class, $httpHeaders); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if (is_array($data)) { | ||||||||||
| $data = (object) $data; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // If a discriminator is defined and points to a valid subclass, use it. | ||||||||||
| $discriminator = $class::DISCRIMINATOR; | ||||||||||
|
|
@@ -536,34 +544,61 @@ class ObjectSerializer | |||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Returns true when $data's PHP type is compatible with the OpenAPI primitive $type. | ||||||||||
| * Prevents settype() coercion (e.g. "abc" → 0 for integer) from producing a false match | ||||||||||
| * when iterating over oneOf/anyOf candidate types. | ||||||||||
| * Non-primitive types always return true and are validated downstream by ModelInterface::valid(). | ||||||||||
| */ | ||||||||||
| private static function isPrimitiveTypeCompatible(mixed $data, string $type): bool | ||||||||||
| { | ||||||||||
| return match($type) { | ||||||||||
| 'int', 'integer' => is_int($data), | ||||||||||
| 'float', 'double', 'number' => is_int($data) || is_float($data), | ||||||||||
| 'bool', 'boolean' => is_bool($data), | ||||||||||
| 'string' => is_string($data), | ||||||||||
| default => true, | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents
Suggested change
|
||||||||||
| }; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Deserialize data into one of the member types of a `oneOf` schema. | ||||||||||
| * | ||||||||||
| * When the schema declares a discriminator, its value selects the member type directly. | ||||||||||
| * Otherwise each member type is tried in turn and the first one that yields a valid model | ||||||||||
| * (or a non-null primitive) is returned. | ||||||||||
| * | ||||||||||
| * @param mixed $data the data already decoded to an object | ||||||||||
| * @param mixed $data the decoded data to resolve to a member type | ||||||||||
| * @param string $class a class name implementing OneOfInterface | ||||||||||
| * @param string[]|null $httpHeaders HTTP headers | ||||||||||
| * | ||||||||||
| * @return mixed an instance of one of the `oneOf` member types | ||||||||||
| */ | ||||||||||
| private static function deserializeOneOf(mixed $data, string $class, ?array $httpHeaders): mixed | ||||||||||
| { | ||||||||||
| // $data is already decoded (see deserialize()). Read the discriminator from an object view | ||||||||||
| // of it, but deserialize each member from the original $data so array and scalar members | ||||||||||
| // keep their own type handling instead of being coerced to an object here. | ||||||||||
| $probe = is_array($data) ? (object) $data : $data; | ||||||||||
|
|
||||||||||
| $discriminator = $class::getOneOfDiscriminator(); | ||||||||||
| if ($discriminator !== null && isset($data->{$discriminator}) && is_string($data->{$discriminator})) { | ||||||||||
| if ($discriminator !== null && is_object($probe) && isset($probe->{$discriminator}) && is_string($probe->{$discriminator})) { | ||||||||||
| $mappings = $class::getOneOfDiscriminatorMappings(); | ||||||||||
| if (isset($mappings[$data->{$discriminator}])) { | ||||||||||
| return self::deserialize($data, $mappings[$data->{$discriminator}], $httpHeaders); | ||||||||||
| if (isset($mappings[$probe->{$discriminator}])) { | ||||||||||
| return self::deserialize($data, $mappings[$probe->{$discriminator}], $httpHeaders); | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| foreach ($class::getOneOfTypes() as $type) { | ||||||||||
| if (!self::isPrimitiveTypeCompatible($data, $type)) { | ||||||||||
| continue; | ||||||||||
| } | ||||||||||
| try { | ||||||||||
| $instance = self::deserialize($data, $type, $httpHeaders); | ||||||||||
| } catch (\Throwable $e) { | ||||||||||
| // The data does not match this member type, try the next one. | ||||||||||
| // Any failure means the data does not match this member type; try the next one. | ||||||||||
| // The broad catch is intentional: a real failure is not hidden, since the loop | ||||||||||
| // throws below when no member type matches. | ||||||||||
| continue; | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
|
@@ -579,6 +614,59 @@ class ObjectSerializer | |||||||||
| throw new \InvalidArgumentException(sprintf('No matching schema in oneOf %s for the given data', $class)); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Deserialize data into one of the member types of an `anyOf` schema. | ||||||||||
| * | ||||||||||
| * When the schema declares a discriminator, its value selects the member type directly. | ||||||||||
| * Otherwise each member type is tried in turn and the first one that yields a valid model | ||||||||||
| * (or a non-null primitive) is returned. | ||||||||||
| * | ||||||||||
| * @param mixed $data the decoded data to resolve to a member type | ||||||||||
| * @param string $class a class name implementing AnyOfInterface | ||||||||||
| * @param string[]|null $httpHeaders HTTP headers | ||||||||||
| * | ||||||||||
| * @return mixed an instance of one of the `anyOf` member types | ||||||||||
| */ | ||||||||||
| private static function deserializeAnyOf(mixed $data, string $class, ?array $httpHeaders): mixed | ||||||||||
| { | ||||||||||
| // $data is already decoded (see deserialize()). Read the discriminator from an object view | ||||||||||
| // of it, but deserialize each member from the original $data so array and scalar members | ||||||||||
| // keep their own type handling instead of being coerced to an object here. | ||||||||||
| $probe = is_array($data) ? (object) $data : $data; | ||||||||||
|
|
||||||||||
| $discriminator = $class::getAnyOfDiscriminator(); | ||||||||||
| if ($discriminator !== null && is_object($probe) && isset($probe->{$discriminator}) && is_string($probe->{$discriminator})) { | ||||||||||
| $mappings = $class::getAnyOfDiscriminatorMappings(); | ||||||||||
| if (isset($mappings[$probe->{$discriminator}])) { | ||||||||||
| return self::deserialize($data, $mappings[$probe->{$discriminator}], $httpHeaders); | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| foreach ($class::getAnyOfTypes() as $type) { | ||||||||||
| if (!self::isPrimitiveTypeCompatible($data, $type)) { | ||||||||||
| continue; | ||||||||||
| } | ||||||||||
| try { | ||||||||||
| $instance = self::deserialize($data, $type, $httpHeaders); | ||||||||||
| } catch (\Throwable $e) { | ||||||||||
| // Any failure means the data does not match this member type; try the next one. | ||||||||||
| // The broad catch is intentional: a real failure is not hidden, since the loop | ||||||||||
| // throws below when no member type matches. | ||||||||||
| continue; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if ($instance instanceof ModelInterface) { | ||||||||||
| if ($instance->valid()) { | ||||||||||
| return $instance; | ||||||||||
| } | ||||||||||
| } elseif ($instance !== null) { | ||||||||||
| return $instance; | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| throw new \InvalidArgumentException(sprintf('No matching schema in anyOf %s for the given data', $class)); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Build a query string from an array of key value pairs. | ||||||||||
| * | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| class {{classname}} implements AnyOfInterface | ||
| { | ||
| /** | ||
| * The original name of the model. | ||
| * | ||
| * @var string | ||
| */ | ||
| public const MODEL_NAME = '{{name}}'; | ||
|
|
||
| /** | ||
| * The discriminator property name, or null when the schema has no discriminator. | ||
| * | ||
| * @var string|null | ||
| */ | ||
| public const DISCRIMINATOR = {{#discriminator}}'{{propertyBaseName}}'{{/discriminator}}{{^discriminator}}null{{/discriminator}}; | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public static function getAnyOfTypes(): array | ||
| { | ||
| return [ | ||
| {{#composedSchemas}}{{#anyOf}}'{{{dataType}}}'{{^-last}}, | ||
| {{/-last}}{{/anyOf}}{{/composedSchemas}} | ||
| ]; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public static function getAnyOfDiscriminator(): ?string | ||
| { | ||
| return self::DISCRIMINATOR; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public static function getAnyOfDiscriminatorMappings(): array | ||
| { | ||
| return [ | ||
| {{#discriminator}}{{#mappedModels}}'{{mappingName}}' => '\{{modelPackage}}\{{modelName}}'{{^-last}}, | ||
| {{/-last}}{{/mappedModels}}{{/discriminator}} | ||
| ]; | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.