Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [11.2.3] - 2026-09-28
### Fixed
- Generated clients can no longer resolve dependency versions that raise PHP 8.4+ deprecations: the generated `composer.json` now requires `docler-labs/api-client-exception` `^1.0 || ^2.3.1` (was `^1.0 || ^2.0`) and, for the Guzzle HTTP message implementation, `guzzlehttp/psr7` `^1.6 || ^2.7` (was `^1.6 || ^2.6`). The 1.x lines stay allowed, so every `CLIENT_PHP_VERSION` target keeps resolving; on PHP 7.4+ Composer now picks only releases that run deprecation-free on PHP 8.5

### Changed
- Updated `docler-labs/api-client-exception` to 2.3.1 and `guzzlehttp/psr7` to 2.13.1 in the `example` and `example3_1` lockfiles, so the acceptance tests also run deprecation-free on PHP 8.5
- Updated `composer/composer` to 2.2.30 (development dependency)

Generated code is unchanged; only the generated `composer.json` constraints differ.

## [11.2.2] - 2026-08-07
### Security
- Updated `guzzlehttp/guzzle` to 7.15.2, resolving CVE-2026-69246 (a noncanonical host can bypass host-based checks) along with the earlier 7.12.x–7.15.x advisories
Expand Down
4 changes: 2 additions & 2 deletions example/PetStoreClient/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
},
"require": {
"php": "^7.4 || ^8.0",
"docler-labs/api-client-exception": "^1.0 || ^2.0",
"docler-labs/api-client-exception": "^1.0 || ^2.3.1",
"ext-dom": "*",
"ext-json": "*",
"guzzlehttp/psr7": "^1.6 || ^2.6",
"guzzlehttp/psr7": "^1.6 || ^2.7",
"pimple/pimple": "^3.5",
"psr/container": "^1.0 || ^2.0",
"psr/http-client": "^1.0"
Expand Down
29 changes: 25 additions & 4 deletions example/PetStoreClient/src/Serializer/BodySerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
use InvalidArgumentException;
use OpenApi\PetStoreClient\Request\RequestInterface;
use OpenApi\PetStoreClient\Serializer\ContentType\ContentTypeSerializerInterface;
use OpenApi\PetStoreClient\Serializer\ContentType\JsonContentTypeSerializer;
use Psr\Http\Message\ResponseInterface;
use Throwable;

class BodySerializer
{
private const JSON_SUFFIX = '+json';

/** @var ContentTypeSerializerInterface[] */
private $contentTypeSerializers = [];

Expand Down Expand Up @@ -66,11 +69,29 @@ public function unserializeResponse(ResponseInterface $response): array

private function getContentTypeSerializer(string $contentType): ContentTypeSerializerInterface
{
$contentType = strtolower(trim(explode(';', $contentType)[0]));
if (!isset($this->contentTypeSerializers[$contentType])) {
throw new InvalidArgumentException(sprintf('Serializer for `%s` is not found. Supported: %s', $contentType, json_encode(array_keys($this->contentTypeSerializers))));
$normalizedContentType = $this->normalizeContentType($contentType);
if (isset($this->contentTypeSerializers[$normalizedContentType])) {
return $this->contentTypeSerializers[$normalizedContentType];
}
// RFC 6839: +json suffix indicates JSON-based format, fall back to JSON serializer
if ($this->isJsonBasedContentType($normalizedContentType)) {
return $this->contentTypeSerializers[JsonContentTypeSerializer::MIME_TYPE];
}
throw new InvalidArgumentException(sprintf('Serializer for `%s` is not found. Supported: %s', $normalizedContentType, json_encode(array_keys($this->contentTypeSerializers))));
}

private function normalizeContentType(string $contentType): string
{
return strtolower(trim(explode(';', $contentType)[0]));
}

return $this->contentTypeSerializers[$contentType];
private function isJsonBasedContentType(string $normalizedContentType): bool
{
return $this->endsWith($normalizedContentType, self::JSON_SUFFIX) && isset($this->contentTypeSerializers[JsonContentTypeSerializer::MIME_TYPE]);
}

private function endsWith(string $haystack, string $needle): bool
{
return $needle === '' || substr($haystack, -strlen($needle)) === $needle;
}
}
89 changes: 73 additions & 16 deletions example/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions example3_1/PetStoreClient/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
},
"require": {
"php": "^7.4 || ^8.0",
"docler-labs/api-client-exception": "^1.0 || ^2.0",
"docler-labs/api-client-exception": "^1.0 || ^2.3.1",
"ext-dom": "*",
"ext-json": "*",
"guzzlehttp/psr7": "^1.6 || ^2.6",
"guzzlehttp/psr7": "^1.6 || ^2.7",
"pimple/pimple": "^3.5",
"psr/container": "^1.0 || ^2.0",
"psr/http-client": "^1.0"
Expand Down
29 changes: 25 additions & 4 deletions example3_1/PetStoreClient/src/Serializer/BodySerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
use InvalidArgumentException;
use OpenApi\PetStoreClient\Request\RequestInterface;
use OpenApi\PetStoreClient\Serializer\ContentType\ContentTypeSerializerInterface;
use OpenApi\PetStoreClient\Serializer\ContentType\JsonContentTypeSerializer;
use Psr\Http\Message\ResponseInterface;
use Throwable;

class BodySerializer
{
private const JSON_SUFFIX = '+json';

/** @var ContentTypeSerializerInterface[] */
private $contentTypeSerializers = [];

Expand Down Expand Up @@ -66,11 +69,29 @@ public function unserializeResponse(ResponseInterface $response): array

private function getContentTypeSerializer(string $contentType): ContentTypeSerializerInterface
{
$contentType = strtolower(trim(explode(';', $contentType)[0]));
if (!isset($this->contentTypeSerializers[$contentType])) {
throw new InvalidArgumentException(sprintf('Serializer for `%s` is not found. Supported: %s', $contentType, json_encode(array_keys($this->contentTypeSerializers))));
$normalizedContentType = $this->normalizeContentType($contentType);
if (isset($this->contentTypeSerializers[$normalizedContentType])) {
return $this->contentTypeSerializers[$normalizedContentType];
}
// RFC 6839: +json suffix indicates JSON-based format, fall back to JSON serializer
if ($this->isJsonBasedContentType($normalizedContentType)) {
return $this->contentTypeSerializers[JsonContentTypeSerializer::MIME_TYPE];
}
throw new InvalidArgumentException(sprintf('Serializer for `%s` is not found. Supported: %s', $normalizedContentType, json_encode(array_keys($this->contentTypeSerializers))));
}

private function normalizeContentType(string $contentType): string
{
return strtolower(trim(explode(';', $contentType)[0]));
}

return $this->contentTypeSerializers[$contentType];
private function isJsonBasedContentType(string $normalizedContentType): bool
{
return $this->endsWith($normalizedContentType, self::JSON_SUFFIX) && isset($this->contentTypeSerializers[JsonContentTypeSerializer::MIME_TYPE]);
}

private function endsWith(string $haystack, string $needle): bool
{
return $needle === '' || substr($haystack, -strlen($needle)) === $needle;
}
}
Loading
Loading