Recently I ran into an interesting problem that was reported back to me by one of my customers. They could not see all the data and obviously something was wrong. It turned out, that PHP implements a limit of variables that can be registered in a POST request. That of course points immediately towards Suhosin from the Hardened PHP project, but the package php5-suhosin was not installed and obviously something else was the culprit.

I do not want to discuss nor judge whether having more than 1000 variables in a POST request is good or bad – it all depends on the task. To test and debug the situation I created two simple scripts that were simulating a large POST request with help of CURL.

Send & report script:

error_reporting(E_ALL);
    ini_set('display_errors', 1);
    $url = "http://example.com/receive.php";
    $indata = range(1, 1500);
    $str = http_build_query($indata);
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_HTTPHEADER, array('Expect:'));
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, "_method=POST&$str");
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_TIMEOUT, 600);
    $data = curl_exec($curl); // execute the curl command
    print_r($data);    
    print_r(curl_getinfo($curl));
    curl_close($curl);

 

Receive script:

if(!empty($_POST)) {
    echo date("Y-m-d-H-i-s");
    echo ":::";
    echo count($_POST);
}

This way I was able to quickly see whether my modifications were affecting PHP in any way. If the limitation is in effect you should see the following output:

2012-10-19-13-44-20:::1001
[some data is cut for clarity]

Obviously the number appended to the date should be 1501. The solution turned out to be easy regardless of the fact if you can modify the php.ini file.

Solution for those that can modify php.ini

In the error.log you should the following lines:

[Fri Oct 19 13:29:06 2012] [error] [client xx.xx.xx.xx] PHP Warning: Unknown: Input variables exceeded 1000. To increase the limit change max_input_vars in php.ini. in Unknown on line 0

The stock php.ini does not contain this configuration setting, that means you have to add it yourself in the global section and set it to the right value (default is 1000, that means putting max_input_vars = 1600 should do the trick in this case). Once you modify it the output will look like this:

2012-10-19-13-57-57:::1501
[some data is cut for clarity]

Remember to reload your apache after you modify your php.ini

Solution for those that cannot modify php.ini (shared hostings etc.)

You will simply have to modify both scripts and stringify your request. This is a common technique used in transforming JSON objects into strings. In PHP we are going to use serialize() and unserialize() methods to achieve a similar effect.

The main point is that all your variables have to be transformed into a string and then put into a container variable. This way your POST request will contain only 1 variable instead of 1501 (looking at the example presented above).

Changes to the sending script:

$indata = range(1, 1500);
$stringify['data'] = serialize($indata);
$str = http_build_query($stringify);

Changes to the receiving script:

if(!empty($_POST['data'])) {
    echo date("Y-m-d-H-i-s");
    echo ":::";
    $data = unserialize($_POST['data']);
    echo count($data);
}

This way you can now POST more than 1000 variables regardless of the fact if you can edit php.ini or not.

In case you do have php5-suhosin installed you should modify the following 3 values in your php.ini:

max_input_vars = 1500
suhosin.post.max_vars = 1500
suhosin.request.max_vars = 1500

More about these configuration directives can be found on Suhosin’s web: suhosin.post.max_vars & suhosin.request.max_vars – and of course PHP max_input_vars.