Skip to content

Commit f0e21ae

Browse files
committed
feat(php): allow to pass raw boolean to api
This allows users to use APIs that require booleans in query params not to be cast to int, e.g. `&foo=true`. Currently, `true` is cast to `1` so it's passed as `&foo=1`. That might not be supported by the target API. The fix contains copy-pasted function from guzzlehttp/psr7 `Query::build()` with minor tweaks.
1 parent 8faa77c commit f0e21ae

File tree

15 files changed

+524
-187
lines changed

15 files changed

+524
-187
lines changed

modules/openapi-generator/src/main/resources/php-nextgen/ObjectSerializer.mustache

+58-17
Original file line numberDiff line numberDiff line change
@@ -529,22 +529,63 @@ class ObjectSerializer
529529
}
530530

531531
/**
532-
* Native `http_build_query` wrapper.
533-
* @see https://www.php.net/manual/en/function.http-build-query
534-
*
535-
* @param array|object $data May be an array or object containing properties.
536-
* @param string $numeric_prefix If numeric indices are used in the base array and this parameter is provided, it will be prepended to the numeric index for elements in the base array only.
537-
* @param string|null $arg_separator arg_separator.output is used to separate arguments but may be overridden by specifying this parameter.
538-
* @param int $encoding_type Encoding type. By default, PHP_QUERY_RFC1738.
539-
*
540-
* @return string
541-
*/
542-
public static function buildQuery(
543-
array|object $data,
544-
string $numeric_prefix = '',
545-
?string $arg_separator = null,
546-
int $encoding_type = \PHP_QUERY_RFC3986
547-
): string {
548-
return \GuzzleHttp\Psr7\Query::build($data, $encoding_type);
532+
* Build a query string from an array of key value pairs.
533+
*
534+
* This function can use the return value of `parse()` to build a query
535+
* string. This function does not modify the provided keys when an array is
536+
* encountered (like `http_build_query()` would).
537+
*
538+
* @param array $params Query string parameters.
539+
* @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
540+
* to encode using RFC3986, or PHP_QUERY_RFC1738
541+
* to encode using RFC1738.
542+
* @param bool $treatBooleansAsInts When `true` values are cast to int (e.g. ['foo' => false] gives `foo=0`).
543+
* When `false` values are cast to strings (e.g. ['foo' => false] gives `foo=false`).
544+
*/
545+
public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBooleansAsInts = true): string
546+
{
547+
if (!$params) {
548+
return '';
549+
}
550+
551+
if ($encoding === false) {
552+
$encoder = function (string $str): string {
553+
return $str;
554+
};
555+
} elseif ($encoding === PHP_QUERY_RFC3986) {
556+
$encoder = 'rawurlencode';
557+
} elseif ($encoding === PHP_QUERY_RFC1738) {
558+
$encoder = 'urlencode';
559+
} else {
560+
throw new \InvalidArgumentException('Invalid type');
561+
}
562+
563+
$castBool = $treatBooleansAsInts
564+
? function ($v) { return (int) $v; }
565+
: function ($v) { return $v ? 'true' : 'false'; };
566+
567+
$qs = '';
568+
foreach ($params as $k => $v) {
569+
$k = $encoder((string) $k);
570+
if (!is_array($v)) {
571+
$qs .= $k;
572+
$v = is_bool($v) ? $castBool($v) : $v;
573+
if ($v !== null) {
574+
$qs .= '='.$encoder((string) $v);
575+
}
576+
$qs .= '&';
577+
} else {
578+
foreach ($v as $vv) {
579+
$qs .= $k;
580+
$vv = is_bool($vv) ? $castBool($vv) : $vv;
581+
if ($vv !== null) {
582+
$qs .= '='.$encoder((string) $vv);
583+
}
584+
$qs .= '&';
585+
}
586+
}
587+
}
588+
589+
return $qs ? (string) substr($qs, 0, -1) : '';
549590
}
550591
}

modules/openapi-generator/src/main/resources/php/ObjectSerializer.mustache

+61-17
Original file line numberDiff line numberDiff line change
@@ -536,22 +536,66 @@ class ObjectSerializer
536536
}
537537

