3

PHP will automatically convert

<input type="text" name="foo[0]" value="x" />
<input type="text" name="foo[1]" value="y" />

into

$_POST['foo'] = array(
    0 => 'x', 
    1 => 'y'
);

Which is what you want most of the time. However, in this case I would like this not to happen. Is there anyway to tell PHP to not do this?

I realize I could parse php://input myself, but I'd rather not do that if I can avoid it.

I also don't have the option of renaming the input names.

cdmckay
  • 31,832
  • 25
  • 83
  • 114
  • 2
    Why do you NOT want this? It is core behaviour. Only thing would be to undo it before further processing this request. – Sven Dec 03 '12 at 22:41
  • indeed, that's by design. PHP correctly converts array like input names to arrays on the server side – pocesar Dec 03 '12 at 22:43
  • @Sven: In this particular case, it's because I want to be able to use `$_POST['foo[0]']` to access that value. – cdmckay Dec 03 '12 at 22:50
  • If this is really necessary, your only option may be to pre-process `$_POST`. Just use `is_array` to determine if a value is an array and reconstruct the desired string keys. – grossvogel Dec 03 '12 at 22:51
  • @grossvogel: I think that's what I'll have to end up doing. It's just a bit tedious because you can specify multi-dimensional arrays like `foo[0][bar][baz]`. – cdmckay Dec 03 '12 at 22:52

4 Answers4

9

By using the brackets [] you explicitly tell PHP to create an array, just don't use the brackets:

<input type="text" name="foo_0" value="x" />
<input type="text" name="foo_1" value="y" />

The fields will be available in the $_POST array as $_POST['foo_0'] and $_POST['foo_1'].

If you don't have influence on the markup (which is weird, since you could always change them client side) you need to flatten the array.

$post = array();

foreach ($_POST as $key => $value) {
    if (!is_array($value)) {
        $post[$key] = $value;
    } else {
        foreach ($value as $foo => $item) {
            $post[$foo] = $item;
        }
    }
}

Or read some great input on array flattening.

Community
  • 1
  • 1
markus
  • 40,136
  • 23
  • 97
  • 142
  • 2
    Unfortunately I don't have the option of amending my input names. – cdmckay Dec 03 '12 at 22:49
  • I considered array flattening. The issue with that is that you can have multi-dimensional names, meaning that you would need to recursively flatten. Also, it requires that you essentially copy the `$_POST` superglobal. At that point, I might as well parse `php://input`. – cdmckay Dec 03 '12 at 22:58
  • You can write this as a recursive function, no problem at all. And where's the problem with copying the $_POST superglobal? You should have a really good reason to parse the input by hand and I don't think you have that. – markus Dec 03 '12 at 23:00
2

I don't think you can disable this feature without recompiling php.

I want to warn you about reconstructing the original variable names by walking the multi dim array. You can't fully reconstruct the names because php renames certain characters. For example, php doesnt allow certain characters in a top-level array key in $_POST/$_GET/etc... and so it replaces the char with an underscore. This makes it impossible to differentiate between a.b a b a[b as they all show up as a_b. In addition, there's longstanding bugs related to parsing the array syntax of request variables that causes this behavior. Here's a bug report I filed a few years ago https://bugs.php.net/bug.php?id=48597 which is unlikely to ever be fixed.

In addition, magic_quotes_gpc has sunk its talons into the array keys if that setting is enabled.

But if you're ok with these aforementioned edge cases failing, then you could reconstruct the array as follows:

$ritit = new RecursiveIteratorIterator(new RecursiveArrayIterator($_POST));
$result = array();
foreach ($ritit as $k => $leafValue) {
    if ($ritit->getDepth() > 0) {
        $path = array($ritit->getSubIterator(0)->key());
        foreach (range(1, $ritit->getDepth()) as $depth) {
            $path[] = sprintf('[%s]', $ritit->getSubIterator($depth)->key());
        }
        $result[ join('', $path) ] = $leafValue;
    } else {
        $result[$k] = $leafValue;
    }
}


print_r($result);
goat
  • 31,486
  • 7
  • 73
  • 96
0

Reverting the array conversion should be done like this:

$POST = array('foo' => array('bar', 'baz'));

foreach ($POST['foo'] as $key => $value) {
    $POST['foo['.$key.']'] = $value;
}
unset($POST['foo']);

var_dump($POST);

Output:

array(2) {
  'foo[0]' =>
  string(3) "bar"
  'foo[1]' =>
  string(3) "baz"
}
Sven
  • 69,403
  • 10
  • 107
  • 109
0

I ended up going with the flattening approach suggested by markus-tharkun. For those interested, here's the code I ended up using:

function flatten($model) {
    $repeat = false;
    foreach ($model as $name => $value) {
        if (is_array($value)) {
            $repeat = true;
            foreach ($value as $sub_name => $sub_value) {
                $model["{$name}[$sub_name]"] = $sub_value;
            }
            unset($model[$name]);
        }
    }
    if ($repeat) {
        $model = flatten($model);
    }
    return $model;
}
cdmckay
  • 31,832
  • 25
  • 83
  • 114