Skip to content

Commit

Permalink
feat(php): allow to pass raw boolean to api
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
simPod committed Apr 29, 2024
1 parent 8faa77c commit f0e21ae
Show file tree
Hide file tree
Showing 15 changed files with 524 additions and 187 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -529,22 +529,63 @@ class ObjectSerializer
}

/**
* Native `http_build_query` wrapper.
* @see https://www.php.net/manual/en/function.http-build-query
*
* @param array|object $data May be an array or object containing properties.
* @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.
* @param string|null $arg_separator arg_separator.output is used to separate arguments but may be overridden by specifying this parameter.
* @param int $encoding_type Encoding type. By default, PHP_QUERY_RFC1738.
*
* @return string
*/
public static function buildQuery(
array|object $data,
string $numeric_prefix = '',
?string $arg_separator = null,
int $encoding_type = \PHP_QUERY_RFC3986
): string {
return \GuzzleHttp\Psr7\Query::build($data, $encoding_type);
* Build a query string from an array of key value pairs.
*
* This function can use the return value of `parse()` to build a query
* string. This function does not modify the provided keys when an array is
* encountered (like `http_build_query()` would).
*
* @param array $params Query string parameters.
* @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
* to encode using RFC3986, or PHP_QUERY_RFC1738
* to encode using RFC1738.
* @param bool $treatBooleansAsInts When `true` values are cast to int (e.g. ['foo' => false] gives `foo=0`).
* When `false` values are cast to strings (e.g. ['foo' => false] gives `foo=false`).
*/
public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBooleansAsInts = true): string
{
if (!$params) {
return '';
}

if ($encoding === false) {
$encoder = function (string $str): string {
return $str;
};
} elseif ($encoding === PHP_QUERY_RFC3986) {
$encoder = 'rawurlencode';
} elseif ($encoding === PHP_QUERY_RFC1738) {
$encoder = 'urlencode';
} else {
throw new \InvalidArgumentException('Invalid type');
}

$castBool = $treatBooleansAsInts
? function ($v) { return (int) $v; }
: function ($v) { return $v ? 'true' : 'false'; };

$qs = '';
foreach ($params as $k => $v) {
$k = $encoder((string) $k);
if (!is_array($v)) {
$qs .= $k;
$v = is_bool($v) ? $castBool($v) : $v;
if ($v !== null) {
$qs .= '='.$encoder((string) $v);
}
$qs .= '&';
} else {
foreach ($v as $vv) {
$qs .= $k;
$vv = is_bool($vv) ? $castBool($vv) : $vv;
if ($vv !== null) {
$qs .= '='.$encoder((string) $vv);
}
$qs .= '&';
}
}
}

return $qs ? (string) substr($qs, 0, -1) : '';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -536,22 +536,66 @@ class ObjectSerializer
}