538538
/**
539-
* Native `http_build_query` wrapper.
540-
* @see https://www.php.net/manual/en/function.http-build-query
541-
*
542-
* @param array|object $data May be an array or object containing properties.
543-
* @param string $numeric_prefix If numeric indices are used in the base array and this parameter is provided, it will be prepended to the numeric index for elements in the base array only.
544-
* @param string|null $arg_separator arg_separator.output is used to separate arguments but may be overridden by specifying this parameter.
545-
* @param int $encoding_type Encoding type. By default, PHP_QUERY_RFC1738.
546-
*
547-
* @return string
548-
*/
549-
public static function buildQuery(
550-
$data,
551-
string $numeric_prefix = '',
552-
?string $arg_separator = null,
553-
int $encoding_type = \PHP_QUERY_RFC3986
554-
): string {
555-
return \GuzzleHttp\Psr7\Query::build($data, $encoding_type);
539+
* Build a query string from an array of key value pairs.
540+
*
541+
* This function can use the return value of `parse()` to build a query
542+
* string. This function does not modify the provided keys when an array is
543+
* encountered (like `http_build_query()` would).
544+
*
545+
* The function is copied from https://github.com/guzzle/psr7/blob/a243f80a1ca7fe8ceed4deee17f12c1930efe662/src/Query.php#L59-L112
546+
* with a modification which is described in https://github.com/guzzle/psr7/pull/603
547+
*
548+
* @param array $params Query string parameters.
549+
* @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
550+
* to encode using RFC3986, or PHP_QUERY_RFC1738
551+
* to encode using RFC1738.
552+
* @param bool $treatBooleansAsInts When `true` values are cast to int (e.g. ['foo' => false] gives `foo=0`).
553+
* When `false` values are cast to strings (e.g. ['foo' => false] gives `foo=false`).
554+
*/
555+
public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBooleansAsInts = true): string
556+
{
557+
if (!$params) {
558+
return '';
559+
}
560+
561+
if ($encoding === false) {
562+
$encoder = function (string $str): string {
563+
return $str;
564+
};
565+
} elseif ($encoding === PHP_QUERY_RFC3986) {
566+
$encoder = 'rawurlencode';
567+
} elseif ($encoding === PHP_QUERY_RFC1738) {
568+
$encoder = 'urlencode';
569+
} else {
570+
throw new \InvalidArgumentException('Invalid type');
571+
}
572+
573+
$castBool = $treatBooleansAsInts
574+
? function ($v) { return (int) $v; }
575+
: function ($v) { return $v ? 'true' : 'false'; };
576+
577+
$qs = '';
578+
foreach ($params as $k => $v) {
579+
$k = $encoder((string) $k);
580+
if (!is_array($v)) {
581+
$qs .= $k;
582+
$v = is_bool($v) ? $castBool($v) : $v;
583+
if ($v !== null) {
584+
$qs .= '='.$encoder((string) $v);
585+
}
586+
$qs .= '&';
587+
} else {
588+
foreach ($v as $vv) {
589+
$qs .= $k;
590+
$vv = is_bool($vv) ? $castBool($vv) : $vv;
591+
if ($vv !== null) {
592+
$qs .= '='.$encoder((string) $vv);
593+
}
594+
$qs .= '&';
595+
}
596+
}
597+
}
598+
599+
return $qs ? (string) substr($qs, 0, -1) : '';
556600
}
557601
}

modules/openapi-generator/src/main/resources/php/libraries/psr-18/api.mustache

+8-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
namespace {{apiPackage}};
2020

2121
use GuzzleHttp\Psr7\MultipartStream;
22-
use GuzzleHttp\Psr7\Query;
2322
use Http\Client\Common\Plugin\ErrorPlugin;
2423
use Http\Client\Common\Plugin\RedirectPlugin;
2524
use Http\Client\Common\PluginClient;
@@ -95,6 +94,8 @@ use function sprintf;
9594
*/
9695
protected $streamFactory;
9796
97+
protected bool $treatQueryBoolsAsInts;
98+
9899
public function __construct(
99100
ClientInterface $httpClient = null,
100101
Configuration $config = null,
@@ -104,7 +105,8 @@ use function sprintf;
104105
StreamFactoryInterface $streamFactory = null,
105106
HeaderSelector $selector = null,
106107
?array $plugins = null,
107-
$hostIndex = 0
108+
$hostIndex = 0,
109+
bool $treatQueryBoolsAsInts = true
108110
) {
109111
$this->config = $config ?? (new Configuration())->setHost('{{basePath}}');
110112
$this->requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory();
@@ -134,6 +136,8 @@ use function sprintf;
134136
$this->headerSelector = $selector ?? new HeaderSelector();
135137

136138
$this->hostIndex = $hostIndex;
139+
140+
$this->treatQueryBoolsAsInts = $treatQueryBoolsAsInts;
137141
}
138142

