01 · THE IDEAREAD → TRY → LEARN
What a query string is
The part of a URL after ? is a query string: a list of name=value pairs joined by &. Characters such as spaces, ampersands and slashes would break that format, so they are encoded. PHP gives you two complementary tools. http_build_query() turns an array into a query string, and parse_str() turns a query string back into an array. This lesson uses fixed strings only, so nothing here touches the network.
Building a query string
The signature is http_build_query(array|object $data, string $numeric_prefix = "", ?string $arg_separator = null, int $encoding_type = PHP_QUERY_RFC1738): string. For most work you only pass the data. Note three behaviours in the first example:
nullvalues are left out entirely.truebecomes1andfalsebecomes0.- With the default encoding, a space becomes
+.
$data = ['name' => 'Ada Lovelace', 'city' => 'London', 'skip' => null, 'ok' => true, 'no' => false];
echo http_build_query($data), "\n";
echo http_build_query($data, '', '&', PHP_QUERY_RFC3986), "\n";name=Ada+Lovelace&city=London&ok=1&no=0 name=Ada%20Lovelace&city=London&ok=1&no=0
The fourth argument chooses the encoding. PHP_QUERY_RFC1738 is the form style with + for spaces. PHP_QUERY_RFC3986 uses %20, which is often what you want when composing a URL. The third argument sets the separator. Passing null falls back to the arg_separator.output setting, so pass '&' explicitly if you need a predictable result.
Parsing a query string
parse_str(string $string, array &$result): void returns nothing. It fills the variable you pass by reference. Since PHP 8 the second argument is required. Every scalar value arrives as a string, even when it looks like a number. Bracket syntax creates nested arrays, and + and %XX sequences are decoded.
parse_str('a=1&b[]=x&b[]=y&c[k]=v&d=hello+world%21', $out);
echo json_encode($out, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"a":"1","b":["x","y"],"c":{"k":"v"},"d":"hello world!"}
Because everything comes back as strings, a value that started as the integer 2 returns as "2". If your code cares about types, cast after parsing.
Round trips and nested data
Nested arrays are written with encoded brackets: tags[0]=php becomes tags%5B0%5D=php. Parsing reverses this, so structure survives the trip while scalar types do not.
$filters = ['tags' => ['php', 'web'], 'page' => 2, 'q' => 'a&b'];
$query = http_build_query($filters);
echo $query, "\n";
parse_str($query, $back);
echo json_encode($back, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";tags%5B0%5D=php&tags%5B1%5D=web&page=2&q=a%26b
{"tags":["php","web"],"page":"2","q":"a&b"}
Notice that the literal & inside the value a&b was encoded as %26. That is why you should never build query strings by concatenating raw values yourself.
Edge cases and mistakes
- Forgetting that
parse_str()writes into its second argument. Its return value isvoid. - Expecting integers back from
parse_str(). You always get strings for scalar values. - Relying on the default separator. Pass
'&'for stable output. - Duplicate names. In
a=1&a=2the last one wins, unless you use bracket syntax such asa[]. - Passing an object. Only its public properties are included, and
__toString()is not called unless you cast the object yourself. - Key order. Output follows the array order, so sort with
ksort()when you need a canonical form, for example for comparing or caching.
Worked plan for the exercise
You will write set_query_param(string $query, string $name, string $value): string. It parses the incoming query, sets or replaces one parameter, sorts the keys, and rebuilds the string using %20 for spaces. Work in this order:
- Call
parse_str($query, $params)to get an array. - Assign
$params[$name] = $value, which replaces an existing entry or appends a new one. - Call
ksort($params)so the key order is canonical. - Return
http_build_query($params, '', '&', PHP_QUERY_RFC3986).
The tests check returned strings, covering an empty query, a replaced key, encoded characters and a nested array that must survive the trip.
Recap
Use http_build_query() to encode arrays safely, choose the RFC 3986 flag for %20 spaces, and pass an explicit '&' separator. Use parse_str() with an output variable to decode, remembering that scalars come back as strings. Sort keys when you need a stable, comparable result.
Functions · Arrays · Types
- Use http_build_query and parse_str on fixed strings without HTTP I/O
- Implement and test a deterministic PHP 8.4 function