diff --git a/modules/openapi-generator/src/main/resources/php-symfony/Controller.mustache b/modules/openapi-generator/src/main/resources/php-symfony/Controller.mustache index 1c3288d813b..4f6da923a6a 100644 --- a/modules/openapi-generator/src/main/resources/php-symfony/Controller.mustache +++ b/modules/openapi-generator/src/main/resources/php-symfony/Controller.mustache @@ -180,34 +180,56 @@ class Controller extends AbstractController */ protected function getOutputFormat(string $accept, array $produced): ?string { - // Figure out what the client accepts - $accept = preg_split("/[\s,]+/", $accept); + // First get the list of formats accepted by the client by weight. + // eg: text/html,*/*; q=0.7, text/*;q=0.8,text/plain;format=fixed;q=0.6 is turned to + // [ + // text/html => 1, // because when no weight is present then the default value is 1. See https://httpwg.org/specs/rfc9110.html#quality.values . + // */* => 0.7, + // text/* => 0.8, + // text/plain => 0.6, + // ] + // So we can subsequently order that list by descending weight (because 1 is the most prefered value, 0.001 is the least preferred) + $weightedFormats = array(); + foreach (explode(",", str_replace(' ', '', $accept)) as $accept) { + $exploded = explode(';', $accept); - // Remove q-factor weighting. E.g. "application/json;q=0.8" becomes "application/json" - $accept = array_map(function ($type) {return explode(';', $type)[0];}, $accept); - - if (in_array('*/*', $accept) || in_array('application/*', $accept)) { - // Prefer JSON if the client has no preference - if (in_array('application/json', $produced)) { - return 'application/json'; + // If no weight is present then the default value is 1 (see https://httpwg.org/specs/rfc9110.html#quality.values ) + if (count($exploded) === 1) { + $weight = 1.0; + } else { + $lastItem = end($exploded); + if (str_starts_with($lastItem, "q=")) { + $weight = (float) str_replace("q=", "", $lastItem); + } else { + $weight = 1.0; + } } - if (in_array('application/xml', $produced)) { - return 'application/xml'; + $weightedFormats[$exploded[0]] = $weight; + } + arsort($weightedFormats); + + // Now return the first produced format that matches + foreach (array_keys($weightedFormats) as $acceptedFormat) { + $acceptedFormatParts = explode('/', $acceptedFormat); + if (count($acceptedFormatParts) != 2) { + // badly formatted header sent by the client. Let's continue (instead of crashing) + continue; + } + $acceptedFormatType = $acceptedFormatParts[0]; + $acceptedFormatSubtype = $acceptedFormatParts[1]; + + foreach ($produced as $producedFormat) { + if ($acceptedFormat === $producedFormat) { + return $producedFormat; + } + if ($acceptedFormatSubtype === '*' && $acceptedFormatType === explode("/", $producedFormat)[0]) { + return $producedFormat; + } + if ($acceptedFormat === "*/*") { + return $producedFormat; + } } } - - if (in_array('application/json', $accept) && in_array('application/json', $produced)) { - return 'application/json'; - } - - if (in_array('application/xml', $accept) && in_array('application/xml', $produced)) { - return 'application/xml'; - } - - if (in_array('*/*', $accept)) { - return $produced[0]; - } - // If we reach this point, we don't have a common ground between server and client return null; } diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/Controller.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/Controller.php index 7d2600dbb6e..d232f8e1593 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/Controller.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/Controller.php @@ -190,34 +190,56 @@ class Controller extends AbstractController */ protected function getOutputFormat(string $accept, array $produced): ?string { - // Figure out what the client accepts - $accept = preg_split("/[\s,]+/", $accept); + // First get the list of formats accepted by the client by weight. + // eg: text/html,*/*; q=0.7, text/*;q=0.8,text/plain;format=fixed;q=0.6 is turned to + // [ + // text/html => 1, // because when no weight is present then the default value is 1. See https://httpwg.org/specs/rfc9110.html#quality.values . + // */* => 0.7, + // text/* => 0.8, + // text/plain => 0.6, + // ] + // So we can subsequently order that list by descending weight (because 1 is the most prefered value, 0.001 is the least preferred) + $weightedFormats = array(); + foreach (explode(",", str_replace(' ', '', $accept)) as $accept) { + $exploded = explode(';', $accept); - // Remove q-factor weighting. E.g. "application/json;q=0.8" becomes "application/json" - $accept = array_map(function ($type) {return explode(';', $type)[0];}, $accept); - - if (in_array('*/*', $accept) || in_array('application/*', $accept)) { - // Prefer JSON if the client has no preference - if (in_array('application/json', $produced)) { - return 'application/json'; + // If no weight is present then the default value is 1 (see https://httpwg.org/specs/rfc9110.html#quality.values ) + if (count($exploded) === 1) { + $weight = 1.0; + } else { + $lastItem = end($exploded); + if (str_starts_with($lastItem, "q=")) { + $weight = (float) str_replace("q=", "", $lastItem); + } else { + $weight = 1.0; + } } - if (in_array('application/xml', $produced)) { - return 'application/xml'; + $weightedFormats[$exploded[0]] = $weight; + } + arsort($weightedFormats); + + // Now return the first produced format that matches + foreach (array_keys($weightedFormats) as $acceptedFormat) { + $acceptedFormatParts = explode('/', $acceptedFormat); + if (count($acceptedFormatParts) != 2) { + // badly formatted header sent by the client. Let's continue (instead of crashing) + continue; + } + $acceptedFormatType = $acceptedFormatParts[0]; + $acceptedFormatSubtype = $acceptedFormatParts[1]; + + foreach ($produced as $producedFormat) { + if ($acceptedFormat === $producedFormat) { + return $producedFormat; + } + if ($acceptedFormatSubtype === '*' && $acceptedFormatType === explode("/", $producedFormat)[0]) { + return $producedFormat; + } + if ($acceptedFormat === "*/*") { + return $producedFormat; + } } } - - if (in_array('application/json', $accept) && in_array('application/json', $produced)) { - return 'application/json'; - } - - if (in_array('application/xml', $accept) && in_array('application/xml', $produced)) { - return 'application/xml'; - } - - if (in_array('*/*', $accept)) { - return $produced[0]; - } - // If we reach this point, we don't have a common ground between server and client return null; }