139143
/**
@@ -643,7 +647,7 @@ use function sprintf;
643647
644648
} else {
645649
// for HTTP post (form)
646-
$httpBody = Query::build($formParams);
650+
$httpBody = ObjectSerializer::buildQuery($formParams);
647651
}
648652
}
649653

@@ -762,7 +766,7 @@ use function sprintf;
762766
->withHost($host)
763767
->withScheme($scheme)
764768
->withPort($port)
765-
->withQuery(Query::build($queryParams));
769+
->withQuery(ObjectSerializer::buildQuery($queryParams, PHP_QUERY_RFC3986, $this->treatQueryBoolsAsInts));
766770
767771
if ($user) {
768772
$uri = $uri->withUserInfo($user, $password);

samples/client/echo_api/php-nextgen/src/ObjectSerializer.php

+58-17
Original file line numberDiff line numberDiff line change
@@ -539,22 +539,63 @@ public static function deserialize(mixed $data, string $class, array $httpHeader
539539
}
540540

541541
/**
542-
* Native `http_build_query` wrapper.
543-
* @see https://www.php.net/manual/en/function.http-build-query
544-
*
545-
* @param array|object $data May be an array or object containing properties.
546-
* @param string $numeric_prefix If numeric indices are used in the base array and this parameter is provided, it will be prepended to the numeric index for elements in the base array only.
547-
* @param string|null $arg_separator arg_separator.output is used to separate arguments but may be overridden by specifying this parameter.
548-
* @param int $encoding_type Encoding type. By default, PHP_QUERY_RFC1738.
549-
*
550-
* @return string
551-
*/
552-
public static function buildQuery(
553-
array|object $data,
554-
string $numeric_prefix = '',
555-
?string $arg_separator = null,
556-
int $encoding_type = \PHP_QUERY_RFC3986
557-
): string {
558-
return \GuzzleHttp\Psr7\Query::build($data, $encoding_type);
542+
* Build a query string from an array of key value pairs.
543+
*
544+
* This function can use the return value of `parse()` to build a query
545+
* string. This function does not modify the provided keys when an array is
546+
* encountered (like `http_build_query()` would).
547+
*
548+
* @param array $params Query string parameters.
549+
* @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
550+
* to encode using RFC3986, or PHP_QUERY_RFC1738
551+
* to encode using RFC1738.
552+
* @param bool $treatBooleansAsInts When `true` values are cast to int (e.g. ['foo' => false] gives `foo=0`).
553+
* When `false` values are cast to strings (e.g. ['foo' => false] gives `foo=false`).
554+
*/
555+
public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBooleansAsInts = true): string
556+
{
557+
if (!$params) {
558+
return '';
559+
}
560+
561+
if ($encoding === false) {
562+
$encoder = function (string $str): string {
563+
return $str;
564+
};
565+
} elseif ($encoding === PHP_QUERY_RFC3986) {
566+
$encoder = 'rawurlencode';
567+
} elseif ($encoding === PHP_QUERY_RFC1738) {
568+
$encoder = 'urlencode';
569+
} else {
570+
throw new \InvalidArgumentException('Invalid type');
571+
}
572+
573+
$castBool = $treatBooleansAsInts
574+
? function ($v) { return (int) $v; }
575+
: function ($v) { return $v ? 'true' : 'false'; };
576+
577+
$qs = '';
578+
foreach ($params as $k => $v) {
579+
$k = $encoder((string) $k);
580+
if (!is_array($v)) {
581+
$qs .= $k;
582+
$v = is_bool($v) ? $castBool($v) : $v;
583+
if ($v !== null) {
584+
$qs .= '='.$encoder((string) $v);
585+
}
586+
$qs .= '&';
587+
} else {
588+
foreach ($v as $vv) {
589+
$qs .= $k;
590+
$vv = is_bool($vv) ? $castBool($vv) : $vv;
591+
if ($vv !== null) {
592+
$qs .= '='.$encoder((string) $vv);
593+
}
594+
$qs .= '&';
595+
}
596+
}
597+
}
598+
599+
return $qs ? (string) substr($qs, 0, -1) : '';
559600
}
560601
}