/**
* Native `http_build_query` wrapper.
* @see https://www.php.net/manual/en/function.http-build-query
*
* @param array|object $data May be an array or object containing properties.
* @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.
* @param string|null $arg_separator arg_separator.output is used to separate arguments but may be overridden by specifying this parameter.
* @param int $encoding_type Encoding type. By default, PHP_QUERY_RFC1738.
*
* @return string
*/
public static function buildQuery(
$data,
string $numeric_prefix = '',
?string $arg_separator = null,
int $encoding_type = \PHP_QUERY_RFC3986
): string {
return \GuzzleHttp\Psr7\Query::build($data, $encoding_type);
* Build a query string from an array of key value pairs.
*
* This function can use the return value of `parse()` to build a query
* string. This function does not modify the provided keys when an array is
* encountered (like `http_build_query()` would).
*
* The function is copied from https://github.com/guzzle/psr7/blob/a243f80a1ca7fe8ceed4deee17f12c1930efe662/src/Query.php#L59-L112
* with a modification which is described in https://github.com/guzzle/psr7/pull/603
*
* @param array $params Query string parameters.
* @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
* to encode using RFC3986, or PHP_QUERY_RFC1738
* to encode using RFC1738.
* @param bool $treatBooleansAsInts When `true` values are cast to int (e.g. ['foo' => false] gives `foo=0`).
* When `false` values are cast to strings (e.g. ['foo' => false] gives `foo=false`).
*/
public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBooleansAsInts = true): string
{
if (!$params) {
return '';
}

if ($encoding === false) {
$encoder = function (string $str): string {
return $str;
};
} elseif ($encoding === PHP_QUERY_RFC3986) {
$encoder = 'rawurlencode';
} elseif ($encoding === PHP_QUERY_RFC1738) {
$encoder = 'urlencode';
} else {
throw new \InvalidArgumentException('Invalid type');
}

$castBool = $treatBooleansAsInts
? function ($v) { return (int) $v; }
: function ($v) { return $v ? 'true' : 'false'; };

$qs = '';
foreach ($params as $k => $v) {
$k = $encoder((string) $k);
if (!is_array($v)) {
$qs .= $k;
$v = is_bool($v) ? $castBool($v) : $v;
if ($v !== null) {
$qs .= '='.$encoder((string) $v);
}
$qs .= '&';
} else {
foreach ($v as $vv) {
$qs .= $k;
$vv = is_bool($vv) ? $castBool($vv) : $vv;
if ($vv !== null) {
$qs .= '='.$encoder((string) $vv);
}
$qs .= '&';
}
}
}

return $qs ? (string) substr($qs, 0, -1) : '';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
namespace {{apiPackage}};

use GuzzleHttp\Psr7\MultipartStream;
use GuzzleHttp\Psr7\Query;
use Http\Client\Common\Plugin\ErrorPlugin;
use Http\Client\Common\Plugin\RedirectPlugin;
use Http\Client\Common\PluginClient;
Expand Down Expand Up @@ -95,6 +94,8 @@ use function sprintf;
*/
protected $streamFactory;
protected bool $treatQueryBoolsAsInts;
public function __construct(
ClientInterface $httpClient = null,
Configuration $config = null,
Expand All @@ -104,7 +105,8 @@ use function sprintf;
StreamFactoryInterface $streamFactory = null,
HeaderSelector $selector = null,
?array $plugins = null,
$hostIndex = 0
$hostIndex = 0,
bool $treatQueryBoolsAsInts = true
) {
$this->config = $config ?? (new Configuration())->setHost('{{basePath}}');
$this->requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory();
Expand Down Expand Up @@ -134,6 +136,8 @@ use function sprintf;
$this->headerSelector = $selector ?? new HeaderSelector();

$this->hostIndex = $hostIndex;

$this->treatQueryBoolsAsInts = $treatQueryBoolsAsInts;
}

/**
Expand Down Expand Up @@ -643,7 +647,7 @@ use function sprintf;
} else {
// for HTTP post (form)
$httpBody = Query::build($formParams);
$httpBody = ObjectSerializer::buildQuery($formParams);
}
}

Expand Down Expand Up @@ -762,7 +766,7 @@ use function sprintf;
->withHost($host)
->withScheme($scheme)
->withPort($port)
->withQuery(Query::build($queryParams));
->withQuery(ObjectSerializer::buildQuery($queryParams, PHP_QUERY_RFC3986, $this->treatQueryBoolsAsInts));
if ($user) {
$uri = $uri->withUserInfo($user, $password);
Expand Down
75 changes: 58 additions & 17 deletions samples/client/echo_api/php-nextgen/src/ObjectSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -539,22 +539,63 @@ public static function deserialize(mixed $data, string $class, array $httpHeader
}

/**
* Native `http_build_query` wrapper.
* @see https://www.php.net/manual/en/function.http-build-query
*
* @param array|object $data May be an array or object containing properties.
* @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.
* @param string|null $arg_separator arg_separator.output is used to separate arguments but may be overridden by specifying this parameter.
* @param int $encoding_type Encoding type. By default, PHP_QUERY_RFC1738.
*
* @return string
*/
public static function buildQuery(
array|object $data,
string $numeric_prefix = '',
?string $arg_separator = null,
int $encoding_type = \PHP_QUERY_RFC3986
): string {
return \GuzzleHttp\Psr7\Query::build($data, $encoding_type);
* Build a query string from an array of key value pairs.
*
* This function can use the return value of `parse()` to build a query
* string. This function does not modify the provided keys when an array is
* encountered (like `http_build_query()` would).
*
* @param array $params Query string parameters.
* @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
* to encode using RFC3986, or PHP_QUERY_RFC1738
* to encode using RFC1738.
* @param bool $treatBooleansAsInts When `true` values are cast to int (e.g. ['foo' => false] gives `foo=0`).
* When `false` values are cast to strings (e.g. ['foo' => false] gives `foo=false`).
*/
public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBooleansAsInts = true): string
{
if (!$params) {
return '';
}

if ($encoding === false) {
$encoder = function (string $str): string {
return $str;
};
} elseif ($encoding === PHP_QUERY_RFC3986) {
$encoder = 'rawurlencode';
} elseif ($encoding === PHP_QUERY_RFC1738) {
$encoder = 'urlencode';
} else {
throw new \InvalidArgumentException('Invalid type');
}

$castBool = $treatBooleansAsInts
? function ($v) { return (int) $v; }
: function ($v) { return $v ? 'true' : 'false'; };

$qs = '';
foreach ($params as $k => $v) {
$k = $encoder((string) $k);
if (!is_array($v)) {
$qs .= $k;
$v = is_bool($v) ? $castBool($v) : $v;
if ($v !== null) {
$qs .= '='.$encoder((string) $v);
}
$qs .= '&';
} else {
foreach ($v as $vv) {
$qs .= $k;
$vv = is_bool($vv) ? $castBool($vv) : $vv;
if ($vv !== null) {
$qs .= '='.$encoder((string) $vv);
}
$qs .= '&';
}
}
}

return $qs ? (string) substr($qs, 0, -1) : '';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@

use GuzzleHttp\Psr7\Utils;
use OpenAPI\Client\Model\ModelInterface;
use function GuzzleHttp\Psr7\;
use function is_array;
use function is_bool;
use function substr;
use const PHP_QUERY_RFC1738;
use const PHP_QUERY_RFC3986;

/**
* ObjectSerializer Class Doc Comment
Expand Down Expand Up @@ -545,22 +551,66 @@ public static function deserialize($data, $class, $httpHeaders = null)
}

/**
* Native `http_build_query` wrapper.
* @see https://www.php.net/manual/en/function.http-build-query
* Build a query string from an array of key value pairs.
*
* @param array|object $data May be an array or object containing properties.
* @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.
* @param string|null $arg_separator arg_separator.output is used to separate arguments but may be overridden by specifying this parameter.
* @param int $encoding_type Encoding type. By default, PHP_QUERY_RFC1738.
* This function can use the return value of `parse()` to build a query
* string. This function does not modify the provided keys when an array is
* encountered (like `http_build_query()` would).
*
* @return string
* The function is copied from https://github.com/guzzle/psr7/blob/a243f80a1ca7fe8ceed4deee17f12c1930efe662/src/Query.php#L59-L112
* with a modification which is described in https://github.com/guzzle/psr7/pull/603
*
* @param array $params Query string parameters.
* @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
* to encode using RFC3986, or PHP_QUERY_RFC1738
* to encode using RFC1738.
* @param bool $treatBooleansAsInts When `true` values are cast to int (e.g. ['foo' => false] gives `foo=0`).
* When `false` values are cast to strings (e.g. ['foo' => false] gives `foo=false`).
*/
public static function buildQuery(
$data,
string $numeric_prefix = '',
?string $arg_separator = null,
int $encoding_type = \PHP_QUERY_RFC3986
): string {
return \GuzzleHttp\Psr7\Query::build($data, $encoding_type);
public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBooleansAsInts = true): string
{
if (!$params) {
return '';
}

if ($encoding === false) {
$encoder = function (string $str): string {
return $str;
};
} elseif ($encoding === PHP_QUERY_RFC3986) {
$encoder = 'rawurlencode';
} elseif ($encoding === PHP_QUERY_RFC1738) {
$encoder = 'urlencode';
} else {
throw new \InvalidArgumentException('Invalid type');
}

$castBool = $treatBooleansAsInts
? function ($v) { return (int) $v; }
: function ($v) { return $v ? 'true' : 'false'; };

$qs = '';
foreach ($params as $k => $v) {
$k = $encoder((string) $k);
if (!is_array($v)) {
$qs .= $k;
$v = is_bool($v) ? $castBool($v) : $v;
if ($v !== null) {
$qs .= '='.$encoder((string) $v);
}
$qs .= '&';
} else {
foreach ($v as $vv) {
$qs .= $k;
$vv = is_bool($vv) ? $castBool($vv) : $vv;
if ($vv !== null) {
$qs .= '='.$encoder((string) $vv);
}
$qs .= '&';
}
}
}

return $qs ? (string) substr($qs, 0, -1) : '';
}
}
Loading

0 comments on commit f0e21ae

Please sign in to comment.