One of the really anoying things about PHP is the need for every multiple select field to end the name in brackets. For example...
<select name="field">
<option value="apple">Apple</option>
<option value="microsoft">Microsoft</option>
<option value="google">Google</option>
</select>
The name
attribute would need to be field[]
not just field
.
The reason this is needed is because of the way PHP builds the $_GET
and $_POST
arrays.
You see, when a multiple select field is given multiple values e.g. apple
and microsoft
, two key value pairs are sent in the requset.
A GET
request would create a query string that looks like this...
?field=apple&field=microsoft
Notice that field
is included twice once for each value.
If the brackets were included the request would instead look like this.
?field%5B%5D=apple&field%5B%5D=microsoft
That is a pretty ugly URL. But that is what PHP requies of us. Or is it?
PHP uses the ending []
to determin that the field sends multiple values. Because of that instead of creating a string value for the field an array is created. This is true even if the field only sends one value.
// URL ?field%5B%5D=apple&field%5B%5D=microsoft
echo '<pre>' . print_r( $_GET, 1 ) . '</pre>';
/* The output
Array
(
[field] => Array
(
[0] => apple
[1] => microsoft
)
)
*/
However, if we sent only one key value pair, The result would still be an array.
// URL ?field%5B%5D=apple
echo '<pre>' . print_r( $_GET, 1 ) . '</pre>';
/* The output
Array
(
[field] => Array
(
[0] => apple
)
)
*/
But if we removed the []
a.k.a %5B%5D
at the end, The result for field
would be a string value.
// URL ?field=apple
echo '<pre>' . print_r( $_GET, 1 ) . '</pre>';
/* The output
Array
(
[field] => apple
)
*/
This is important. If we send a two or more values without the brackets each value is over written by the following value. This will cause problems because only the last value is given.
// URL ?field=apple&field=microsoft
echo '<pre>' . print_r( $_GET, 1 ) . '</pre>';
/* The output
Array
(
[field] => microsoft
)
*/
Is there a way to avoid this URL monstrocity PHP is trying to for on us? Yes, there is.
Bracketless Query String
The way to get around this is to parse the query string itself. Unfortunately, all the standard methods to do this in PHP will overwrite repeated keys with the last value if []
is not appended to the key.
The following function will parse the query string and generate an associative array of form values.
/**
* Parses GET and POST form input like $_GET and $_POST, but without requiring multiple select inputs to end the name
* in a pair of brackets.
*
* @param string $method The input method to use 'GET' or 'POST'.
* @param string $querystring A custom form input in the query string format.
* @return array $output Returns an array containing the input keys and values.
*/
function bracketless_input( $method, $querystring=null ) {
// Create empty array to
$output = array();
// Get query string from function call
if( $querystring !== null ) {
$query = $querystring;
// Get raw POST data
} elseif ($method == 'POST') {
$query = file_get_contents('php://input');
// Get raw GET data
} elseif ($method == 'GET') {
$query = $_SERVER['QUERY_STRING'];
}
// Separerate each parameter into key value pairs
foreach( explode( '&', $query ) as $params ) {
$parts = explode( '=', $params );
// Remove any existing brackets and clean up key and value
$parts[0] = trim(preg_replace( '(\%5B|\%5D|[\[\]])', '', $parts[0] ) );
$parts[0] = preg_replace( '([^0-9a-zA-Z])', '_', urldecode($parts[0]) );
$parts[1] = urldecode($parts[1]);
// Create new key in $output array if param does not exist.
if( !key_exists( $parts[0], $output ) ) {
$output[$parts[0]] = $parts[1];
// Add param to array if param key already exists in $output
} elseif( is_array( $output[$parts[0]] ) ) {
array_push( $output[$parts[0]], $parts[1] );
// Otherwise turn $output param into array and append current param
} else {
$output[$parts[0]] = array( $output[$parts[0]], $parts[1] );
}
}
return $output;
}
Let's see the results if we use this function. We will return the GET
data array from the following URLs using our new function.
// URL ?field=apple&field=microsoft
$get = bracketless_input('GET');
echo '<pre>' . print_r( $get, 1 ) . '</pre>';
/* The output
Array
(
[field] => Array
(
[0] => apple
[1] => microsoft
)
)
*/
This works with brackets as well.
// URL ?field%5B%5D=apple&field%5B%5D=microsoft
$get = bracketless_input('GET');
echo '<pre>' . print_r( $get, 1 ) . '</pre>';
/* The output
Array
(
[field] => Array
(
[0] => apple
[1] => microsoft
)
)
*/
It should be noted that if only one key / value pair is sent to the server, the result will be a string not an array like you would get if you used $_GET
.
// URL ?field=apple
$get = bracketless_get();
echo '<pre>' . print_r( $get, 1 ) . '</pre>';
/* The output
Array
(
[field] => apple
)
*/
Warning:
This does not work with enctype="multipart/form-data"
.
If your form requires multipart/form-data
, you will have to use the brackets at the end of the name.