samples/client/petstore/php-nextgen/OpenAPIClient-php/lib/ObjectSerializer.php

+64-14
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030

3131
use GuzzleHttp\Psr7\Utils;
3232
use OpenAPI\Client\Model\ModelInterface;
33+
use function GuzzleHttp\Psr7\;
34+
use function is_array;
35+
use function is_bool;
36+
use function substr;
37+
use const PHP_QUERY_RFC1738;
38+
use const PHP_QUERY_RFC3986;
3339

3440
/**
3541
* ObjectSerializer Class Doc Comment
@@ -545,22 +551,66 @@ public static function deserialize($data, $class, $httpHeaders = null)
545551
}
546552

547553
/**
548-
* Native `http_build_query` wrapper.
549-
* @see https://www.php.net/manual/en/function.http-build-query
554+
* Build a query string from an array of key value pairs.
550555
*
551-
* @param array|object $data May be an array or object containing properties.
552-
* @param string $numeric_prefix If numeric indices are used in the base array and this parameter is provided, it will be prepended to the numeric index for elements in the base array only.
553-
* @param string|null $arg_separator arg_separator.output is used to separate arguments but may be overridden by specifying this parameter.
554-
* @param int $encoding_type Encoding type. By default, PHP_QUERY_RFC1738.
556+
* This function can use the return value of `parse()` to build a query
557+
* string. This function does not modify the provided keys when an array is
558+
* encountered (like `http_build_query()` would).
555559
*
556-
* @return string
560+
* The function is copied from https://github.com/guzzle/psr7/blob/a243f80a1ca7fe8ceed4deee17f12c1930efe662/src/Query.php#L59-L112
561+
* with a modification which is described in https://github.com/guzzle/psr7/pull/603
562+
*
563+
* @param array $params Query string parameters.
564+
* @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
565+
* to encode using RFC3986, or PHP_QUERY_RFC1738
566+
* to encode using RFC1738.
567+
* @param bool $treatBooleansAsInts When `true` values are cast to int (e.g. ['foo' => false] gives `foo=0`).
568+
* When `false` values are cast to strings (e.g. ['foo' => false] gives `foo=false`).
557569
*/
558-
public static function buildQuery(
559-
$data,
560-
string $numeric_prefix = '',
561-
?string $arg_separator = null,
562-
int $encoding_type = \PHP_QUERY_RFC3986
563-
): string {
564-
return \GuzzleHttp\Psr7\Query::build($data, $encoding_type);
570+
public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBooleansAsInts = true): string
571+
{
572+
if (!$params) {
573+
return '';
574+
}
575+
576+
if ($encoding === false) {
577+
$encoder = function (string $str): string {
578+
return $str;
579+
};
580+
} elseif ($encoding === PHP_QUERY_RFC3986) {
581+
$encoder = 'rawurlencode';
582+
} elseif ($encoding === PHP_QUERY_RFC1738) {
583+
$encoder = 'urlencode';
584+
} else {
585+
throw new \InvalidArgumentException('Invalid type');
586+
}
587+
588+
$castBool = $treatBooleansAsInts
589+
? function ($v) { return (int) $v; }
590+
: function ($v) { return $v ? 'true' : 'false'; };
591+
592+
$qs = '';
593+
foreach ($params as $k => $v) {
594+
$k = $encoder((string) $k);
595+
if (!is_array($v)) {
596+
$qs .= $k;
597+
$v = is_bool($v) ? $castBool($v) : $v;
598+
if ($v !== null) {
599+
$qs .= '='.$encoder((string) $v);
600+
}
601+
$qs .= '&';
602+
} else {
603+
foreach ($v as $vv) {
604+
$qs .= $k;
605+
$vv = is_bool($vv) ? $castBool($vv) : $vv;
606+
if ($vv !== null) {
607+
$qs .= '='.$encoder((string) $vv);
608+
}
609+
$qs .= '&';
610+
}
611+
}
612+
}
613+
614+
return $qs ? (string) substr($qs, 0, -1) : '';
565615
}
566616
}

0 commit comments

Comments
 (0)