Since PHP 5.6, the use of $HTTP_RAW_POST_DATA
is deprecated. Now, I’m not using this so I’m in the clear, or at least I thought I was …
The default value for always_populate_raw_post_data
in PHP 5.6 is doing more harm than good. Perfectly good code will spit out that error whenever it receives a request that uses a payload (viz. non-application/x-www-form-urlencoded
encoded data in the request). To fix this issue, explicitly set the value of always_populate_raw_post_data
to -1
in your php.ini
. That way the error won’t appear.
The problem
As mentioned in the link above PHP 5.6 will trigger a warning whenever:
- You use
$HTTP_RAW_POST_DATA
. - A script receives non
application/x-www-form-urlencoded
data ifalways_populate_raw_post_data
— a setting inphp.ini
— is enabled
The warning spit out is this:
Deprecated: Automatically populating $HTTP_RAW_POST_DATA is deprecated and will be removed in a future version. To avoid this warning set 'always_populate_raw_post_data' to '-1' in php.ini and use the php://input stream instead. in Unknown on line 0
In the default php.ini
which ships with PHP 5.6 this setting has been commented out. This results in the fact that the setting is set to 0
, thus which should disable it. This hower is not the case: the warning is still shown whenever your script receives a request which uses a payload, whether you use $HTTP_RAW_POST_DATA
or not.
A POST
request, using application/x-www-form-urlencoded
encoded data (default form encoding), will work fine:
$ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "foo=bar" http://domain.tld/
A POST
request, using application/json
encoded data (not uncommon in the age of Hypermedia APIs), will output the warning even if you don’t use $HTTP_RAW_POST_DATA
in the receiving PHP script:
$ curl -X POST -H "Content-Type: application/json" -d "{foo: bar}" http://domain.tld/
If you’re more a JavaScript type of guy/gal, here’s how to trigger it (using jQuery):
$.ajax({
url: 'http://domain.tld/',
type: 'POST',
data: {foo: 'bar' },
contentType: 'application/json; charset=utf-8'
});
The fix
Turns out that a value of 0
for always_populate_raw_post_data
does not actually disable it. The fix is to set always_populate_raw_post_data
it to -1
. Unfortunately it’s not possible to override this value using ini_set
(more on that later), so one actually has to edit php.ini
. Another option is to add php_value always_populate_raw_post_data -1
to your Apache config.
Using MAMP Pro, you can set it per host (if you want):
Unfortunately, not everyone has access to their php.ini
, or their Apache httpd.conf
. When on a shared hosting account you are dependent on the goodwill of the hosting provider. If they’re not that PHP savvy, they might refuse to change the value. This results in the fact that perfectly working libraries — which don’t even use $HTTP_RAW_POST_DATA
— will not work properly on default PHP 5.6 installations! To me, this is rather unacceptable: you do everything right, yet PHP still complains for no apparent reason.
The proper fix / An explanation
When looking at the PHP bug report covering this issue it becomes clear that the warning is shown when $HTTP_RAW_POST_DATA
is being populated – not when it is being used. Now, this is odd at first sight, as always_populate_raw_post_data
is set to 0
(#wat?) yet there’s an explanation for that: “always_populate_raw_post_data=0
does not mean, that $HTTP_RAW_POST_DATA
won’t be populated, it only means, that it will be only populated when a supported MIME type is present” (source) … so 0
is falsy
, but not for this setting 🙁
Other arguments in the bug report covering the issue are that it is technically difficult to only show the notice when that variable is actually being used …
Now, I understand that the PHP developers don’t want to break backwards compatibility: shipping with a default -1
would cause issues as scripts relying on $HTTP_RAW_POST_DATA
will break. On the other hand, not setting it to -1
by default also breaks stuff: the notice will not allow you to do proper redirects anymore (headers already sent
should ring a bell), JSON output becomes invalid, etc.
So no, I don’t agree with the excuse that “it’s technically difficult”. If a setting is causing trouble whether it’s on or off, then a better solution should be found. I see three possible solutions. Here goes, in order of preference:
- Do ship with
-1
as default. If you’re using$HTTP_RAW_POST_DATA
, you’re doing it wrong. - Make PHP properly honor the
0
value: When settingalways_populate_raw_post_data
to0
— which it is by default — then$HTTP_RAW_POST_DATA
should not be populated and the notice should not be shown.
Yes, that would mean reverting the “always_populate_raw_post_data=0
does not mean, that$HTTP_RAW_POST_DATA
won’t be populated, it only means, that it will be only populated when a supported MIME type is present” behaviour (who’s impact I am not aware of). - Although that indeed would be technically impossible (as
$HTTP_RAW_POST_DATA
is populated in the bootstrapping phase of the PHP process) allow one to override the setting by means of callingini_set
.
As options 2 and 3 already have been shot down in the bug report thread, or are virtually impossible; only option 1 remains. So please, core PHP devs, reconsider shipping PHP a default always_populate_raw_post_data -1
in following releases.
Yes, I am aware that you should not display errors on screen in production, yet during development they are. As you don’t have to do an awful lot to trigger the warning, it is confusing for beginning developers. In my job — a lecturer webdev — it’s really strange to have to explain to my students that they’re actually not doing anything wrong but that it’s PHP’s defaults that are the culprit. Their first reaction in such cases always is: “then why is it the default value?”
Thank me with a coffee.
I don\'t do this for profit but a small one-time donation would surely put a smile on my face. Thanks!
To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.
1, “Do ship with -1 as default. If you’re using $HTTP_RAW_POST_DATA, you’re doing it wrong.”
using $HTTP_RAW_POST_DATA before 5.6 was not deprecated, and there were valid reasons to use it (for example using php://input was not re-usable, so if you had two piece of code in the same request which wanted to touch the post data, you couldn’t let them do that through php://input independently, you either had to wrap that into a function call (which also had to store the data in a variable so it works when called multiple times) or simply use $HTTP_RAW_POST_DATA.
so your suggestion would solve the issue for those who did not use $HTTP_RAW_POST_DATA, but it would be a nasty BC for those people who have used it.
and the BC breaks you are bringing up about the current default(eg. an E_DEPRECATED message printed to the output and flushing the output buffer) should not happen, because if you run display_errors = On AND display_startup_errors=On in production, you are already “doing it wrong”.
2, “Make PHP properly honor the 0 value: When setting”
you seem to misunderstand the always_populate_raw_post_data ini setting.
originally that was a boolean, but even then, it did not control whether or not the population should be turned on or off, but whether or not should we always populate or only in some cases (based on the mime type).
I know that this could be a bit confusing, but this is why this setting not called populate_raw_post_data but always_populate_raw_post_data.
Now with 5.6 we turned it into an integer, and kept the meaning for 0 and 1, but introduced -1 which basically means that $HTTP_RAW_POST_DATA will be never populated (what you seem to expect from the value of 0).
So your proposal of changing the meaning of the value 0 would be still a BC break for some people who were/are implicitly depending on the population of $HTTP_RAW_POST_DATA.
3, “Although that indeed would be technically impossible (as $HTTP_RAW_POST_DATA is populated in the bootstrapping phase of the PHP process) allow one to override the setting by means of calling ini_set.”
that would require to somehow make it possible for the userland code to be executed before the deprecated message is triggered.
We can’t really delay the population of the variable, because the userland code can expect it to be populated, and we can’t use just-in-time population either, because that can only be used for superglobals and unfortunately $HTTP_RAW_POST_DATA is not a superglobal but a normal global variable.
We could theoretically delay the triggering of the deprecated message, or buffer it until the output buffer is flushed, but there are a bunch of other ways to trigger similar warnings/notices, even startup errors like this (for example if somebody sends a request with more GET/POST/COOKIE values than allowed by max_input_vars that will trigger a similar startup error, which will cause the same problems as you are trying to solve here).
tl;dr: I’m not completely satisfied with the way we resolved this for 5.6, but I don’t think that we had much better alternatives or that we can do more for 5.6 than maybe improving the wording of the deprecated message.
Hi tyrael,
Thanks for your reply.
Furthermore I am totally aware that one should not display errors on screen, as mentioned in the closing note at the bottom of the post.
What I meant is that a
0
or1
always – at least with thephp.ini
settings that I ever needed to fiddle with – results in disabling or enabling a feature. It’s really strange that a value of0
foralways_populate_raw_post_data
doesn’t result infalsy
, as for all other setting it does result in that. Therefore to me, at first, it seems as if PHP is not honoring the0
/1
value.In short: Shipping with
-1
and only giving the notice when$HTTP_RAW_POST_DATA
is being accessed (as that’s _no longer_ the way to do it) would be an ideal situation I guess, yet I’ve read in the linked bug report thread that that somehow is difficult (although it doesn’t seem virtually impossible as #3) 🙁Regards,
Bram.
“What I meant is that a 0 or 1 always – at least with the php.ini settings that I ever needed to fiddle with – results in disabling or enabling a feature.”
Thats true, but the feature itself wasn’t to control that $HTTP_RAW_POST_DATA is populated(1) or not(0), but as the name suggest if it always populated(1) or only in a specific case(0).
Now we turned that into a three way setting where 1 means always, 0 means or in a specific case and -1 means never, which isn’t a perfect solution, but I do think that it was a better alternative than introducing a separate ini setting for turning the population off completely then killing both settings in PHP7.
4, (changing the default to -1) would be “easy”, but it would be introducing a BC break for those using $HTTP_RAW_POST_DATA, and only having a notice would be a bit harder to figure out and fix for many users/apps because they tend to ignore notices in error_reporting: https://github.com/search?l=php&q=E_ALL+E_NOTICE&ref=searchresults&type=Code&utf8=%E2%9C%93
I’m relucant to change that given how much effort/bad practice is required for the current default to cause noticable problems for the users not using $HTTP_RAW_POST_DATA. (it requires that you enable both display_errors and display_startup_errors and an error_reporting which includes E_DEPRECATED for those errors to show up and even if they do show up, they aren’t inherently cause problems, only if you want to send headers from that script which receives the post data or that your client breaks on such an output.
Thank its working fine