From 64f11e4d8f5874aa4a4876415dacf9260ef4d3c3 Mon Sep 17 00:00:00 2001 From: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com> Date: Thu, 13 Feb 2025 12:30:53 -0500 Subject: [PATCH] fix(php): JSON serialize empty arrays as empty object (#5986) Fix empty object as array JSON issue --- .../src/asIs/Client/RawClient.Template.php | 81 ++++++++++++++----- generators/php/sdk/versions.yml | 7 ++ .../bearer-token-environment-variable.json | 34 +++++++- .../bearer-token-environment-variable.json | 17 +++- .../.mock/definition/api.yml | 4 + .../src/Ast/ContainerValue.php | 36 ++++++--- .../src/Ast/FieldValue.php | 23 ++++-- .../src/Ast/ObjectFieldValue.php | 6 +- .../src/Ast/ContainerValue.php | 36 ++++++--- .../src/Ast/FieldValue.php | 23 ++++-- .../examples/src/Types/BigEntity.php | 39 ++++----- .../trace/src/Commons/DebugKeyValuePairs.php | 12 +-- .../trace/src/Commons/DebugVariableValue.php | 8 +- .../trace/src/Commons/KeyValuePair.php | 12 +-- seed/php-model/trace/src/Commons/ListType.php | 6 +- seed/php-model/trace/src/Commons/MapType.php | 12 +-- seed/php-model/trace/src/Commons/TestCase.php | 6 +- .../Commons/TestCaseWithExpectedResult.php | 6 +- .../trace/src/Commons/VariableValue.php | 8 +- .../src/Problem/CreateProblemRequest.php | 7 +- .../src/Problem/CreateProblemResponse.php | 23 ++++-- .../trace/src/Problem/ProblemDescription.php | 6 +- .../src/Problem/ProblemDescriptionBoard.php | 24 ++++-- .../trace/src/Problem/ProblemInfo.php | 7 +- .../trace/src/Problem/VariableTypeAndName.php | 7 +- .../trace/src/Submission/ActualResult.php | 51 +++++++----- .../trace/src/Submission/ErroredResponse.php | 6 +- .../Submission/GetSubmissionStateResponse.php | 6 +- .../trace/src/Submission/GradedResponseV2.php | 6 +- .../src/Submission/GradedTestCaseUpdate.php | 6 +- .../src/Submission/InvalidRequestResponse.php | 12 +-- seed/php-model/trace/src/Submission/Scope.php | 7 +- .../src/Submission/SubmissionResponse.php | 27 ++++--- .../SubmissionStatusForTestCase.php | 27 ++++--- .../src/Submission/TestCaseNonHiddenGrade.php | 13 +-- .../trace/src/Submission/TestCaseResult.php | 13 +-- .../src/Submission/TestSubmissionState.php | 6 +- .../src/Submission/TestSubmissionStatus.php | 35 ++++---- .../src/Submission/TestSubmissionUpdate.php | 6 +- .../Submission/TestSubmissionUpdateInfo.php | 27 ++++--- .../trace/src/Submission/TraceResponse.php | 7 +- .../trace/src/Submission/TraceResponseV2.php | 7 +- .../src/Submission/WorkspaceRunDetails.php | 6 +- .../Submission/WorkspaceSubmissionState.php | 6 +- .../Submission/WorkspaceSubmissionStatus.php | 27 ++++--- .../Submission/WorkspaceSubmissionUpdate.php | 6 +- .../WorkspaceSubmissionUpdateInfo.php | 23 ++++-- .../src/V2/Problem/CreateProblemRequestV2.php | 6 +- .../src/V2/Problem/DefaultProvidedFile.php | 7 +- .../Problem/GetFunctionSignatureRequest.php | 6 +- .../V2/Problem/LightweightProblemInfoV2.php | 7 +- .../V2/Problem/NonVoidFunctionSignature.php | 7 +- .../trace/src/V2/Problem/Parameter.php | 7 +- .../trace/src/V2/Problem/ProblemInfoV2.php | 6 +- .../src/V2/Problem/TestCaseImplementation.php | 6 +- .../TestCaseImplementationDescription.php | 6 +- .../trace/src/V2/Problem/TestCaseV2.php | 13 +-- ...TestCaseWithActualResultImplementation.php | 6 +- ...FunctionSignatureThatTakesActualResult.php | 7 +- .../V2/V3/Problem/CreateProblemRequestV2.php | 6 +- .../src/V2/V3/Problem/DefaultProvidedFile.php | 7 +- .../Problem/GetFunctionSignatureRequest.php | 6 +- .../V3/Problem/LightweightProblemInfoV2.php | 7 +- .../V3/Problem/NonVoidFunctionSignature.php | 7 +- .../trace/src/V2/V3/Problem/Parameter.php | 7 +- .../trace/src/V2/V3/Problem/ProblemInfoV2.php | 6 +- .../V2/V3/Problem/TestCaseImplementation.php | 6 +- .../TestCaseImplementationDescription.php | 6 +- .../trace/src/V2/V3/Problem/TestCaseV2.php | 13 +-- ...TestCaseWithActualResultImplementation.php | 6 +- ...FunctionSignatureThatTakesActualResult.php | 7 +- .../unions/.mock/definition/bigunion.yml | 6 ++ .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../any-auth/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../audiences/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../basic-auth/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../bytes/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../custom-auth/src/Core/Client/RawClient.php | 53 +++++++++--- .../enum/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../examples/src/Core/Client/RawClient.php | 53 +++++++++--- .../exhaustive/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../private/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../file-upload/src/Core/Client/RawClient.php | 53 +++++++++--- .../folders/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../clientName/src/Core/Client/RawClient.php | 53 +++++++++--- .../namespace/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../packageName/src/Core/Client/RawClient.php | 53 +++++++++--- .../private/src/Core/Client/RawClient.php | 53 +++++++++--- .../license/src/Core/Client/RawClient.php | 53 +++++++++--- .../literal/src/Core/Client/RawClient.php | 53 +++++++++--- .../mixed-case/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../nullable/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../object/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../optional/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../private/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../plain-text/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../private/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../simple-fhir/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../streaming/src/Core/Client/RawClient.php | 53 +++++++++--- .../trace/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../unions/src/Core/Client/RawClient.php | 53 +++++++++--- .../unknown/src/Core/Client/RawClient.php | 53 +++++++++--- .../validation/src/Core/Client/RawClient.php | 53 +++++++++--- .../variables/src/Core/Client/RawClient.php | 53 +++++++++--- .../src/Core/Client/RawClient.php | 53 +++++++++--- .../version/src/Core/Client/RawClient.php | 53 +++++++++--- .../websocket/src/Core/Client/RawClient.php | 53 +++++++++--- 147 files changed, 3669 insertions(+), 1260 deletions(-) diff --git a/generators/php/base/src/asIs/Client/RawClient.Template.php b/generators/php/base/src/asIs/Client/RawClient.Template.php index 142c333dc41..58b31259795 100644 --- a/generators/php/base/src/asIs/Client/RawClient.Template.php +++ b/generators/php/base/src/asIs/Client/RawClient.Template.php @@ -37,7 +37,8 @@ class RawClient */ public function __construct( public readonly ?array $options = null, - ) { + ) + { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; @@ -67,8 +68,9 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, - ): ResponseInterface { + ?array $options = null, + ): ResponseInterface + { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -81,7 +83,8 @@ public function sendRequest( * } $options * @return array */ - private function toGuzzleOptions(array $options): array { + private function toGuzzleOptions(array $options): array + { $guzzleOptions = []; if (isset($options['maxRetries'])) { $guzzleOptions['maxRetries'] = $options['maxRetries']; @@ -103,8 +106,9 @@ private function toGuzzleOptions(array $options): array { */ private function buildRequest( BaseApiRequest $request, - array $options - ): Request { + array $options + ): Request + { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -125,8 +129,9 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, - ): array { + array $options, + ): array + { return match (get_class($request)) { JsonApiRequest::class => array_merge( ["Content-Type" => "application/json"], @@ -152,8 +157,9 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, - ): ?StreamInterface { + array $options, + ): ?StreamInterface + { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -178,17 +184,27 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed { + ): mixed + { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; + } + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + + return $result; } /** @@ -200,8 +216,9 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, - ): string { + array $options, + ): string + { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -220,7 +237,8 @@ private function buildUrl( * @param array $query * @return string */ - private function encodeQuery(array $query): string { + private function encodeQuery(array $query): string + { $parts = []; foreach ($query as $key => $value) { if (is_array($value)) { @@ -234,7 +252,8 @@ private function encodeQuery(array $query): string { return implode('&', $parts); } - private function encodeQueryValue(mixed $value): string { + private function encodeQueryValue(mixed $value): string + { if (is_string($value)) { return urlencode($value); } @@ -247,4 +266,22 @@ private function encodeQueryValue(mixed $value): string { // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) return false; + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/generators/php/sdk/versions.yml b/generators/php/sdk/versions.yml index 2276ae68aed..040a829859f 100644 --- a/generators/php/sdk/versions.yml +++ b/generators/php/sdk/versions.yml @@ -1,3 +1,10 @@ +- version: 0.13.3 + changelogEntry: + - type: fix + summary: >- + Fix issue where an empty request would be JSON serialized as an empty array instead of an empty object. + irVersion: 55 + - version: 0.13.2 changelogEntry: - type: fix diff --git a/packages/cli/generation/ir-generator-tests/src/dynamic-snippets/__test__/test-definitions/bearer-token-environment-variable.json b/packages/cli/generation/ir-generator-tests/src/dynamic-snippets/__test__/test-definitions/bearer-token-environment-variable.json index 9573bf6f396..d475be7325e 100644 --- a/packages/cli/generation/ir-generator-tests/src/dynamic-snippets/__test__/test-definitions/bearer-token-environment-variable.json +++ b/packages/cli/generation/ir-generator-tests/src/dynamic-snippets/__test__/test-definitions/bearer-token-environment-variable.json @@ -1,7 +1,39 @@ { "version": "1.0.0", "types": {}, - "headers": [], + "headers": [ + { + "name": { + "name": { + "originalName": "version", + "camelCase": { + "unsafeName": "version", + "safeName": "version" + }, + "snakeCase": { + "unsafeName": "version", + "safeName": "version" + }, + "screamingSnakeCase": { + "unsafeName": "VERSION", + "safeName": "VERSION" + }, + "pascalCase": { + "unsafeName": "Version", + "safeName": "Version" + } + }, + "wireValue": "X-API-Version" + }, + "typeReference": { + "type": "literal", + "value": { + "type": "string", + "value": "1.0.0" + } + } + } + ], "endpoints": { "endpoint_service.getWithBearerToken": { "auth": { diff --git a/packages/cli/register/src/ir-to-fdr-converter/__test__/__snapshots__/bearer-token-environment-variable.json b/packages/cli/register/src/ir-to-fdr-converter/__test__/__snapshots__/bearer-token-environment-variable.json index 1d2b343b3e1..f9d9eab53d2 100644 --- a/packages/cli/register/src/ir-to-fdr-converter/__test__/__snapshots__/bearer-token-environment-variable.json +++ b/packages/cli/register/src/ir-to-fdr-converter/__test__/__snapshots__/bearer-token-environment-variable.json @@ -44,7 +44,9 @@ "path": "/apiKey", "pathParameters": {}, "queryParameters": {}, - "headers": {}, + "headers": { + "X-API-Version": "1.0.0" + }, "responseStatusCode": 200, "responseBody": "string", "responseBodyV3": { @@ -76,5 +78,16 @@ "tokenName": "apiKey" }, "snippetsConfiguration": {}, - "globalHeaders": [] + "globalHeaders": [ + { + "key": "X-API-Version", + "type": { + "type": "literal", + "value": { + "type": "stringLiteral", + "value": "1.0.0" + } + } + } + ] } \ No newline at end of file diff --git a/seed/php-model/bearer-token-environment-variable/.mock/definition/api.yml b/seed/php-model/bearer-token-environment-variable/.mock/definition/api.yml index 819d8807966..c5bfa0199c3 100644 --- a/seed/php-model/bearer-token-environment-variable/.mock/definition/api.yml +++ b/seed/php-model/bearer-token-environment-variable/.mock/definition/api.yml @@ -6,3 +6,7 @@ auth-schemes: token: name: apiKey env: COURIER_API_KEY +headers: + X-API-Version: + name: version + type: literal<"1.0.0"> diff --git a/seed/php-model/circular-references-advanced/src/Ast/ContainerValue.php b/seed/php-model/circular-references-advanced/src/Ast/ContainerValue.php index 8fddc16414f..ce70867e5c9 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/ContainerValue.php +++ b/seed/php-model/circular-references-advanced/src/Ast/ContainerValue.php @@ -15,7 +15,8 @@ class ContainerValue extends JsonSerializableType /** * @var ( - * array + * array + * |FieldValue * |mixed * ) $value */ @@ -25,7 +26,8 @@ class ContainerValue extends JsonSerializableType * @param array{ * type: string, * value: ( - * array + * array + * |FieldValue * |mixed * ), * } $values @@ -38,7 +40,7 @@ public function __construct( } /** - * @param array $list + * @param array $list * @return ContainerValue */ public static function list(array $list): ContainerValue @@ -50,10 +52,10 @@ public static function list(array $list): ContainerValue } /** - * @param mixed $optional + * @param ?FieldValue $optional * @return ContainerValue */ - public static function optional(mixed $optional = null): ContainerValue + public static function optional(?FieldValue $optional = null): ContainerValue { return new ContainerValue([ 'type' => 'optional', @@ -82,7 +84,7 @@ public function isList_(): bool } /** - * @return array + * @return array */ public function asList_(): array { @@ -100,15 +102,15 @@ public function asList_(): array */ public function isOptional(): bool { - return is_null($this->value) && $this->type === 'optional'; + return (is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional'; } /** - * @return mixed + * @return ?FieldValue */ - public function asOptional(): mixed + public function asOptional(): ?FieldValue { - if (!(is_null($this->value) && $this->type === 'optional')) { + if (!((is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional')) { throw new Exception( "Expected optional; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -143,6 +145,9 @@ public function jsonSerialize(): array break; case 'optional': $value = $this->asOptional(); + if (!is_null($value)) { + $value = $value->jsonSerialize(); + } $result['optional'] = $value; break; case '_unknown': @@ -210,7 +215,16 @@ public static function jsonDeserialize(array $data): static ); } - $args['optional'] = $data['optional']; + if (is_null($data['optional'])) { + $args['optional'] = null; + } else { + if (!(is_array($data['optional']))) { + throw new Exception( + "Expected property 'optional' in JSON data to be array, instead received " . get_debug_type($data['optional']), + ); + } + $args['optional'] = FieldValue::jsonDeserialize($data['optional']); + } break; case '_unknown': default: diff --git a/seed/php-model/circular-references-advanced/src/Ast/FieldValue.php b/seed/php-model/circular-references-advanced/src/Ast/FieldValue.php index aa3d18b3c46..b3935140973 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/FieldValue.php +++ b/seed/php-model/circular-references-advanced/src/Ast/FieldValue.php @@ -17,6 +17,7 @@ class FieldValue extends JsonSerializableType * @var ( * value-of * |ObjectValue + * |ContainerValue * |mixed * ) $value */ @@ -28,6 +29,7 @@ class FieldValue extends JsonSerializableType * value: ( * value-of * |ObjectValue + * |ContainerValue * |mixed * ), * } $values @@ -64,10 +66,10 @@ public static function objectValue(ObjectValue $objectValue): FieldValue } /** - * @param mixed $containerValue + * @param ContainerValue $containerValue * @return FieldValue */ - public static function containerValue(mixed $containerValue): FieldValue + public static function containerValue(ContainerValue $containerValue): FieldValue { return new FieldValue([ 'type' => 'container_value', @@ -136,15 +138,15 @@ public function asObjectValue(): ObjectValue */ public function isContainerValue(): bool { - return is_null($this->value) && $this->type === 'container_value'; + return $this->value instanceof ContainerValue && $this->type === 'container_value'; } /** - * @return mixed + * @return ContainerValue */ - public function asContainerValue(): mixed + public function asContainerValue(): ContainerValue { - if (!(is_null($this->value) && $this->type === 'container_value')) { + if (!($this->value instanceof ContainerValue && $this->type === 'container_value')) { throw new Exception( "Expected container_value; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -182,7 +184,7 @@ public function jsonSerialize(): array $result = array_merge($value, $result); break; case 'container_value': - $value = $this->value; + $value = $this->asContainerValue()->jsonSerialize(); $result['container_value'] = $value; break; case '_unknown': @@ -254,7 +256,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['container_value'] = $data['container_value']; + if (!(is_array($data['container_value']))) { + throw new Exception( + "Expected property 'containerValue' in JSON data to be array, instead received " . get_debug_type($data['container_value']), + ); + } + $args['container_value'] = ContainerValue::jsonDeserialize($data['container_value']); break; case '_unknown': default: diff --git a/seed/php-model/circular-references-advanced/src/Ast/ObjectFieldValue.php b/seed/php-model/circular-references-advanced/src/Ast/ObjectFieldValue.php index e4ff71b5230..366af8fa026 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/ObjectFieldValue.php +++ b/seed/php-model/circular-references-advanced/src/Ast/ObjectFieldValue.php @@ -17,15 +17,15 @@ class ObjectFieldValue extends JsonSerializableType public string $name; /** - * @var mixed $value + * @var FieldValue $value */ #[JsonProperty('value')] - public mixed $value; + public FieldValue $value; /** * @param array{ * name: string, - * value: mixed, + * value: FieldValue, * } $values */ public function __construct( diff --git a/seed/php-model/circular-references/src/Ast/ContainerValue.php b/seed/php-model/circular-references/src/Ast/ContainerValue.php index 8fddc16414f..ce70867e5c9 100644 --- a/seed/php-model/circular-references/src/Ast/ContainerValue.php +++ b/seed/php-model/circular-references/src/Ast/ContainerValue.php @@ -15,7 +15,8 @@ class ContainerValue extends JsonSerializableType /** * @var ( - * array + * array + * |FieldValue * |mixed * ) $value */ @@ -25,7 +26,8 @@ class ContainerValue extends JsonSerializableType * @param array{ * type: string, * value: ( - * array + * array + * |FieldValue * |mixed * ), * } $values @@ -38,7 +40,7 @@ public function __construct( } /** - * @param array $list + * @param array $list * @return ContainerValue */ public static function list(array $list): ContainerValue @@ -50,10 +52,10 @@ public static function list(array $list): ContainerValue } /** - * @param mixed $optional + * @param ?FieldValue $optional * @return ContainerValue */ - public static function optional(mixed $optional = null): ContainerValue + public static function optional(?FieldValue $optional = null): ContainerValue { return new ContainerValue([ 'type' => 'optional', @@ -82,7 +84,7 @@ public function isList_(): bool } /** - * @return array + * @return array */ public function asList_(): array { @@ -100,15 +102,15 @@ public function asList_(): array */ public function isOptional(): bool { - return is_null($this->value) && $this->type === 'optional'; + return (is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional'; } /** - * @return mixed + * @return ?FieldValue */ - public function asOptional(): mixed + public function asOptional(): ?FieldValue { - if (!(is_null($this->value) && $this->type === 'optional')) { + if (!((is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional')) { throw new Exception( "Expected optional; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -143,6 +145,9 @@ public function jsonSerialize(): array break; case 'optional': $value = $this->asOptional(); + if (!is_null($value)) { + $value = $value->jsonSerialize(); + } $result['optional'] = $value; break; case '_unknown': @@ -210,7 +215,16 @@ public static function jsonDeserialize(array $data): static ); } - $args['optional'] = $data['optional']; + if (is_null($data['optional'])) { + $args['optional'] = null; + } else { + if (!(is_array($data['optional']))) { + throw new Exception( + "Expected property 'optional' in JSON data to be array, instead received " . get_debug_type($data['optional']), + ); + } + $args['optional'] = FieldValue::jsonDeserialize($data['optional']); + } break; case '_unknown': default: diff --git a/seed/php-model/circular-references/src/Ast/FieldValue.php b/seed/php-model/circular-references/src/Ast/FieldValue.php index aa3d18b3c46..b3935140973 100644 --- a/seed/php-model/circular-references/src/Ast/FieldValue.php +++ b/seed/php-model/circular-references/src/Ast/FieldValue.php @@ -17,6 +17,7 @@ class FieldValue extends JsonSerializableType * @var ( * value-of * |ObjectValue + * |ContainerValue * |mixed * ) $value */ @@ -28,6 +29,7 @@ class FieldValue extends JsonSerializableType * value: ( * value-of * |ObjectValue + * |ContainerValue * |mixed * ), * } $values @@ -64,10 +66,10 @@ public static function objectValue(ObjectValue $objectValue): FieldValue } /** - * @param mixed $containerValue + * @param ContainerValue $containerValue * @return FieldValue */ - public static function containerValue(mixed $containerValue): FieldValue + public static function containerValue(ContainerValue $containerValue): FieldValue { return new FieldValue([ 'type' => 'container_value', @@ -136,15 +138,15 @@ public function asObjectValue(): ObjectValue */ public function isContainerValue(): bool { - return is_null($this->value) && $this->type === 'container_value'; + return $this->value instanceof ContainerValue && $this->type === 'container_value'; } /** - * @return mixed + * @return ContainerValue */ - public function asContainerValue(): mixed + public function asContainerValue(): ContainerValue { - if (!(is_null($this->value) && $this->type === 'container_value')) { + if (!($this->value instanceof ContainerValue && $this->type === 'container_value')) { throw new Exception( "Expected container_value; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -182,7 +184,7 @@ public function jsonSerialize(): array $result = array_merge($value, $result); break; case 'container_value': - $value = $this->value; + $value = $this->asContainerValue()->jsonSerialize(); $result['container_value'] = $value; break; case '_unknown': @@ -254,7 +256,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['container_value'] = $data['container_value']; + if (!(is_array($data['container_value']))) { + throw new Exception( + "Expected property 'containerValue' in JSON data to be array, instead received " . get_debug_type($data['container_value']), + ); + } + $args['container_value'] = ContainerValue::jsonDeserialize($data['container_value']); break; case '_unknown': default: diff --git a/seed/php-model/examples/src/Types/BigEntity.php b/seed/php-model/examples/src/Types/BigEntity.php index a3253b3ad4a..a0875fba93d 100644 --- a/seed/php-model/examples/src/Types/BigEntity.php +++ b/seed/php-model/examples/src/Types/BigEntity.php @@ -5,7 +5,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; use Seed\Core\Types\Union; -use Seed\Commons\Types\Metadata; +use Seed\Commons\Types\EventInfo; +use Seed\Commons\Types\Data; class BigEntity extends JsonSerializableType { @@ -32,28 +33,28 @@ class BigEntity extends JsonSerializableType public ?Entity $entity; /** - * @var mixed $metadata + * @var ?Metadata $metadata */ #[JsonProperty('metadata')] - public mixed $metadata; + public ?Metadata $metadata; /** - * @var ?Metadata $commonMetadata + * @var ?\Seed\Commons\Types\Metadata $commonMetadata */ #[JsonProperty('commonMetadata')] - public ?Metadata $commonMetadata; + public ?\Seed\Commons\Types\Metadata $commonMetadata; /** - * @var mixed $eventInfo + * @var ?EventInfo $eventInfo */ #[JsonProperty('eventInfo')] - public mixed $eventInfo; + public ?EventInfo $eventInfo; /** - * @var mixed $data + * @var ?Data $data */ #[JsonProperty('data')] - public mixed $data; + public ?Data $data; /** * @var ?Migration $migration @@ -62,16 +63,16 @@ class BigEntity extends JsonSerializableType public ?Migration $migration; /** - * @var mixed $exception + * @var ?Exception $exception */ #[JsonProperty('exception')] - public mixed $exception; + public ?Exception $exception; /** - * @var mixed $test + * @var ?Test $test */ #[JsonProperty('test')] - public mixed $test; + public ?Test $test; /** * @var ?Node $node @@ -100,13 +101,13 @@ class BigEntity extends JsonSerializableType * )|null, * extendedMovie?: ?ExtendedMovie, * entity?: ?Entity, - * metadata?: mixed, - * commonMetadata?: ?Metadata, - * eventInfo?: mixed, - * data?: mixed, + * metadata?: ?Metadata, + * commonMetadata?: ?\Seed\Commons\Types\Metadata, + * eventInfo?: ?EventInfo, + * data?: ?Data, * migration?: ?Migration, - * exception?: mixed, - * test?: mixed, + * exception?: ?Exception, + * test?: ?Test, * node?: ?Node, * directory?: ?Directory, * moment?: ?Moment, diff --git a/seed/php-model/trace/src/Commons/DebugKeyValuePairs.php b/seed/php-model/trace/src/Commons/DebugKeyValuePairs.php index e2e017b4ea8..3c50e747ddc 100644 --- a/seed/php-model/trace/src/Commons/DebugKeyValuePairs.php +++ b/seed/php-model/trace/src/Commons/DebugKeyValuePairs.php @@ -8,21 +8,21 @@ class DebugKeyValuePairs extends JsonSerializableType { /** - * @var mixed $key + * @var DebugVariableValue $key */ #[JsonProperty('key')] - public mixed $key; + public DebugVariableValue $key; /** - * @var mixed $value + * @var DebugVariableValue $value */ #[JsonProperty('value')] - public mixed $value; + public DebugVariableValue $value; /** * @param array{ - * key: mixed, - * value: mixed, + * key: DebugVariableValue, + * value: DebugVariableValue, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Commons/DebugVariableValue.php b/seed/php-model/trace/src/Commons/DebugVariableValue.php index 12be3474940..69c552efb1e 100644 --- a/seed/php-model/trace/src/Commons/DebugVariableValue.php +++ b/seed/php-model/trace/src/Commons/DebugVariableValue.php @@ -20,7 +20,7 @@ class DebugVariableValue extends JsonSerializableType * |float * |string * |DebugMapValue - * |array + * |array * |BinaryTreeNodeAndTreeValue * |SinglyLinkedListNodeAndListValue * |DoublyLinkedListNodeAndListValue @@ -40,7 +40,7 @@ class DebugVariableValue extends JsonSerializableType * |float * |string * |DebugMapValue - * |array + * |array * |BinaryTreeNodeAndTreeValue * |SinglyLinkedListNodeAndListValue * |DoublyLinkedListNodeAndListValue @@ -130,7 +130,7 @@ public static function mapValue(DebugMapValue $mapValue): DebugVariableValue } /** - * @param array $listValue + * @param array $listValue * @return DebugVariableValue */ public static function listValue(array $listValue): DebugVariableValue @@ -364,7 +364,7 @@ public function isListValue(): bool } /** - * @return array + * @return array */ public function asListValue(): array { diff --git a/seed/php-model/trace/src/Commons/KeyValuePair.php b/seed/php-model/trace/src/Commons/KeyValuePair.php index 5f73294c85a..783cc6a3ace 100644 --- a/seed/php-model/trace/src/Commons/KeyValuePair.php +++ b/seed/php-model/trace/src/Commons/KeyValuePair.php @@ -8,21 +8,21 @@ class KeyValuePair extends JsonSerializableType { /** - * @var mixed $key + * @var VariableValue $key */ #[JsonProperty('key')] - public mixed $key; + public VariableValue $key; /** - * @var mixed $value + * @var VariableValue $value */ #[JsonProperty('value')] - public mixed $value; + public VariableValue $value; /** * @param array{ - * key: mixed, - * value: mixed, + * key: VariableValue, + * value: VariableValue, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Commons/ListType.php b/seed/php-model/trace/src/Commons/ListType.php index 62052407de9..d52338d43fd 100644 --- a/seed/php-model/trace/src/Commons/ListType.php +++ b/seed/php-model/trace/src/Commons/ListType.php @@ -8,10 +8,10 @@ class ListType extends JsonSerializableType { /** - * @var mixed $valueType + * @var VariableType $valueType */ #[JsonProperty('valueType')] - public mixed $valueType; + public VariableType $valueType; /** * @var ?bool $isFixedLength Whether this list is fixed-size (for languages that supports fixed-size lists). Defaults to false. @@ -21,7 +21,7 @@ class ListType extends JsonSerializableType /** * @param array{ - * valueType: mixed, + * valueType: VariableType, * isFixedLength?: ?bool, * } $values */ diff --git a/seed/php-model/trace/src/Commons/MapType.php b/seed/php-model/trace/src/Commons/MapType.php index 0535bcb6a7c..3f4305a851b 100644 --- a/seed/php-model/trace/src/Commons/MapType.php +++ b/seed/php-model/trace/src/Commons/MapType.php @@ -8,21 +8,21 @@ class MapType extends JsonSerializableType { /** - * @var mixed $keyType + * @var VariableType $keyType */ #[JsonProperty('keyType')] - public mixed $keyType; + public VariableType $keyType; /** - * @var mixed $valueType + * @var VariableType $valueType */ #[JsonProperty('valueType')] - public mixed $valueType; + public VariableType $valueType; /** * @param array{ - * keyType: mixed, - * valueType: mixed, + * keyType: VariableType, + * valueType: VariableType, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Commons/TestCase.php b/seed/php-model/trace/src/Commons/TestCase.php index 14c68b26189..6878519b6f0 100644 --- a/seed/php-model/trace/src/Commons/TestCase.php +++ b/seed/php-model/trace/src/Commons/TestCase.php @@ -15,15 +15,15 @@ class TestCase extends JsonSerializableType public string $id; /** - * @var array $params + * @var array $params */ - #[JsonProperty('params'), ArrayType(['mixed'])] + #[JsonProperty('params'), ArrayType([VariableValue::class])] public array $params; /** * @param array{ * id: string, - * params: array, + * params: array, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Commons/TestCaseWithExpectedResult.php b/seed/php-model/trace/src/Commons/TestCaseWithExpectedResult.php index 718ad685e6b..2a190f4db28 100644 --- a/seed/php-model/trace/src/Commons/TestCaseWithExpectedResult.php +++ b/seed/php-model/trace/src/Commons/TestCaseWithExpectedResult.php @@ -14,15 +14,15 @@ class TestCaseWithExpectedResult extends JsonSerializableType public TestCase $testCase; /** - * @var mixed $expectedResult + * @var VariableValue $expectedResult */ #[JsonProperty('expectedResult')] - public mixed $expectedResult; + public VariableValue $expectedResult; /** * @param array{ * testCase: TestCase, - * expectedResult: mixed, + * expectedResult: VariableValue, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Commons/VariableValue.php b/seed/php-model/trace/src/Commons/VariableValue.php index 5bbe91a6f94..7e3fcda32db 100644 --- a/seed/php-model/trace/src/Commons/VariableValue.php +++ b/seed/php-model/trace/src/Commons/VariableValue.php @@ -20,7 +20,7 @@ class VariableValue extends JsonSerializableType * |float * |string * |MapValue - * |array + * |array * |BinaryTreeValue * |SinglyLinkedListValue * |DoublyLinkedListValue @@ -39,7 +39,7 @@ class VariableValue extends JsonSerializableType * |float * |string * |MapValue - * |array + * |array * |BinaryTreeValue * |SinglyLinkedListValue * |DoublyLinkedListValue @@ -128,7 +128,7 @@ public static function mapValue(MapValue $mapValue): VariableValue } /** - * @param array $listValue + * @param array $listValue * @return VariableValue */ public static function listValue(array $listValue): VariableValue @@ -339,7 +339,7 @@ public function isListValue(): bool } /** - * @return array + * @return array */ public function asListValue(): array { diff --git a/seed/php-model/trace/src/Problem/CreateProblemRequest.php b/seed/php-model/trace/src/Problem/CreateProblemRequest.php index 41766a9e8af..eb7d6f1ebf7 100644 --- a/seed/php-model/trace/src/Problem/CreateProblemRequest.php +++ b/seed/php-model/trace/src/Problem/CreateProblemRequest.php @@ -6,6 +6,7 @@ use Seed\Core\Json\JsonProperty; use Seed\Commons\Language; use Seed\Core\Types\ArrayType; +use Seed\Commons\VariableType; use Seed\Commons\TestCaseWithExpectedResult; class CreateProblemRequest extends JsonSerializableType @@ -35,10 +36,10 @@ class CreateProblemRequest extends JsonSerializableType public array $inputParams; /** - * @var mixed $outputType + * @var VariableType $outputType */ #[JsonProperty('outputType')] - public mixed $outputType; + public VariableType $outputType; /** * @var array $testcases @@ -58,7 +59,7 @@ class CreateProblemRequest extends JsonSerializableType * problemDescription: ProblemDescription, * files: array, ProblemFiles>, * inputParams: array, - * outputType: mixed, + * outputType: VariableType, * testcases: array, * methodName: string, * } $values diff --git a/seed/php-model/trace/src/Problem/CreateProblemResponse.php b/seed/php-model/trace/src/Problem/CreateProblemResponse.php index 73201a47428..f6fda255a86 100644 --- a/seed/php-model/trace/src/Problem/CreateProblemResponse.php +++ b/seed/php-model/trace/src/Problem/CreateProblemResponse.php @@ -16,6 +16,7 @@ class CreateProblemResponse extends JsonSerializableType /** * @var ( * string + * |CreateProblemError * |mixed * ) $value */ @@ -26,6 +27,7 @@ class CreateProblemResponse extends JsonSerializableType * type: string, * value: ( * string + * |CreateProblemError * |mixed * ), * } $values @@ -50,10 +52,10 @@ public static function success(string $success): CreateProblemResponse } /** - * @param mixed $error + * @param CreateProblemError $error * @return CreateProblemResponse */ - public static function error(mixed $error): CreateProblemResponse + public static function error(CreateProblemError $error): CreateProblemResponse { return new CreateProblemResponse([ 'type' => 'error', @@ -100,15 +102,15 @@ public function asSuccess(): string */ public function isError(): bool { - return is_null($this->value) && $this->type === 'error'; + return $this->value instanceof CreateProblemError && $this->type === 'error'; } /** - * @return mixed + * @return CreateProblemError */ - public function asError(): mixed + public function asError(): CreateProblemError { - if (!(is_null($this->value) && $this->type === 'error')) { + if (!($this->value instanceof CreateProblemError && $this->type === 'error')) { throw new Exception( "Expected error; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -142,7 +144,7 @@ public function jsonSerialize(): array $result['success'] = $value; break; case 'error': - $value = $this->value; + $value = $this->asError()->jsonSerialize(); $result['error'] = $value; break; case '_unknown': @@ -210,7 +212,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['error'] = $data['error']; + if (!(is_array($data['error']))) { + throw new Exception( + "Expected property 'error' in JSON data to be array, instead received " . get_debug_type($data['error']), + ); + } + $args['error'] = CreateProblemError::jsonDeserialize($data['error']); break; case '_unknown': default: diff --git a/seed/php-model/trace/src/Problem/ProblemDescription.php b/seed/php-model/trace/src/Problem/ProblemDescription.php index e7d4357c9ca..c58430953e9 100644 --- a/seed/php-model/trace/src/Problem/ProblemDescription.php +++ b/seed/php-model/trace/src/Problem/ProblemDescription.php @@ -9,14 +9,14 @@ class ProblemDescription extends JsonSerializableType { /** - * @var array $boards + * @var array $boards */ - #[JsonProperty('boards'), ArrayType(['mixed'])] + #[JsonProperty('boards'), ArrayType([ProblemDescriptionBoard::class])] public array $boards; /** * @param array{ - * boards: array, + * boards: array, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Problem/ProblemDescriptionBoard.php b/seed/php-model/trace/src/Problem/ProblemDescriptionBoard.php index cbcdfda80cd..053588fe2bb 100644 --- a/seed/php-model/trace/src/Problem/ProblemDescriptionBoard.php +++ b/seed/php-model/trace/src/Problem/ProblemDescriptionBoard.php @@ -3,6 +3,7 @@ namespace Seed\Problem; use Seed\Core\Json\JsonSerializableType; +use Seed\Commons\VariableValue; use Exception; use Seed\Core\Json\JsonDecoder; @@ -16,6 +17,7 @@ class ProblemDescriptionBoard extends JsonSerializableType /** * @var ( * string + * |VariableValue * |mixed * ) $value */ @@ -26,6 +28,7 @@ class ProblemDescriptionBoard extends JsonSerializableType * type: string, * value: ( * string + * |VariableValue * |mixed * ), * } $values @@ -50,10 +53,10 @@ public static function html(string $html): ProblemDescriptionBoard } /** - * @param mixed $variable + * @param VariableValue $variable * @return ProblemDescriptionBoard */ - public static function variable(mixed $variable): ProblemDescriptionBoard + public static function variable(VariableValue $variable): ProblemDescriptionBoard { return new ProblemDescriptionBoard([ 'type' => 'variable', @@ -112,15 +115,15 @@ public function asHtml(): string */ public function isVariable(): bool { - return is_null($this->value) && $this->type === 'variable'; + return $this->value instanceof VariableValue && $this->type === 'variable'; } /** - * @return mixed + * @return VariableValue */ - public function asVariable(): mixed + public function asVariable(): VariableValue { - if (!(is_null($this->value) && $this->type === 'variable')) { + if (!($this->value instanceof VariableValue && $this->type === 'variable')) { throw new Exception( "Expected variable; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -176,7 +179,7 @@ public function jsonSerialize(): array $result['html'] = $value; break; case 'variable': - $value = $this->value; + $value = $this->asVariable()->jsonSerialize(); $result['variable'] = $value; break; case 'testCaseId': @@ -248,7 +251,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['variable'] = $data['variable']; + if (!(is_array($data['variable']))) { + throw new Exception( + "Expected property 'variable' in JSON data to be array, instead received " . get_debug_type($data['variable']), + ); + } + $args['variable'] = VariableValue::jsonDeserialize($data['variable']); break; case 'testCaseId': $args['type'] = 'testCaseId'; diff --git a/seed/php-model/trace/src/Problem/ProblemInfo.php b/seed/php-model/trace/src/Problem/ProblemInfo.php index dfbb30591fe..b19caf57dde 100644 --- a/seed/php-model/trace/src/Problem/ProblemInfo.php +++ b/seed/php-model/trace/src/Problem/ProblemInfo.php @@ -6,6 +6,7 @@ use Seed\Core\Json\JsonProperty; use Seed\Commons\Language; use Seed\Core\Types\ArrayType; +use Seed\Commons\VariableType; use Seed\Commons\TestCaseWithExpectedResult; class ProblemInfo extends JsonSerializableType @@ -47,10 +48,10 @@ class ProblemInfo extends JsonSerializableType public array $inputParams; /** - * @var mixed $outputType + * @var VariableType $outputType */ #[JsonProperty('outputType')] - public mixed $outputType; + public VariableType $outputType; /** * @var array $testcases @@ -78,7 +79,7 @@ class ProblemInfo extends JsonSerializableType * problemVersion: int, * files: array, ProblemFiles>, * inputParams: array, - * outputType: mixed, + * outputType: VariableType, * testcases: array, * methodName: string, * supportsCustomTestCases: bool, diff --git a/seed/php-model/trace/src/Problem/VariableTypeAndName.php b/seed/php-model/trace/src/Problem/VariableTypeAndName.php index 16d9eb4dedc..190aaedd8de 100644 --- a/seed/php-model/trace/src/Problem/VariableTypeAndName.php +++ b/seed/php-model/trace/src/Problem/VariableTypeAndName.php @@ -3,15 +3,16 @@ namespace Seed\Problem; use Seed\Core\Json\JsonSerializableType; +use Seed\Commons\VariableType; use Seed\Core\Json\JsonProperty; class VariableTypeAndName extends JsonSerializableType { /** - * @var mixed $variableType + * @var VariableType $variableType */ #[JsonProperty('variableType')] - public mixed $variableType; + public VariableType $variableType; /** * @var string $name @@ -21,7 +22,7 @@ class VariableTypeAndName extends JsonSerializableType /** * @param array{ - * variableType: mixed, + * variableType: VariableType, * name: string, * } $values */ diff --git a/seed/php-model/trace/src/Submission/ActualResult.php b/seed/php-model/trace/src/Submission/ActualResult.php index 46daba77ceb..9a7861f3917 100644 --- a/seed/php-model/trace/src/Submission/ActualResult.php +++ b/seed/php-model/trace/src/Submission/ActualResult.php @@ -3,6 +3,7 @@ namespace Seed\Submission; use Seed\Core\Json\JsonSerializableType; +use Seed\Commons\VariableValue; use Exception; use Seed\Core\Json\JsonDecoder; @@ -15,8 +16,10 @@ class ActualResult extends JsonSerializableType /** * @var ( - * mixed + * VariableValue * |ExceptionInfo + * |ExceptionV2 + * |mixed * ) $value */ public readonly mixed $value; @@ -25,8 +28,10 @@ class ActualResult extends JsonSerializableType * @param array{ * type: string, * value: ( - * mixed + * VariableValue * |ExceptionInfo + * |ExceptionV2 + * |mixed * ), * } $values */ @@ -38,10 +43,10 @@ public function __construct( } /** - * @param mixed $value + * @param VariableValue $value * @return ActualResult */ - public static function value(mixed $value): ActualResult + public static function value(VariableValue $value): ActualResult { return new ActualResult([ 'type' => 'value', @@ -62,10 +67,10 @@ public static function exception(ExceptionInfo $exception): ActualResult } /** - * @param mixed $exceptionV2 + * @param ExceptionV2 $exceptionV2 * @return ActualResult */ - public static function exceptionV2(mixed $exceptionV2): ActualResult + public static function exceptionV2(ExceptionV2 $exceptionV2): ActualResult { return new ActualResult([ 'type' => 'exceptionV2', @@ -90,15 +95,15 @@ public static function _unknown(mixed $_unknown): ActualResult */ public function isValue(): bool { - return is_null($this->value) && $this->type === 'value'; + return $this->value instanceof VariableValue && $this->type === 'value'; } /** - * @return mixed + * @return VariableValue */ - public function asValue(): mixed + public function asValue(): VariableValue { - if (!(is_null($this->value) && $this->type === 'value')) { + if (!($this->value instanceof VariableValue && $this->type === 'value')) { throw new Exception( "Expected value; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -134,15 +139,15 @@ public function asException(): ExceptionInfo */ public function isExceptionV2(): bool { - return is_null($this->value) && $this->type === 'exceptionV2'; + return $this->value instanceof ExceptionV2 && $this->type === 'exceptionV2'; } /** - * @return mixed + * @return ExceptionV2 */ - public function asExceptionV2(): mixed + public function asExceptionV2(): ExceptionV2 { - if (!(is_null($this->value) && $this->type === 'exceptionV2')) { + if (!($this->value instanceof ExceptionV2 && $this->type === 'exceptionV2')) { throw new Exception( "Expected exceptionV2; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -172,7 +177,7 @@ public function jsonSerialize(): array switch ($this->type) { case 'value': - $value = $this->value; + $value = $this->asValue()->jsonSerialize(); $result['value'] = $value; break; case 'exception': @@ -180,7 +185,7 @@ public function jsonSerialize(): array $result = array_merge($value, $result); break; case 'exceptionV2': - $value = $this->value; + $value = $this->asExceptionV2()->jsonSerialize(); $result['exceptionV2'] = $value; break; case '_unknown': @@ -238,7 +243,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['value'] = $data['value']; + if (!(is_array($data['value']))) { + throw new Exception( + "Expected property 'value' in JSON data to be array, instead received " . get_debug_type($data['value']), + ); + } + $args['value'] = VariableValue::jsonDeserialize($data['value']); break; case 'exception': $args['type'] = 'exception'; @@ -252,7 +262,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['exceptionV2'] = $data['exceptionV2']; + if (!(is_array($data['exceptionV2']))) { + throw new Exception( + "Expected property 'exceptionV2' in JSON data to be array, instead received " . get_debug_type($data['exceptionV2']), + ); + } + $args['exceptionV2'] = ExceptionV2::jsonDeserialize($data['exceptionV2']); break; case '_unknown': default: diff --git a/seed/php-model/trace/src/Submission/ErroredResponse.php b/seed/php-model/trace/src/Submission/ErroredResponse.php index f1b545cfc52..24e150a76cd 100644 --- a/seed/php-model/trace/src/Submission/ErroredResponse.php +++ b/seed/php-model/trace/src/Submission/ErroredResponse.php @@ -14,15 +14,15 @@ class ErroredResponse extends JsonSerializableType public string $submissionId; /** - * @var mixed $errorInfo + * @var ErrorInfo $errorInfo */ #[JsonProperty('errorInfo')] - public mixed $errorInfo; + public ErrorInfo $errorInfo; /** * @param array{ * submissionId: string, - * errorInfo: mixed, + * errorInfo: ErrorInfo, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Submission/GetSubmissionStateResponse.php b/seed/php-model/trace/src/Submission/GetSubmissionStateResponse.php index c33dc0177f1..f40faead428 100644 --- a/seed/php-model/trace/src/Submission/GetSubmissionStateResponse.php +++ b/seed/php-model/trace/src/Submission/GetSubmissionStateResponse.php @@ -29,16 +29,16 @@ class GetSubmissionStateResponse extends JsonSerializableType public string $language; /** - * @var mixed $submissionTypeState + * @var SubmissionTypeState $submissionTypeState */ #[JsonProperty('submissionTypeState')] - public mixed $submissionTypeState; + public SubmissionTypeState $submissionTypeState; /** * @param array{ * submission: string, * language: value-of, - * submissionTypeState: mixed, + * submissionTypeState: SubmissionTypeState, * timeSubmitted?: ?DateTime, * } $values */ diff --git a/seed/php-model/trace/src/Submission/GradedResponseV2.php b/seed/php-model/trace/src/Submission/GradedResponseV2.php index 6de03f82dd9..cd12edb2506 100644 --- a/seed/php-model/trace/src/Submission/GradedResponseV2.php +++ b/seed/php-model/trace/src/Submission/GradedResponseV2.php @@ -15,15 +15,15 @@ class GradedResponseV2 extends JsonSerializableType public string $submissionId; /** - * @var array $testCases + * @var array $testCases */ - #[JsonProperty('testCases'), ArrayType(['string' => 'mixed'])] + #[JsonProperty('testCases'), ArrayType(['string' => TestCaseGrade::class])] public array $testCases; /** * @param array{ * submissionId: string, - * testCases: array, + * testCases: array, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Submission/GradedTestCaseUpdate.php b/seed/php-model/trace/src/Submission/GradedTestCaseUpdate.php index f0f702e1f8d..24a1add1d48 100644 --- a/seed/php-model/trace/src/Submission/GradedTestCaseUpdate.php +++ b/seed/php-model/trace/src/Submission/GradedTestCaseUpdate.php @@ -14,15 +14,15 @@ class GradedTestCaseUpdate extends JsonSerializableType public string $testCaseId; /** - * @var mixed $grade + * @var TestCaseGrade $grade */ #[JsonProperty('grade')] - public mixed $grade; + public TestCaseGrade $grade; /** * @param array{ * testCaseId: string, - * grade: mixed, + * grade: TestCaseGrade, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Submission/InvalidRequestResponse.php b/seed/php-model/trace/src/Submission/InvalidRequestResponse.php index feaac86dbfa..d971ed240bd 100644 --- a/seed/php-model/trace/src/Submission/InvalidRequestResponse.php +++ b/seed/php-model/trace/src/Submission/InvalidRequestResponse.php @@ -8,21 +8,21 @@ class InvalidRequestResponse extends JsonSerializableType { /** - * @var mixed $request + * @var SubmissionRequest $request */ #[JsonProperty('request')] - public mixed $request; + public SubmissionRequest $request; /** - * @var mixed $cause + * @var InvalidRequestCause $cause */ #[JsonProperty('cause')] - public mixed $cause; + public InvalidRequestCause $cause; /** * @param array{ - * request: mixed, - * cause: mixed, + * request: SubmissionRequest, + * cause: InvalidRequestCause, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Submission/Scope.php b/seed/php-model/trace/src/Submission/Scope.php index ec1a46dbaea..5f112404c2b 100644 --- a/seed/php-model/trace/src/Submission/Scope.php +++ b/seed/php-model/trace/src/Submission/Scope.php @@ -3,20 +3,21 @@ namespace Seed\Submission; use Seed\Core\Json\JsonSerializableType; +use Seed\Commons\DebugVariableValue; use Seed\Core\Json\JsonProperty; use Seed\Core\Types\ArrayType; class Scope extends JsonSerializableType { /** - * @var array $variables + * @var array $variables */ - #[JsonProperty('variables'), ArrayType(['string' => 'mixed'])] + #[JsonProperty('variables'), ArrayType(['string' => DebugVariableValue::class])] public array $variables; /** * @param array{ - * variables: array, + * variables: array, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Submission/SubmissionResponse.php b/seed/php-model/trace/src/Submission/SubmissionResponse.php index 0551ffea36e..3a3a739a343 100644 --- a/seed/php-model/trace/src/Submission/SubmissionResponse.php +++ b/seed/php-model/trace/src/Submission/SubmissionResponse.php @@ -18,8 +18,9 @@ class SubmissionResponse extends JsonSerializableType * null * |string * |ExceptionInfo - * |mixed + * |CodeExecutionUpdate * |TerminatedResponse + * |mixed * ) $value */ public readonly mixed $value; @@ -31,8 +32,9 @@ class SubmissionResponse extends JsonSerializableType * null * |string * |ExceptionInfo - * |mixed + * |CodeExecutionUpdate * |TerminatedResponse + * |mixed * ), * } $values */ @@ -90,10 +92,10 @@ public static function serverErrored(ExceptionInfo $serverErrored): SubmissionRe } /** - * @param mixed $codeExecutionUpdate + * @param CodeExecutionUpdate $codeExecutionUpdate * @return SubmissionResponse */ - public static function codeExecutionUpdate(mixed $codeExecutionUpdate): SubmissionResponse + public static function codeExecutionUpdate(CodeExecutionUpdate $codeExecutionUpdate): SubmissionResponse { return new SubmissionResponse([ 'type' => 'codeExecutionUpdate', @@ -190,15 +192,15 @@ public function asServerErrored(): ExceptionInfo */ public function isCodeExecutionUpdate(): bool { - return is_null($this->value) && $this->type === 'codeExecutionUpdate'; + return $this->value instanceof CodeExecutionUpdate && $this->type === 'codeExecutionUpdate'; } /** - * @return mixed + * @return CodeExecutionUpdate */ - public function asCodeExecutionUpdate(): mixed + public function asCodeExecutionUpdate(): CodeExecutionUpdate { - if (!(is_null($this->value) && $this->type === 'codeExecutionUpdate')) { + if (!($this->value instanceof CodeExecutionUpdate && $this->type === 'codeExecutionUpdate')) { throw new Exception( "Expected codeExecutionUpdate; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -264,7 +266,7 @@ public function jsonSerialize(): array $result = array_merge($value, $result); break; case 'codeExecutionUpdate': - $value = $this->value; + $value = $this->asCodeExecutionUpdate()->jsonSerialize(); $result['codeExecutionUpdate'] = $value; break; case 'terminated': @@ -348,7 +350,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['codeExecutionUpdate'] = $data['codeExecutionUpdate']; + if (!(is_array($data['codeExecutionUpdate']))) { + throw new Exception( + "Expected property 'codeExecutionUpdate' in JSON data to be array, instead received " . get_debug_type($data['codeExecutionUpdate']), + ); + } + $args['codeExecutionUpdate'] = CodeExecutionUpdate::jsonDeserialize($data['codeExecutionUpdate']); break; case 'terminated': $args['type'] = 'terminated'; diff --git a/seed/php-model/trace/src/Submission/SubmissionStatusForTestCase.php b/seed/php-model/trace/src/Submission/SubmissionStatusForTestCase.php index 98f670a14ed..1e317099572 100644 --- a/seed/php-model/trace/src/Submission/SubmissionStatusForTestCase.php +++ b/seed/php-model/trace/src/Submission/SubmissionStatusForTestCase.php @@ -16,8 +16,9 @@ class SubmissionStatusForTestCase extends JsonSerializableType /** * @var ( * TestCaseResultWithStdout - * |mixed + * |TestCaseGrade * |TracedTestCase + * |mixed * ) $value */ public readonly mixed $value; @@ -27,8 +28,9 @@ class SubmissionStatusForTestCase extends JsonSerializableType * type: string, * value: ( * TestCaseResultWithStdout - * |mixed + * |TestCaseGrade * |TracedTestCase + * |mixed * ), * } $values */ @@ -52,10 +54,10 @@ public static function graded(TestCaseResultWithStdout $graded): SubmissionStatu } /** - * @param mixed $gradedV2 + * @param TestCaseGrade $gradedV2 * @return SubmissionStatusForTestCase */ - public static function gradedV2(mixed $gradedV2): SubmissionStatusForTestCase + public static function gradedV2(TestCaseGrade $gradedV2): SubmissionStatusForTestCase { return new SubmissionStatusForTestCase([ 'type' => 'gradedV2', @@ -114,15 +116,15 @@ public function asGraded(): TestCaseResultWithStdout */ public function isGradedV2(): bool { - return is_null($this->value) && $this->type === 'gradedV2'; + return $this->value instanceof TestCaseGrade && $this->type === 'gradedV2'; } /** - * @return mixed + * @return TestCaseGrade */ - public function asGradedV2(): mixed + public function asGradedV2(): TestCaseGrade { - if (!(is_null($this->value) && $this->type === 'gradedV2')) { + if (!($this->value instanceof TestCaseGrade && $this->type === 'gradedV2')) { throw new Exception( "Expected gradedV2; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -178,7 +180,7 @@ public function jsonSerialize(): array $result = array_merge($value, $result); break; case 'gradedV2': - $value = $this->value; + $value = $this->asGradedV2()->jsonSerialize(); $result['gradedV2'] = $value; break; case 'traced': @@ -244,7 +246,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['gradedV2'] = $data['gradedV2']; + if (!(is_array($data['gradedV2']))) { + throw new Exception( + "Expected property 'gradedV2' in JSON data to be array, instead received " . get_debug_type($data['gradedV2']), + ); + } + $args['gradedV2'] = TestCaseGrade::jsonDeserialize($data['gradedV2']); break; case 'traced': $args['type'] = 'traced'; diff --git a/seed/php-model/trace/src/Submission/TestCaseNonHiddenGrade.php b/seed/php-model/trace/src/Submission/TestCaseNonHiddenGrade.php index f13c0c1c6ec..f9d70caa89d 100644 --- a/seed/php-model/trace/src/Submission/TestCaseNonHiddenGrade.php +++ b/seed/php-model/trace/src/Submission/TestCaseNonHiddenGrade.php @@ -4,6 +4,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; +use Seed\Commons\VariableValue; class TestCaseNonHiddenGrade extends JsonSerializableType { @@ -14,16 +15,16 @@ class TestCaseNonHiddenGrade extends JsonSerializableType public bool $passed; /** - * @var mixed $actualResult + * @var ?VariableValue $actualResult */ #[JsonProperty('actualResult')] - public mixed $actualResult; + public ?VariableValue $actualResult; /** - * @var mixed $exception + * @var ?ExceptionV2 $exception */ #[JsonProperty('exception')] - public mixed $exception; + public ?ExceptionV2 $exception; /** * @var string $stdout @@ -35,8 +36,8 @@ class TestCaseNonHiddenGrade extends JsonSerializableType * @param array{ * passed: bool, * stdout: string, - * actualResult?: mixed, - * exception?: mixed, + * actualResult?: ?VariableValue, + * exception?: ?ExceptionV2, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Submission/TestCaseResult.php b/seed/php-model/trace/src/Submission/TestCaseResult.php index 5f91f004b2a..90f387be19e 100644 --- a/seed/php-model/trace/src/Submission/TestCaseResult.php +++ b/seed/php-model/trace/src/Submission/TestCaseResult.php @@ -3,21 +3,22 @@ namespace Seed\Submission; use Seed\Core\Json\JsonSerializableType; +use Seed\Commons\VariableValue; use Seed\Core\Json\JsonProperty; class TestCaseResult extends JsonSerializableType { /** - * @var mixed $expectedResult + * @var VariableValue $expectedResult */ #[JsonProperty('expectedResult')] - public mixed $expectedResult; + public VariableValue $expectedResult; /** - * @var mixed $actualResult + * @var ActualResult $actualResult */ #[JsonProperty('actualResult')] - public mixed $actualResult; + public ActualResult $actualResult; /** * @var bool $passed @@ -27,8 +28,8 @@ class TestCaseResult extends JsonSerializableType /** * @param array{ - * expectedResult: mixed, - * actualResult: mixed, + * expectedResult: VariableValue, + * actualResult: ActualResult, * passed: bool, * } $values */ diff --git a/seed/php-model/trace/src/Submission/TestSubmissionState.php b/seed/php-model/trace/src/Submission/TestSubmissionState.php index 69c52ec3ac2..3785157f2eb 100644 --- a/seed/php-model/trace/src/Submission/TestSubmissionState.php +++ b/seed/php-model/trace/src/Submission/TestSubmissionState.php @@ -28,17 +28,17 @@ class TestSubmissionState extends JsonSerializableType public array $customTestCases; /** - * @var mixed $status + * @var TestSubmissionStatus $status */ #[JsonProperty('status')] - public mixed $status; + public TestSubmissionStatus $status; /** * @param array{ * problemId: string, * defaultTestCases: array, * customTestCases: array, - * status: mixed, + * status: TestSubmissionStatus, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Submission/TestSubmissionStatus.php b/seed/php-model/trace/src/Submission/TestSubmissionStatus.php index 92ba25a84fc..3a9d00825e1 100644 --- a/seed/php-model/trace/src/Submission/TestSubmissionStatus.php +++ b/seed/php-model/trace/src/Submission/TestSubmissionStatus.php @@ -16,9 +16,10 @@ class TestSubmissionStatus extends JsonSerializableType /** * @var ( * null - * |mixed + * |ErrorInfo * |value-of - * |array + * |array + * |mixed * ) $value */ public readonly mixed $value; @@ -28,9 +29,10 @@ class TestSubmissionStatus extends JsonSerializableType * type: string, * value: ( * null - * |mixed + * |ErrorInfo * |value-of - * |array + * |array + * |mixed * ), * } $values */ @@ -53,10 +55,10 @@ public static function stopped(): TestSubmissionStatus } /** - * @param mixed $errored + * @param ErrorInfo $errored * @return TestSubmissionStatus */ - public static function errored(mixed $errored): TestSubmissionStatus + public static function errored(ErrorInfo $errored): TestSubmissionStatus { return new TestSubmissionStatus([ 'type' => 'errored', @@ -77,7 +79,7 @@ public static function running(string $running): TestSubmissionStatus } /** - * @param array $testCaseIdToState + * @param array $testCaseIdToState * @return TestSubmissionStatus */ public static function testCaseIdToState(array $testCaseIdToState): TestSubmissionStatus @@ -113,15 +115,15 @@ public function isStopped(): bool */ public function isErrored(): bool { - return is_null($this->value) && $this->type === 'errored'; + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** - * @return mixed + * @return ErrorInfo */ - public function asErrored(): mixed + public function asErrored(): ErrorInfo { - if (!(is_null($this->value) && $this->type === 'errored')) { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -161,7 +163,7 @@ public function isTestCaseIdToState(): bool } /** - * @return array + * @return array */ public function asTestCaseIdToState(): array { @@ -198,7 +200,7 @@ public function jsonSerialize(): array $result['stopped'] = []; break; case 'errored': - $value = $this->value; + $value = $this->asErrored()->jsonSerialize(); $result['errored'] = $value; break; case 'running': @@ -268,7 +270,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['errored'] = $data['errored']; + if (!(is_array($data['errored']))) { + throw new Exception( + "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), + ); + } + $args['errored'] = ErrorInfo::jsonDeserialize($data['errored']); break; case 'running': $args['type'] = 'running'; diff --git a/seed/php-model/trace/src/Submission/TestSubmissionUpdate.php b/seed/php-model/trace/src/Submission/TestSubmissionUpdate.php index 4fdf29e1ea7..aea54bab391 100644 --- a/seed/php-model/trace/src/Submission/TestSubmissionUpdate.php +++ b/seed/php-model/trace/src/Submission/TestSubmissionUpdate.php @@ -16,15 +16,15 @@ class TestSubmissionUpdate extends JsonSerializableType public DateTime $updateTime; /** - * @var mixed $updateInfo + * @var TestSubmissionUpdateInfo $updateInfo */ #[JsonProperty('updateInfo')] - public mixed $updateInfo; + public TestSubmissionUpdateInfo $updateInfo; /** * @param array{ * updateTime: DateTime, - * updateInfo: mixed, + * updateInfo: TestSubmissionUpdateInfo, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Submission/TestSubmissionUpdateInfo.php b/seed/php-model/trace/src/Submission/TestSubmissionUpdateInfo.php index 85621883f72..b39092954af 100644 --- a/seed/php-model/trace/src/Submission/TestSubmissionUpdateInfo.php +++ b/seed/php-model/trace/src/Submission/TestSubmissionUpdateInfo.php @@ -17,9 +17,10 @@ class TestSubmissionUpdateInfo extends JsonSerializableType * @var ( * value-of * |null - * |mixed + * |ErrorInfo * |GradedTestCaseUpdate * |RecordedTestCaseUpdate + * |mixed * ) $value */ public readonly mixed $value; @@ -30,9 +31,10 @@ class TestSubmissionUpdateInfo extends JsonSerializableType * value: ( * value-of * |null - * |mixed + * |ErrorInfo * |GradedTestCaseUpdate * |RecordedTestCaseUpdate + * |mixed * ), * } $values */ @@ -67,10 +69,10 @@ public static function stopped(): TestSubmissionUpdateInfo } /** - * @param mixed $errored + * @param ErrorInfo $errored * @return TestSubmissionUpdateInfo */ - public static function errored(mixed $errored): TestSubmissionUpdateInfo + public static function errored(ErrorInfo $errored): TestSubmissionUpdateInfo { return new TestSubmissionUpdateInfo([ 'type' => 'errored', @@ -160,15 +162,15 @@ public function isStopped(): bool */ public function isErrored(): bool { - return is_null($this->value) && $this->type === 'errored'; + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** - * @return mixed + * @return ErrorInfo */ - public function asErrored(): mixed + public function asErrored(): ErrorInfo { - if (!(is_null($this->value) && $this->type === 'errored')) { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -257,7 +259,7 @@ public function jsonSerialize(): array $result['stopped'] = []; break; case 'errored': - $value = $this->value; + $value = $this->asErrored()->jsonSerialize(); $result['errored'] = $value; break; case 'gradedTestCase': @@ -340,7 +342,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['errored'] = $data['errored']; + if (!(is_array($data['errored']))) { + throw new Exception( + "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), + ); + } + $args['errored'] = ErrorInfo::jsonDeserialize($data['errored']); break; case 'gradedTestCase': $args['type'] = 'gradedTestCase'; diff --git a/seed/php-model/trace/src/Submission/TraceResponse.php b/seed/php-model/trace/src/Submission/TraceResponse.php index b7d8da166e8..1248f4f4cf9 100644 --- a/seed/php-model/trace/src/Submission/TraceResponse.php +++ b/seed/php-model/trace/src/Submission/TraceResponse.php @@ -4,6 +4,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; +use Seed\Commons\DebugVariableValue; class TraceResponse extends JsonSerializableType { @@ -20,10 +21,10 @@ class TraceResponse extends JsonSerializableType public int $lineNumber; /** - * @var mixed $returnValue + * @var ?DebugVariableValue $returnValue */ #[JsonProperty('returnValue')] - public mixed $returnValue; + public ?DebugVariableValue $returnValue; /** * @var ?ExpressionLocation $expressionLocation @@ -48,7 +49,7 @@ class TraceResponse extends JsonSerializableType * submissionId: string, * lineNumber: int, * stack: StackInformation, - * returnValue?: mixed, + * returnValue?: ?DebugVariableValue, * expressionLocation?: ?ExpressionLocation, * stdout?: ?string, * } $values diff --git a/seed/php-model/trace/src/Submission/TraceResponseV2.php b/seed/php-model/trace/src/Submission/TraceResponseV2.php index ceec57218dd..37296f515bb 100644 --- a/seed/php-model/trace/src/Submission/TraceResponseV2.php +++ b/seed/php-model/trace/src/Submission/TraceResponseV2.php @@ -4,6 +4,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; +use Seed\Commons\DebugVariableValue; class TraceResponseV2 extends JsonSerializableType { @@ -26,10 +27,10 @@ class TraceResponseV2 extends JsonSerializableType public TracedFile $file; /** - * @var mixed $returnValue + * @var ?DebugVariableValue $returnValue */ #[JsonProperty('returnValue')] - public mixed $returnValue; + public ?DebugVariableValue $returnValue; /** * @var ?ExpressionLocation $expressionLocation @@ -55,7 +56,7 @@ class TraceResponseV2 extends JsonSerializableType * lineNumber: int, * file: TracedFile, * stack: StackInformation, - * returnValue?: mixed, + * returnValue?: ?DebugVariableValue, * expressionLocation?: ?ExpressionLocation, * stdout?: ?string, * } $values diff --git a/seed/php-model/trace/src/Submission/WorkspaceRunDetails.php b/seed/php-model/trace/src/Submission/WorkspaceRunDetails.php index 04e355bac3a..5ffeb43efa0 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceRunDetails.php +++ b/seed/php-model/trace/src/Submission/WorkspaceRunDetails.php @@ -8,10 +8,10 @@ class WorkspaceRunDetails extends JsonSerializableType { /** - * @var mixed $exceptionV2 + * @var ?ExceptionV2 $exceptionV2 */ #[JsonProperty('exceptionV2')] - public mixed $exceptionV2; + public ?ExceptionV2 $exceptionV2; /** * @var ?ExceptionInfo $exception @@ -28,7 +28,7 @@ class WorkspaceRunDetails extends JsonSerializableType /** * @param array{ * stdout: string, - * exceptionV2?: mixed, + * exceptionV2?: ?ExceptionV2, * exception?: ?ExceptionInfo, * } $values */ diff --git a/seed/php-model/trace/src/Submission/WorkspaceSubmissionState.php b/seed/php-model/trace/src/Submission/WorkspaceSubmissionState.php index de2bb65d30f..4d9c126126b 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceSubmissionState.php +++ b/seed/php-model/trace/src/Submission/WorkspaceSubmissionState.php @@ -8,14 +8,14 @@ class WorkspaceSubmissionState extends JsonSerializableType { /** - * @var mixed $status + * @var WorkspaceSubmissionStatus $status */ #[JsonProperty('status')] - public mixed $status; + public WorkspaceSubmissionStatus $status; /** * @param array{ - * status: mixed, + * status: WorkspaceSubmissionStatus, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatus.php b/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatus.php index 9fe1154be1f..de3e8e2a6a5 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatus.php +++ b/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatus.php @@ -16,9 +16,10 @@ class WorkspaceSubmissionStatus extends JsonSerializableType /** * @var ( * null - * |mixed + * |ErrorInfo * |value-of * |WorkspaceRunDetails + * |mixed * ) $value */ public readonly mixed $value; @@ -28,9 +29,10 @@ class WorkspaceSubmissionStatus extends JsonSerializableType * type: string, * value: ( * null - * |mixed + * |ErrorInfo * |value-of * |WorkspaceRunDetails + * |mixed * ), * } $values */ @@ -53,10 +55,10 @@ public static function stopped(): WorkspaceSubmissionStatus } /** - * @param mixed $errored + * @param ErrorInfo $errored * @return WorkspaceSubmissionStatus */ - public static function errored(mixed $errored): WorkspaceSubmissionStatus + public static function errored(ErrorInfo $errored): WorkspaceSubmissionStatus { return new WorkspaceSubmissionStatus([ 'type' => 'errored', @@ -125,15 +127,15 @@ public function isStopped(): bool */ public function isErrored(): bool { - return is_null($this->value) && $this->type === 'errored'; + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** - * @return mixed + * @return ErrorInfo */ - public function asErrored(): mixed + public function asErrored(): ErrorInfo { - if (!(is_null($this->value) && $this->type === 'errored')) { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -232,7 +234,7 @@ public function jsonSerialize(): array $result['stopped'] = []; break; case 'errored': - $value = $this->value; + $value = $this->asErrored()->jsonSerialize(); $result['errored'] = $value; break; case 'running': @@ -306,7 +308,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['errored'] = $data['errored']; + if (!(is_array($data['errored']))) { + throw new Exception( + "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), + ); + } + $args['errored'] = ErrorInfo::jsonDeserialize($data['errored']); break; case 'running': $args['type'] = 'running'; diff --git a/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdate.php b/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdate.php index 9b3d403a957..e86c1888588 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdate.php +++ b/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdate.php @@ -16,15 +16,15 @@ class WorkspaceSubmissionUpdate extends JsonSerializableType public DateTime $updateTime; /** - * @var mixed $updateInfo + * @var WorkspaceSubmissionUpdateInfo $updateInfo */ #[JsonProperty('updateInfo')] - public mixed $updateInfo; + public WorkspaceSubmissionUpdateInfo $updateInfo; /** * @param array{ * updateTime: DateTime, - * updateInfo: mixed, + * updateInfo: WorkspaceSubmissionUpdateInfo, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdateInfo.php b/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdateInfo.php index 510c4843d8c..1fe9dc50255 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdateInfo.php +++ b/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdateInfo.php @@ -19,6 +19,7 @@ class WorkspaceSubmissionUpdateInfo extends JsonSerializableType * |WorkspaceRunDetails * |null * |WorkspaceTracedUpdate + * |ErrorInfo * |mixed * ) $value */ @@ -32,6 +33,7 @@ class WorkspaceSubmissionUpdateInfo extends JsonSerializableType * |WorkspaceRunDetails * |null * |WorkspaceTracedUpdate + * |ErrorInfo * |mixed * ), * } $values @@ -102,10 +104,10 @@ public static function tracedV2(WorkspaceTracedUpdate $tracedV2): WorkspaceSubmi } /** - * @param mixed $errored + * @param ErrorInfo $errored * @return WorkspaceSubmissionUpdateInfo */ - public static function errored(mixed $errored): WorkspaceSubmissionUpdateInfo + public static function errored(ErrorInfo $errored): WorkspaceSubmissionUpdateInfo { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'errored', @@ -223,15 +225,15 @@ public function asTracedV2(): WorkspaceTracedUpdate */ public function isErrored(): bool { - return is_null($this->value) && $this->type === 'errored'; + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** - * @return mixed + * @return ErrorInfo */ - public function asErrored(): mixed + public function asErrored(): ErrorInfo { - if (!(is_null($this->value) && $this->type === 'errored')) { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . "with value of type " . get_debug_type($this->value), ); @@ -287,7 +289,7 @@ public function jsonSerialize(): array $result = array_merge($value, $result); break; case 'errored': - $value = $this->value; + $value = $this->asErrored()->jsonSerialize(); $result['errored'] = $value; break; case 'finished': @@ -374,7 +376,12 @@ public static function jsonDeserialize(array $data): static ); } - $args['errored'] = $data['errored']; + if (!(is_array($data['errored']))) { + throw new Exception( + "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), + ); + } + $args['errored'] = ErrorInfo::jsonDeserialize($data['errored']); break; case 'finished': $args['type'] = 'finished'; diff --git a/seed/php-model/trace/src/V2/Problem/CreateProblemRequestV2.php b/seed/php-model/trace/src/V2/Problem/CreateProblemRequestV2.php index 51690ea0340..77b28f86219 100644 --- a/seed/php-model/trace/src/V2/Problem/CreateProblemRequestV2.php +++ b/seed/php-model/trace/src/V2/Problem/CreateProblemRequestV2.php @@ -23,10 +23,10 @@ class CreateProblemRequestV2 extends JsonSerializableType public ProblemDescription $problemDescription; /** - * @var mixed $customFiles + * @var CustomFiles $customFiles */ #[JsonProperty('customFiles')] - public mixed $customFiles; + public CustomFiles $customFiles; /** * @var array $customTestCaseTemplates @@ -56,7 +56,7 @@ class CreateProblemRequestV2 extends JsonSerializableType * @param array{ * problemName: string, * problemDescription: ProblemDescription, - * customFiles: mixed, + * customFiles: CustomFiles, * customTestCaseTemplates: array, * testcases: array, * supportedLanguages: array>, diff --git a/seed/php-model/trace/src/V2/Problem/DefaultProvidedFile.php b/seed/php-model/trace/src/V2/Problem/DefaultProvidedFile.php index 4318a65f8f3..38f211d61c7 100644 --- a/seed/php-model/trace/src/V2/Problem/DefaultProvidedFile.php +++ b/seed/php-model/trace/src/V2/Problem/DefaultProvidedFile.php @@ -4,6 +4,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; +use Seed\Commons\VariableType; use Seed\Core\Types\ArrayType; class DefaultProvidedFile extends JsonSerializableType @@ -15,15 +16,15 @@ class DefaultProvidedFile extends JsonSerializableType public FileInfoV2 $file; /** - * @var array $relatedTypes + * @var array $relatedTypes */ - #[JsonProperty('relatedTypes'), ArrayType(['mixed'])] + #[JsonProperty('relatedTypes'), ArrayType([VariableType::class])] public array $relatedTypes; /** * @param array{ * file: FileInfoV2, - * relatedTypes: array, + * relatedTypes: array, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureRequest.php b/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureRequest.php index c99eb662a37..eb07ac0a2a6 100644 --- a/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureRequest.php +++ b/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureRequest.php @@ -8,14 +8,14 @@ class GetFunctionSignatureRequest extends JsonSerializableType { /** - * @var mixed $functionSignature + * @var FunctionSignature $functionSignature */ #[JsonProperty('functionSignature')] - public mixed $functionSignature; + public FunctionSignature $functionSignature; /** * @param array{ - * functionSignature: mixed, + * functionSignature: FunctionSignature, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/Problem/LightweightProblemInfoV2.php b/seed/php-model/trace/src/V2/Problem/LightweightProblemInfoV2.php index f4cff4fa43d..1a21689c0aa 100644 --- a/seed/php-model/trace/src/V2/Problem/LightweightProblemInfoV2.php +++ b/seed/php-model/trace/src/V2/Problem/LightweightProblemInfoV2.php @@ -4,6 +4,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; +use Seed\Commons\VariableType; use Seed\Core\Types\ArrayType; class LightweightProblemInfoV2 extends JsonSerializableType @@ -27,9 +28,9 @@ class LightweightProblemInfoV2 extends JsonSerializableType public int $problemVersion; /** - * @var array $variableTypes + * @var array $variableTypes */ - #[JsonProperty('variableTypes'), ArrayType(['mixed'])] + #[JsonProperty('variableTypes'), ArrayType([VariableType::class])] public array $variableTypes; /** @@ -37,7 +38,7 @@ class LightweightProblemInfoV2 extends JsonSerializableType * problemId: string, * problemName: string, * problemVersion: int, - * variableTypes: array, + * variableTypes: array, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/Problem/NonVoidFunctionSignature.php b/seed/php-model/trace/src/V2/Problem/NonVoidFunctionSignature.php index 7cb36880fe8..83521de8008 100644 --- a/seed/php-model/trace/src/V2/Problem/NonVoidFunctionSignature.php +++ b/seed/php-model/trace/src/V2/Problem/NonVoidFunctionSignature.php @@ -5,6 +5,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; use Seed\Core\Types\ArrayType; +use Seed\Commons\VariableType; class NonVoidFunctionSignature extends JsonSerializableType { @@ -15,15 +16,15 @@ class NonVoidFunctionSignature extends JsonSerializableType public array $parameters; /** - * @var mixed $returnType + * @var VariableType $returnType */ #[JsonProperty('returnType')] - public mixed $returnType; + public VariableType $returnType; /** * @param array{ * parameters: array, - * returnType: mixed, + * returnType: VariableType, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/Problem/Parameter.php b/seed/php-model/trace/src/V2/Problem/Parameter.php index b1c729c77bd..0c16bff0da2 100644 --- a/seed/php-model/trace/src/V2/Problem/Parameter.php +++ b/seed/php-model/trace/src/V2/Problem/Parameter.php @@ -4,6 +4,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; +use Seed\Commons\VariableType; class Parameter extends JsonSerializableType { @@ -20,16 +21,16 @@ class Parameter extends JsonSerializableType public string $name; /** - * @var mixed $variableType + * @var VariableType $variableType */ #[JsonProperty('variableType')] - public mixed $variableType; + public VariableType $variableType; /** * @param array{ * parameterId: string, * name: string, - * variableType: mixed, + * variableType: VariableType, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/Problem/ProblemInfoV2.php b/seed/php-model/trace/src/V2/Problem/ProblemInfoV2.php index 963a9612df0..730fd946317 100644 --- a/seed/php-model/trace/src/V2/Problem/ProblemInfoV2.php +++ b/seed/php-model/trace/src/V2/Problem/ProblemInfoV2.php @@ -41,10 +41,10 @@ class ProblemInfoV2 extends JsonSerializableType public array $supportedLanguages; /** - * @var mixed $customFiles + * @var CustomFiles $customFiles */ #[JsonProperty('customFiles')] - public mixed $customFiles; + public CustomFiles $customFiles; /** * @var GeneratedFiles $generatedFiles @@ -77,7 +77,7 @@ class ProblemInfoV2 extends JsonSerializableType * problemName: string, * problemVersion: int, * supportedLanguages: array>, - * customFiles: mixed, + * customFiles: CustomFiles, * generatedFiles: GeneratedFiles, * customTestCaseTemplates: array, * testcases: array, diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseImplementation.php b/seed/php-model/trace/src/V2/Problem/TestCaseImplementation.php index 156995ee5f7..7b6fb9e5a61 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseImplementation.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseImplementation.php @@ -14,15 +14,15 @@ class TestCaseImplementation extends JsonSerializableType public TestCaseImplementationDescription $description; /** - * @var mixed $function + * @var TestCaseFunction $function */ #[JsonProperty('function')] - public mixed $function; + public TestCaseFunction $function; /** * @param array{ * description: TestCaseImplementationDescription, - * function: mixed, + * function: TestCaseFunction, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescription.php b/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescription.php index 0fd264ccd53..47be75e1b05 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescription.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescription.php @@ -9,14 +9,14 @@ class TestCaseImplementationDescription extends JsonSerializableType { /** - * @var array $boards + * @var array $boards */ - #[JsonProperty('boards'), ArrayType(['mixed'])] + #[JsonProperty('boards'), ArrayType([TestCaseImplementationDescriptionBoard::class])] public array $boards; /** * @param array{ - * boards: array, + * boards: array, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseV2.php b/seed/php-model/trace/src/V2/Problem/TestCaseV2.php index 6ff1d8ca917..aeebf4c6e9b 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseV2.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseV2.php @@ -4,6 +4,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; +use Seed\Commons\VariableValue; use Seed\Core\Types\ArrayType; class TestCaseV2 extends JsonSerializableType @@ -15,15 +16,15 @@ class TestCaseV2 extends JsonSerializableType public TestCaseMetadata $metadata; /** - * @var mixed $implementation + * @var TestCaseImplementationReference $implementation */ #[JsonProperty('implementation')] - public mixed $implementation; + public TestCaseImplementationReference $implementation; /** - * @var array $arguments + * @var array $arguments */ - #[JsonProperty('arguments'), ArrayType(['string' => 'mixed'])] + #[JsonProperty('arguments'), ArrayType(['string' => VariableValue::class])] public array $arguments; /** @@ -35,8 +36,8 @@ class TestCaseV2 extends JsonSerializableType /** * @param array{ * metadata: TestCaseMetadata, - * implementation: mixed, - * arguments: array, + * implementation: TestCaseImplementationReference, + * arguments: array, * expects?: ?TestCaseExpects, * } $values */ diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseWithActualResultImplementation.php b/seed/php-model/trace/src/V2/Problem/TestCaseWithActualResultImplementation.php index 7fe5065497f..97d2d32b9cf 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseWithActualResultImplementation.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseWithActualResultImplementation.php @@ -14,15 +14,15 @@ class TestCaseWithActualResultImplementation extends JsonSerializableType public NonVoidFunctionDefinition $getActualResult; /** - * @var mixed $assertCorrectnessCheck + * @var AssertCorrectnessCheck $assertCorrectnessCheck */ #[JsonProperty('assertCorrectnessCheck')] - public mixed $assertCorrectnessCheck; + public AssertCorrectnessCheck $assertCorrectnessCheck; /** * @param array{ * getActualResult: NonVoidFunctionDefinition, - * assertCorrectnessCheck: mixed, + * assertCorrectnessCheck: AssertCorrectnessCheck, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/Problem/VoidFunctionSignatureThatTakesActualResult.php b/seed/php-model/trace/src/V2/Problem/VoidFunctionSignatureThatTakesActualResult.php index 59173657c14..d62dc03ca1d 100644 --- a/seed/php-model/trace/src/V2/Problem/VoidFunctionSignatureThatTakesActualResult.php +++ b/seed/php-model/trace/src/V2/Problem/VoidFunctionSignatureThatTakesActualResult.php @@ -5,6 +5,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; use Seed\Core\Types\ArrayType; +use Seed\Commons\VariableType; class VoidFunctionSignatureThatTakesActualResult extends JsonSerializableType { @@ -15,15 +16,15 @@ class VoidFunctionSignatureThatTakesActualResult extends JsonSerializableType public array $parameters; /** - * @var mixed $actualResultType + * @var VariableType $actualResultType */ #[JsonProperty('actualResultType')] - public mixed $actualResultType; + public VariableType $actualResultType; /** * @param array{ * parameters: array, - * actualResultType: mixed, + * actualResultType: VariableType, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/V3/Problem/CreateProblemRequestV2.php b/seed/php-model/trace/src/V2/V3/Problem/CreateProblemRequestV2.php index 527d6ce9b15..90f5348b6ac 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/CreateProblemRequestV2.php +++ b/seed/php-model/trace/src/V2/V3/Problem/CreateProblemRequestV2.php @@ -23,10 +23,10 @@ class CreateProblemRequestV2 extends JsonSerializableType public ProblemDescription $problemDescription; /** - * @var mixed $customFiles + * @var CustomFiles $customFiles */ #[JsonProperty('customFiles')] - public mixed $customFiles; + public CustomFiles $customFiles; /** * @var array $customTestCaseTemplates @@ -56,7 +56,7 @@ class CreateProblemRequestV2 extends JsonSerializableType * @param array{ * problemName: string, * problemDescription: ProblemDescription, - * customFiles: mixed, + * customFiles: CustomFiles, * customTestCaseTemplates: array, * testcases: array, * supportedLanguages: array>, diff --git a/seed/php-model/trace/src/V2/V3/Problem/DefaultProvidedFile.php b/seed/php-model/trace/src/V2/V3/Problem/DefaultProvidedFile.php index 969e25af514..ed83b285c56 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/DefaultProvidedFile.php +++ b/seed/php-model/trace/src/V2/V3/Problem/DefaultProvidedFile.php @@ -4,6 +4,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; +use Seed\Commons\VariableType; use Seed\Core\Types\ArrayType; class DefaultProvidedFile extends JsonSerializableType @@ -15,15 +16,15 @@ class DefaultProvidedFile extends JsonSerializableType public FileInfoV2 $file; /** - * @var array $relatedTypes + * @var array $relatedTypes */ - #[JsonProperty('relatedTypes'), ArrayType(['mixed'])] + #[JsonProperty('relatedTypes'), ArrayType([VariableType::class])] public array $relatedTypes; /** * @param array{ * file: FileInfoV2, - * relatedTypes: array, + * relatedTypes: array, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureRequest.php b/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureRequest.php index bff9a59eb9f..9c2f4d2b081 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureRequest.php +++ b/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureRequest.php @@ -8,14 +8,14 @@ class GetFunctionSignatureRequest extends JsonSerializableType { /** - * @var mixed $functionSignature + * @var FunctionSignature $functionSignature */ #[JsonProperty('functionSignature')] - public mixed $functionSignature; + public FunctionSignature $functionSignature; /** * @param array{ - * functionSignature: mixed, + * functionSignature: FunctionSignature, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/V3/Problem/LightweightProblemInfoV2.php b/seed/php-model/trace/src/V2/V3/Problem/LightweightProblemInfoV2.php index 6ea0e6e48da..7e4dd5c4df5 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/LightweightProblemInfoV2.php +++ b/seed/php-model/trace/src/V2/V3/Problem/LightweightProblemInfoV2.php @@ -4,6 +4,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; +use Seed\Commons\VariableType; use Seed\Core\Types\ArrayType; class LightweightProblemInfoV2 extends JsonSerializableType @@ -27,9 +28,9 @@ class LightweightProblemInfoV2 extends JsonSerializableType public int $problemVersion; /** - * @var array $variableTypes + * @var array $variableTypes */ - #[JsonProperty('variableTypes'), ArrayType(['mixed'])] + #[JsonProperty('variableTypes'), ArrayType([VariableType::class])] public array $variableTypes; /** @@ -37,7 +38,7 @@ class LightweightProblemInfoV2 extends JsonSerializableType * problemId: string, * problemName: string, * problemVersion: int, - * variableTypes: array, + * variableTypes: array, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionSignature.php b/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionSignature.php index 7a7bc5f7911..80e3a7010b1 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionSignature.php +++ b/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionSignature.php @@ -5,6 +5,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; use Seed\Core\Types\ArrayType; +use Seed\Commons\VariableType; class NonVoidFunctionSignature extends JsonSerializableType { @@ -15,15 +16,15 @@ class NonVoidFunctionSignature extends JsonSerializableType public array $parameters; /** - * @var mixed $returnType + * @var VariableType $returnType */ #[JsonProperty('returnType')] - public mixed $returnType; + public VariableType $returnType; /** * @param array{ * parameters: array, - * returnType: mixed, + * returnType: VariableType, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/V3/Problem/Parameter.php b/seed/php-model/trace/src/V2/V3/Problem/Parameter.php index 78172d52979..ca76ac3d2c1 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/Parameter.php +++ b/seed/php-model/trace/src/V2/V3/Problem/Parameter.php @@ -4,6 +4,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; +use Seed\Commons\VariableType; class Parameter extends JsonSerializableType { @@ -20,16 +21,16 @@ class Parameter extends JsonSerializableType public string $name; /** - * @var mixed $variableType + * @var VariableType $variableType */ #[JsonProperty('variableType')] - public mixed $variableType; + public VariableType $variableType; /** * @param array{ * parameterId: string, * name: string, - * variableType: mixed, + * variableType: VariableType, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/V3/Problem/ProblemInfoV2.php b/seed/php-model/trace/src/V2/V3/Problem/ProblemInfoV2.php index 88b4be8aa84..693b3ded1e8 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/ProblemInfoV2.php +++ b/seed/php-model/trace/src/V2/V3/Problem/ProblemInfoV2.php @@ -41,10 +41,10 @@ class ProblemInfoV2 extends JsonSerializableType public array $supportedLanguages; /** - * @var mixed $customFiles + * @var CustomFiles $customFiles */ #[JsonProperty('customFiles')] - public mixed $customFiles; + public CustomFiles $customFiles; /** * @var GeneratedFiles $generatedFiles @@ -77,7 +77,7 @@ class ProblemInfoV2 extends JsonSerializableType * problemName: string, * problemVersion: int, * supportedLanguages: array>, - * customFiles: mixed, + * customFiles: CustomFiles, * generatedFiles: GeneratedFiles, * customTestCaseTemplates: array, * testcases: array, diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementation.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementation.php index 928e9aa3d90..ff4fc886130 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementation.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementation.php @@ -14,15 +14,15 @@ class TestCaseImplementation extends JsonSerializableType public TestCaseImplementationDescription $description; /** - * @var mixed $function + * @var TestCaseFunction $function */ #[JsonProperty('function')] - public mixed $function; + public TestCaseFunction $function; /** * @param array{ * description: TestCaseImplementationDescription, - * function: mixed, + * function: TestCaseFunction, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescription.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescription.php index 0025c16b1c4..597dcb8539b 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescription.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescription.php @@ -9,14 +9,14 @@ class TestCaseImplementationDescription extends JsonSerializableType { /** - * @var array $boards + * @var array $boards */ - #[JsonProperty('boards'), ArrayType(['mixed'])] + #[JsonProperty('boards'), ArrayType([TestCaseImplementationDescriptionBoard::class])] public array $boards; /** * @param array{ - * boards: array, + * boards: array, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseV2.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseV2.php index aa4c6e540a1..b8c9c3b7f77 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseV2.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseV2.php @@ -4,6 +4,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; +use Seed\Commons\VariableValue; use Seed\Core\Types\ArrayType; class TestCaseV2 extends JsonSerializableType @@ -15,15 +16,15 @@ class TestCaseV2 extends JsonSerializableType public TestCaseMetadata $metadata; /** - * @var mixed $implementation + * @var TestCaseImplementationReference $implementation */ #[JsonProperty('implementation')] - public mixed $implementation; + public TestCaseImplementationReference $implementation; /** - * @var array $arguments + * @var array $arguments */ - #[JsonProperty('arguments'), ArrayType(['string' => 'mixed'])] + #[JsonProperty('arguments'), ArrayType(['string' => VariableValue::class])] public array $arguments; /** @@ -35,8 +36,8 @@ class TestCaseV2 extends JsonSerializableType /** * @param array{ * metadata: TestCaseMetadata, - * implementation: mixed, - * arguments: array, + * implementation: TestCaseImplementationReference, + * arguments: array, * expects?: ?TestCaseExpects, * } $values */ diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseWithActualResultImplementation.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseWithActualResultImplementation.php index eeb8c74f622..ecc0d2a745d 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseWithActualResultImplementation.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseWithActualResultImplementation.php @@ -14,15 +14,15 @@ class TestCaseWithActualResultImplementation extends JsonSerializableType public NonVoidFunctionDefinition $getActualResult; /** - * @var mixed $assertCorrectnessCheck + * @var AssertCorrectnessCheck $assertCorrectnessCheck */ #[JsonProperty('assertCorrectnessCheck')] - public mixed $assertCorrectnessCheck; + public AssertCorrectnessCheck $assertCorrectnessCheck; /** * @param array{ * getActualResult: NonVoidFunctionDefinition, - * assertCorrectnessCheck: mixed, + * assertCorrectnessCheck: AssertCorrectnessCheck, * } $values */ public function __construct( diff --git a/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignatureThatTakesActualResult.php b/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignatureThatTakesActualResult.php index ca1a22f9f1f..e3a930eafd4 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignatureThatTakesActualResult.php +++ b/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignatureThatTakesActualResult.php @@ -5,6 +5,7 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; use Seed\Core\Types\ArrayType; +use Seed\Commons\VariableType; class VoidFunctionSignatureThatTakesActualResult extends JsonSerializableType { @@ -15,15 +16,15 @@ class VoidFunctionSignatureThatTakesActualResult extends JsonSerializableType public array $parameters; /** - * @var mixed $actualResultType + * @var VariableType $actualResultType */ #[JsonProperty('actualResultType')] - public mixed $actualResultType; + public VariableType $actualResultType; /** * @param array{ * parameters: array, - * actualResultType: mixed, + * actualResultType: VariableType, * } $values */ public function __construct( diff --git a/seed/php-model/unions/.mock/definition/bigunion.yml b/seed/php-model/unions/.mock/definition/bigunion.yml index fdc5bde1dde..6302d0fc309 100644 --- a/seed/php-model/unions/.mock/definition/bigunion.yml +++ b/seed/php-model/unions/.mock/definition/bigunion.yml @@ -168,3 +168,9 @@ service: request: BigUnion response: boolean + update-many: + path: /many + method: PATCH + request: list + response: map + diff --git a/seed/php-sdk/accept-header/src/Core/Client/RawClient.php b/seed/php-sdk/accept-header/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/accept-header/src/Core/Client/RawClient.php +++ b/seed/php-sdk/accept-header/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/alias-extends/src/Core/Client/RawClient.php b/seed/php-sdk/alias-extends/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/alias-extends/src/Core/Client/RawClient.php +++ b/seed/php-sdk/alias-extends/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/alias/composer-json/src/Core/Client/RawClient.php b/seed/php-sdk/alias/composer-json/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/alias/composer-json/src/Core/Client/RawClient.php +++ b/seed/php-sdk/alias/composer-json/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/alias/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/alias/no-custom-config/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/alias/no-custom-config/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/any-auth/src/Core/Client/RawClient.php b/seed/php-sdk/any-auth/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/any-auth/src/Core/Client/RawClient.php +++ b/seed/php-sdk/any-auth/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/api-wide-base-path/src/Core/Client/RawClient.php b/seed/php-sdk/api-wide-base-path/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/api-wide-base-path/src/Core/Client/RawClient.php +++ b/seed/php-sdk/api-wide-base-path/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/audiences/src/Core/Client/RawClient.php b/seed/php-sdk/audiences/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/audiences/src/Core/Client/RawClient.php +++ b/seed/php-sdk/audiences/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/auth-environment-variables/src/Core/Client/RawClient.php b/seed/php-sdk/auth-environment-variables/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/auth-environment-variables/src/Core/Client/RawClient.php +++ b/seed/php-sdk/auth-environment-variables/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RawClient.php b/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RawClient.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/basic-auth/src/Core/Client/RawClient.php b/seed/php-sdk/basic-auth/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/basic-auth/src/Core/Client/RawClient.php +++ b/seed/php-sdk/basic-auth/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/bearer-token-environment-variable/src/Core/Client/RawClient.php b/seed/php-sdk/bearer-token-environment-variable/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/bearer-token-environment-variable/src/Core/Client/RawClient.php +++ b/seed/php-sdk/bearer-token-environment-variable/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/bytes/src/Core/Client/RawClient.php b/seed/php-sdk/bytes/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/bytes/src/Core/Client/RawClient.php +++ b/seed/php-sdk/bytes/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/circular-references-advanced/src/Core/Client/RawClient.php b/seed/php-sdk/circular-references-advanced/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/circular-references-advanced/src/Core/Client/RawClient.php +++ b/seed/php-sdk/circular-references-advanced/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/circular-references/src/Core/Client/RawClient.php b/seed/php-sdk/circular-references/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/circular-references/src/Core/Client/RawClient.php +++ b/seed/php-sdk/circular-references/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/cross-package-type-names/src/Core/Client/RawClient.php b/seed/php-sdk/cross-package-type-names/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/cross-package-type-names/src/Core/Client/RawClient.php +++ b/seed/php-sdk/cross-package-type-names/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/custom-auth/src/Core/Client/RawClient.php b/seed/php-sdk/custom-auth/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/custom-auth/src/Core/Client/RawClient.php +++ b/seed/php-sdk/custom-auth/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/enum/src/Core/Client/RawClient.php b/seed/php-sdk/enum/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/enum/src/Core/Client/RawClient.php +++ b/seed/php-sdk/enum/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/error-property/src/Core/Client/RawClient.php b/seed/php-sdk/error-property/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/error-property/src/Core/Client/RawClient.php +++ b/seed/php-sdk/error-property/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/examples/src/Core/Client/RawClient.php b/seed/php-sdk/examples/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/examples/src/Core/Client/RawClient.php +++ b/seed/php-sdk/examples/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/exhaustive/src/Core/Client/RawClient.php b/seed/php-sdk/exhaustive/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/exhaustive/src/Core/Client/RawClient.php +++ b/seed/php-sdk/exhaustive/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/extends/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/extends/no-custom-config/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/extends/no-custom-config/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/extends/private/src/Core/Client/RawClient.php b/seed/php-sdk/extends/private/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/extends/private/src/Core/Client/RawClient.php +++ b/seed/php-sdk/extends/private/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/extra-properties/src/Core/Client/RawClient.php b/seed/php-sdk/extra-properties/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/extra-properties/src/Core/Client/RawClient.php +++ b/seed/php-sdk/extra-properties/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/file-download/src/Core/Client/RawClient.php b/seed/php-sdk/file-download/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/file-download/src/Core/Client/RawClient.php +++ b/seed/php-sdk/file-download/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/file-upload/src/Core/Client/RawClient.php b/seed/php-sdk/file-upload/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/file-upload/src/Core/Client/RawClient.php +++ b/seed/php-sdk/file-upload/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/folders/src/Core/Client/RawClient.php b/seed/php-sdk/folders/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/folders/src/Core/Client/RawClient.php +++ b/seed/php-sdk/folders/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/idempotency-headers/src/Core/Client/RawClient.php b/seed/php-sdk/idempotency-headers/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/idempotency-headers/src/Core/Client/RawClient.php +++ b/seed/php-sdk/idempotency-headers/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/imdb/clientName/src/Core/Client/RawClient.php b/seed/php-sdk/imdb/clientName/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/imdb/clientName/src/Core/Client/RawClient.php +++ b/seed/php-sdk/imdb/clientName/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/imdb/namespace/src/Core/Client/RawClient.php b/seed/php-sdk/imdb/namespace/src/Core/Client/RawClient.php index 6962f0efe38..b58d7981f17 100644 --- a/seed/php-sdk/imdb/namespace/src/Core/Client/RawClient.php +++ b/seed/php-sdk/imdb/namespace/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/imdb/packageName/src/Core/Client/RawClient.php b/seed/php-sdk/imdb/packageName/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/imdb/packageName/src/Core/Client/RawClient.php +++ b/seed/php-sdk/imdb/packageName/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/imdb/private/src/Core/Client/RawClient.php b/seed/php-sdk/imdb/private/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/imdb/private/src/Core/Client/RawClient.php +++ b/seed/php-sdk/imdb/private/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/license/src/Core/Client/RawClient.php b/seed/php-sdk/license/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/license/src/Core/Client/RawClient.php +++ b/seed/php-sdk/license/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/literal/src/Core/Client/RawClient.php b/seed/php-sdk/literal/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/literal/src/Core/Client/RawClient.php +++ b/seed/php-sdk/literal/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/mixed-case/src/Core/Client/RawClient.php b/seed/php-sdk/mixed-case/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/mixed-case/src/Core/Client/RawClient.php +++ b/seed/php-sdk/mixed-case/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/mixed-file-directory/src/Core/Client/RawClient.php b/seed/php-sdk/mixed-file-directory/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/mixed-file-directory/src/Core/Client/RawClient.php +++ b/seed/php-sdk/mixed-file-directory/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/multi-line-docs/src/Core/Client/RawClient.php b/seed/php-sdk/multi-line-docs/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/multi-line-docs/src/Core/Client/RawClient.php +++ b/seed/php-sdk/multi-line-docs/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/no-environment/src/Core/Client/RawClient.php b/seed/php-sdk/no-environment/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/no-environment/src/Core/Client/RawClient.php +++ b/seed/php-sdk/no-environment/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/nullable/src/Core/Client/RawClient.php b/seed/php-sdk/nullable/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/nullable/src/Core/Client/RawClient.php +++ b/seed/php-sdk/nullable/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RawClient.php b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RawClient.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RawClient.php b/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RawClient.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RawClient.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RawClient.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RawClient.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RawClient.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/oauth-client-credentials/src/Core/Client/RawClient.php b/seed/php-sdk/oauth-client-credentials/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/oauth-client-credentials/src/Core/Client/RawClient.php +++ b/seed/php-sdk/oauth-client-credentials/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/object/src/Core/Client/RawClient.php b/seed/php-sdk/object/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/object/src/Core/Client/RawClient.php +++ b/seed/php-sdk/object/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/objects-with-imports/src/Core/Client/RawClient.php b/seed/php-sdk/objects-with-imports/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/objects-with-imports/src/Core/Client/RawClient.php +++ b/seed/php-sdk/objects-with-imports/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/optional/src/Core/Client/RawClient.php b/seed/php-sdk/optional/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/optional/src/Core/Client/RawClient.php +++ b/seed/php-sdk/optional/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/package-yml/private/src/Core/Client/RawClient.php b/seed/php-sdk/package-yml/private/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/package-yml/private/src/Core/Client/RawClient.php +++ b/seed/php-sdk/package-yml/private/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Client/RawClient.php b/seed/php-sdk/pagination/property-accessors/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Client/RawClient.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RawClient.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RawClient.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RawClient.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RawClient.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/plain-text/src/Core/Client/RawClient.php b/seed/php-sdk/plain-text/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/plain-text/src/Core/Client/RawClient.php +++ b/seed/php-sdk/plain-text/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/query-parameters/private/src/Core/Client/RawClient.php b/seed/php-sdk/query-parameters/private/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/query-parameters/private/src/Core/Client/RawClient.php +++ b/seed/php-sdk/query-parameters/private/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/reserved-keywords/src/Core/Client/RawClient.php b/seed/php-sdk/reserved-keywords/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/reserved-keywords/src/Core/Client/RawClient.php +++ b/seed/php-sdk/reserved-keywords/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/response-property/src/Core/Client/RawClient.php b/seed/php-sdk/response-property/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/response-property/src/Core/Client/RawClient.php +++ b/seed/php-sdk/response-property/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/server-sent-event-examples/src/Core/Client/RawClient.php b/seed/php-sdk/server-sent-event-examples/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Core/Client/RawClient.php +++ b/seed/php-sdk/server-sent-event-examples/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/server-sent-events/src/Core/Client/RawClient.php b/seed/php-sdk/server-sent-events/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/server-sent-events/src/Core/Client/RawClient.php +++ b/seed/php-sdk/server-sent-events/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/simple-fhir/src/Core/Client/RawClient.php b/seed/php-sdk/simple-fhir/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/simple-fhir/src/Core/Client/RawClient.php +++ b/seed/php-sdk/simple-fhir/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/single-url-environment-default/src/Core/Client/RawClient.php b/seed/php-sdk/single-url-environment-default/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/single-url-environment-default/src/Core/Client/RawClient.php +++ b/seed/php-sdk/single-url-environment-default/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RawClient.php b/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RawClient.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/streaming-parameter/src/Core/Client/RawClient.php b/seed/php-sdk/streaming-parameter/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/streaming-parameter/src/Core/Client/RawClient.php +++ b/seed/php-sdk/streaming-parameter/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/streaming/src/Core/Client/RawClient.php b/seed/php-sdk/streaming/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/streaming/src/Core/Client/RawClient.php +++ b/seed/php-sdk/streaming/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/trace/src/Core/Client/RawClient.php b/seed/php-sdk/trace/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/trace/src/Core/Client/RawClient.php +++ b/seed/php-sdk/trace/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/undiscriminated-unions/src/Core/Client/RawClient.php b/seed/php-sdk/undiscriminated-unions/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Core/Client/RawClient.php +++ b/seed/php-sdk/undiscriminated-unions/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/unions/src/Core/Client/RawClient.php b/seed/php-sdk/unions/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/unions/src/Core/Client/RawClient.php +++ b/seed/php-sdk/unions/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/unknown/src/Core/Client/RawClient.php b/seed/php-sdk/unknown/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/unknown/src/Core/Client/RawClient.php +++ b/seed/php-sdk/unknown/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/validation/src/Core/Client/RawClient.php b/seed/php-sdk/validation/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/validation/src/Core/Client/RawClient.php +++ b/seed/php-sdk/validation/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/variables/src/Core/Client/RawClient.php b/seed/php-sdk/variables/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/variables/src/Core/Client/RawClient.php +++ b/seed/php-sdk/variables/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/version-no-default/src/Core/Client/RawClient.php b/seed/php-sdk/version-no-default/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/version-no-default/src/Core/Client/RawClient.php +++ b/seed/php-sdk/version-no-default/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/version/src/Core/Client/RawClient.php b/seed/php-sdk/version/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/version/src/Core/Client/RawClient.php +++ b/seed/php-sdk/version/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } } diff --git a/seed/php-sdk/websocket/src/Core/Client/RawClient.php b/seed/php-sdk/websocket/src/Core/Client/RawClient.php index b9458ecd869..40d0c7d3f13 100644 --- a/seed/php-sdk/websocket/src/Core/Client/RawClient.php +++ b/seed/php-sdk/websocket/src/Core/Client/RawClient.php @@ -67,7 +67,7 @@ private function createDefaultClient(): Client */ public function sendRequest( BaseApiRequest $request, - ?array $options = null, + ?array $options = null, ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); @@ -104,7 +104,7 @@ private function toGuzzleOptions(array $options): array */ private function buildRequest( BaseApiRequest $request, - array $options + array $options ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); @@ -126,7 +126,7 @@ private function buildRequest( */ private function encodeHeaders( BaseApiRequest $request, - array $options, + array $options, ): array { return match (get_class($request)) { JsonApiRequest::class => array_merge( @@ -153,7 +153,7 @@ private function encodeHeaders( */ private function encodeRequestBody( BaseApiRequest $request, - array $options, + array $options, ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( @@ -181,15 +181,24 @@ private function buildJsonBody( array $options, ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; + if (is_array($body) && (empty($body) || self::isSequential($body))) { + return array_merge($body, $overrideProperties); + } + if ($body instanceof JsonSerializable) { - $serialized = $body->jsonSerialize(); - return is_array($serialized) - ? array_merge($serialized, $overrideProperties) - : $serialized; + $result = $body->jsonSerialize(); + } else { + $result = $body; } - return is_array($body) - ? array_merge($body, $overrideProperties) - : $body; + if (is_array($result)) { + $result = array_merge($result, $overrideProperties); + if (empty($result)) { + // force to be serialized as {} instead of [] + return (object)($result); + } + } + + return $result; } /** @@ -201,7 +210,7 @@ private function buildJsonBody( */ private function buildUrl( BaseApiRequest $request, - array $options, + array $options, ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); @@ -250,4 +259,24 @@ private function encodeQueryValue(mixed $value): string // Unreachable, but included for a best effort. return urlencode(strval(json_encode($value))); } + + /** + * Check if an array is sequential, not associative. + * @param mixed[] $arr + * @return bool + */ + private static function isSequential(array $arr): bool + { + if (empty($arr)) { + return false; + } + $length = count($arr); + $keys = array_keys($arr); + for ($i = 0; $i < $length; $i++) { + if ($keys[$i] !== $i) { + return false; + } + } + return true; + } }