PHP Truncating POST Data

A user reported an issue with Shortmarks that he was unable to change the default search engine. Given that the whole point of Shortmarks is to help you search a variety of search engines faster, this was peculiar. I search with Shortmarks dozens if not hundreds of times a day and had no problem changing my default search engine.

My first guess was that one of his Shortmarks entries had illegal characters or some other issue that was causing problems. But when I looked into the data, that didn’t turn out to be the case. Everything was working as it should, except the default search engine key wasn’t showing up in the server’s $_POST array.

I was mystified and had to leave it alone for a while to let my subconscious work on it. Eventually I came up with the idea to start from a very simple test to verify that nothing was wrong with that particular array key. It was there with no problems.

The only way to reproduce the issue was using this user’s JSON data. I knew I wasn’t hitting overall POST data limit, but I wondered if there might be another limit I was bumping up against. It turns out that’s exactly what the problem was.

The post request was hitting PHP’s max-input-vars default limit of 1,000. I bumped it up to 2,000 and the problem went away. Then I put it back to 1,000 and it failed again.

I knew the problem, but I was still confused. Shortmarks uses a jQuery post request to send the data up to the server, and I had explicitly set dataType to JSON in that call, and data was JSON data.

This was the crux of the issue. jQuery.post() is a shorthand for this jQuery.ajax() call:

$.ajax({
  type: "POST",
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

The dataType seems like it would define the type of the contents of the data property, but that’s not what it does. Instead, it defines the type of data expected from the server. It has nothing to do with the type of the data property.

I also learned that the jQuery ajax method defaults to application/x-www-form-urlencoded; charset=UTF-8 if you don’t specify the type. And if you use the jQuery.post method, the only way to specify the type is to set the Content-Type using jQuery.ajaxSetup. But the docs recommend not using that method and it affects all future ajax requests. In short, it’s not a good solution.

Once I realized this, I replaced the jQuery.post method with jQuery.ajax and set the contentType property.

If you’ve followed along thus far, and you’re curious how to use jQuery to send JSON data and avoid the max_input_value limit entirely, here’s how:

$.ajax({
  url: "/update", // The URL to post to
  data: { "foo" : "bar", "biz" : "baz" }, // Your JSON data
  success: receiveData, // Function called if the request succeeds
  error: receiveError, // Function called if an error occurs
  contentType: "application/json; charset=utf-8", // Type of data sent to server
  dataType: "json", // Type of data in the response
  type: "POST",
});

Remember, if you don’t set contentType to JSON, jQuery will instead encode your JSON data as if it was submitted as a very large HTML form, and you’re liable to hit the max_input vars 1,000 limit.

On the server side, instead of reading from the $_POST variable, you’ll need to read the input like you would a file.

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $jsonPostData = file_get_contents('php://input');
}

In summary, if you’re using jQuery to post data to a PHP service and your data is getting truncated, use the jQuery.ajax method to set the contentType to JSON.

Comments are closed