Here is a function I created to build the query and preserve names. I created this to work with a third-party API that requires multiple query string parameters with the same name.
function custom_build_query(
array|object $query_data,
string $numeric_prefix = "",
?string $arg_separator = null,
int $encoding_type = PHP_QUERY_RFC1738
): string {
// Cast to array if object is supplied.
$query_data = is_object($query_data) ? get_object_vars($query_data) : $query_data;
// Use supplied arg_separator value, defaulting to the `arg_separator.output` php configuration.
$arg_separator = $arg_separator ?? ini_get("arg_separator.output");
// If PHP_QUERY_RFC3986 is specified, use rawurlencode to encode parameters.
$encoding_function = $encoding_type === PHP_QUERY_RFC3986 ? "rawurlencode" : "urlencode";
$query = [];
foreach ($query_data as $name => $value) {
$value = (array) $value;
$name = is_int($name) ? $numeric_prefix . $name : $name;
array_walk_recursive($value, function ($value) use (&$query, $name, $encoding_function) {
$query[] = $encoding_function($name) . "=" . $encoding_function($value);
});
}
return implode($arg_separator, $query);
}
Usage:
echo custom_build_query(['a' => 1, 'b' => 2, 'c' => [3, 4]]);
Output:
a=1&b=2&c=3&c=4
Note: The function's signature is fully compatible with http_build_query. The only difference is, of course, the function will not use brackets for sub-array parameters.