$_POST is empty even though CONTENT_LENGTH is correct - php

I've got an HTTP POST pointed at two different places.
The first location is handled by a thirdparty solution, so I can't see how they're handling the data (which they must be because the values are getting through and I'm seeing results).
In my location, I have an NGINX server (which has never had any problems before with lots of use). I am using php to read the POST data, and I expect the content to be in the $_POST variable as the POST is $_SERVER["CONTENT_TYPE"] => "multipart/form-data"
But, even though the type and the $_SERVER["CONTENT_LENGTH"] are correct, I'm getting nothing in my $_POST, $_REQUEST, and when checking file_get_contents('php://input') there is nothing inside either.
The body is a very small json lump (<1k). Always an array of objects.
To see what's in the arrays I used echo json_encode( array( "GET" => $_GET, "POST" => $_POST, "REQUEST" => $_REQUEST, "SERVER" => $_SERVER ) )
I've run out of ideas of what to check now.
the entry $_SERVER["PHP_SELF"] has a strange garbling possibly due to the path delimiters?

How to post JSON to PHP with curl lead me to the solution:
Don’t forget to send it as application/json
Once I did that, the data came through in the body. This helps me, but I'm still curious as to why the third party receiver can handle the data as it was.

Related

How can I parse multipart/form-data from an HTTP PUT request into an associative array in PHP?

I'm trying to write a REST API in PHP from the ground up for my website, partly as a learning exercise, partly to develop a codebase that I can reference later in case I forget how something works.
To my dismay, I've discovered that PHP has no $_PUT superglobal.
Remembering that Laravel makes GET/PUT/POST/DELETE distinctions, I figured Laravel must have code to handle HTTP PUT requests correcly, but no, in fact, it depends on a hidden form field with the value "_PUT" to specify the action to take.
Without the need to process files, is there any way to take multipart/form-data and parse it into an associative array in a similar fashion to $_POST, such that it is foreach iterable?
Here is what I tried and it simply doesn't work. I'm not understanding what the extra data is that is sent, must be related to the PHP session?
parse_str(file_get_contents("php://input"), $_PUT);
foreach ($_PUT as $key => $value)
{
unset($_PUT[$key]);
$_PUT[str_replace('amp;', '', $key)] = $value;
}
$_REQUEST = array_merge($_REQUEST, $_PUT);
foreach($_PUT as $key=>$value){
$ani->state[$key]['value'] = $value;
}
What I end up getting out of this looks like this: (I am pretty sure I can beat this into what I want, but I don't think what I come up with is going to be robust enough to trust not to break all the time.)
So as not to anger anyone, the code I'm using came directly from here: https://joshtronic.com/2014/06/01/how-to-process-put-requests-with-php/
I tried to spin my own, which ended up looking very similar to his, minus merging the body back into the request, which I still don't completely understand the purpose of.
My solution is now simply to not use multipart/form-data and instead use application/x-www-form-urlencoded. I don't know if this decision will haunt me later on down the road but it solves my problem for now. I can still use this for my API as in JavaScript I can specify the content type as such:
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
I'd be very interested in knowing why PHP doesn't add a $_PUT superglobal?

How do you use XMLHttpRequest with POST method?

I have a central PHP script I use for handling many requests from my page, using XMLHttpRequest() calls from JavaScript, using the GET method. My server PHP is currently 5.6, and yes, I'm avoiding synchronous calls.
It all works well, however there are cases where I'd prefer NOT to have the request remain in browser history, or even be displayed in the URL. So based on the MDN reference on this call, I thought I could simply switch to the POST method, keeping my existing requests (such as "http://myscript.php?cmd=dothis&data=somedata"). then all I'd have to do is add some code to my PHP script, so I could gather the passed data either way...
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if(isset($_GET['cmd'])) $cmd = $_GET['cmd'];
if(isset($_GET['data'])) $data = $_GET['data'];
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if(isset($_POST['cmd'])) $cmd = $_POST['cmd'];
if(isset($_POST['data'])) $data = $_POST['data'];
}
Well obviously i was dead wrong. What I found was...
Neither of the variables shown in this example are seen by the script within the $_POST array, when I switch to the POST method.
Switching from the $_POST array to the $_REQUEST array works fine (eg: "if(isset($_REQUEST['cmd'])) $cmd = $_REQUEST['cmd']);" ), BUT....
Just the act of issuing my XMLHttpRequest() calls with the POST method, results in errors like this in my log:
PHP 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
So obviously This is a dead end. Doing my best to research what is going on here, in the PHP manual reference to POST I read this right on top...
$_POST $HTTP_POST_VARS [deprecated]
So if I'm understanding this right, the reason I can only obtain the POST variables using the $_REQUEST array is because my requests are still formatted as if I were still using GET, which (apparently?) is a deprecated method. Therefore, "coaxing" it to work is just moving the problem to the future.
Incidentally, I can see by calling the phpinfo.php on my server that despite the ERROR I see in my logs, "always_populate_raw_post_data" is indeed already set to "-1". So all the "solutions" I've found recommending I "JUST" make this setting in my PHP.INI file do not help.
And even if it DID "hide" the error, doing so seems irrelevant to me. Am I wrong in saying that even if I simply gather my passed PHP vars from the $_REQUEST array when using the POST method, AND figure out a way to suppressing the "deprecated" warnings in my LOG, the whole scheme would still fall apart (and make my POST requests fail), the day I finally migrate to PHP 7.x.?
I apologize for asking here, but I have really tried to search every reference I could find for both doing XMLHttpRequests using POST method, AND the deprecated error messages I get in my logs. There is so much obsolete info out there, I can't find any clear discussion on how to PROPERLY alter or re-format my XMLHttpRequests so that I can use the POST method. In fact, I can't even positively determine whether doing so will be possible beyond PHP.5.6. Any help will be very much appreciated.
PHP made a mistake of naming $_GET and $_POST after the default place that a web browser will put data in an HTML form where the method attribute is set to GET or POST.
$_GET gets data from the query string, even if the request method was POST.
The request body is determined by the argument you pass to send. Data you put in the query string remains in the query string.
const url = 'http://myscript.php'
const form_data = new FormData();
form_data.append("cmd", "dothis");
form_data.append("data", "somedata");
cost xhr = new XMLHttpRequest;
xhr.open("POST", url);
xhr.addEventListener('load', function () { console.log(this.response); });
xhr.send(form_data);

not getting POST data with application/json

I'm using Backbone, PHP(5.6) and Apache(2.4).
Problem: When posting data using Backbone's model.save() the $_POST array is empty on the server.
I know about different ways to fix this.
Client way: setting Backbone.emulateJSON to true or changing the Backbone source.
Server way: reading raw body instead of $_POST.
These work but they feel like hacks and in fact they end up being a bit inconvenient. Also, Backbone's doc says:
If you're working with a legacy web server that can't handle requests encoded as application/json, setting Backbone.emulateJSON = true; will [fix it].
So it sounds like a server problem. How do I make my server non-legacy?
Thanks to the comments I realized this is correct behavior.
$_POST is for form data, while json data (application/json) should be gotten from the raw input.

How does get_headers work in the background

I tryied searching for this and I belive I alredy know the answer but it's crusal that I'm not wrong, so here I go..
When calling get_headers, will I retrieve the whole file even though the function only returns the headers or will it retrieve, as expected, only the headers and nothing else?
I'm guessing the last but if I'm wrong this will cause some serious problems..
Also I noticed that there is a global setting I can change to send a HEAD request instead of the default GET request, witch is why I'm asking my self whats really going on.
Edit
Maybe this function is a better alternative? stream_get_meta_data or do they actually do the same thing?
You could also take a look at the source code, if you are familiar with C.
The function is defined here. I quickly looked over this, and it seems it is a header-only request, see line 715:
STREAM_ONLY_GET_HEADERS
GET
Requests a representation of the specified resource. Requests using
GET should only retrieve data and should have no other effect. (This
is also true of some other HTTP methods.) The W3C has published
guidance principles on this distinction, saying, "Web application
design should be informed by the above principles, but also by the
relevant limitations."
HEAD
Asks for the response identical to the one that would correspond to a
GET request, but without the response body. This is useful for
retrieving meta-information written in response headers, without
having to transport the entire content.
Wikipedia/Hypertext_Transfer_Protocol
The PHP-docs clearly states that normal get_headers() uses a GET-request, but you can force it to use HEAD instead, like this:
<?php
// By default get_headers uses a GET request to fetch the headers. If you
// want to send a HEAD request instead, you can do so using a stream context:
stream_context_set_default(
array(
'http' => array(
'method' => 'HEAD'
)
)
);
$headers = get_headers('http://example.com');
?>
Unfortunaley you're right, just read the PHP manual:
get_headers() returns an array with the headers sent by the server in response to a HTTP request.
Also take a look at the examples.
Okay, next time I should spend more attention to the question formulation.
Yeh, if the request type is set to GET (standard) you will get the whole content. You could change it to HEAD, but this is not what you want.

Access PUT-data in PHP

I want to access JSON data sent with the PUT-method, as a payload to a PHP-backend.
From what I've read, the data should be available in the php://input stream, and I should be able to fetch it using.
file_get_contents('php://input');
This works for POST-requests, but just returns an empty string when using PUT,
although I can find the PUT-data as the first key in the $_POST variable, like this:
array(
"{"latLngPosition":null,"pixelPosition":null,"id":"1","marking":"012312312313"}" => ""
)
It doesn't seem right to fetch the data this way, so I wonder what I might do wrong when fetching using php://input.
I'm using nginx with fastcgi to run PHP.

Categories