PHP:: Get POST Request Content - php

I'm sending a POST request using WebClient.UploadData() method (C#) to my webserver. The packet sent to my webserver looks like so:
POST / HTTP/1.1
Host: {ip}
Content-Length: {length}
Expect: 100-continue
Connection: Keep-Alive
{buffer_content}
As the {buffer_content} is nowhere assigned in the $_POST array, I have the following question...
Question: How do I read the {buffer_content} with PHP?
I've stumbled upon file_get_contents('php://input'), but I'm unsure whether that is recommended to do.

Use the php://input stream:
$requestBody = file_get_contents('php://input');
This is the recommended way to do this and, in PHP 7.0, the only way. Previously, there was sometimes a global variable called $HTTP_RAW_POST_DATA, but whether it existed would depend on an INI setting, and creating it hurt performance. That variable was deprecated and removed.
Beware that prior to PHP 5.6, you can only read php://input once, so make sure you store it.
Once you have your body, you can then decode it from JSON or whatever, if you need that:
$requestBody = json_decode($requestBody) or die("Could not decode JSON");

Related

PSR-7: getParsedBody() vs getBody()

Scenario 1 sending x-www-form-urlencoded data
POST /path HTTP/1.1
Content-Type: application/x-www-form-urlencoded
foo=bar
Running print_r($request->getParsedBody()); returns fine:
Array
(
[foo] => bar
)
Running print_r($request->getBody()->getContents()); returns a string foo=bar
Scenario 2 sending application/json data
POST /path HTTP/1.1
Content-Type: application/json
{
"foo": "bar"
}
Running print_r($request->getParsedBody()); returns an empty array. Array ( )
But, running print_r($request->getBody()->getContents()); returns fine:
{"foo":"bar"}
Is this expected behavior?
Meaning, if we're sending x-www-form-urlencoded data, we should use getParsedBody().
While getBody()->getContents() should be used if we're sending application/json?
Additional info:
Request object is created using:
$request = \Laminas\Diactoros\ServerRequestFactory::fromGlobals(
$_SERVER, $_GET, $_POST, $_COOKIE, $_FILES
);
Message body:
In a PSR-7 library, the message body is abstracted by the StreamInterface. Any implementation of this interface MUST wrap a PHP stream and, of course, should provide the proper functionality to perform the specific read/write/seek operations on it. PHP provides a list of I/O streams, from which php://input is suitable for the task in question.
php://input is a read-only stream that allows you to read raw data from the request body. php://input is not available with enctype="multipart/form-data".
In this context, when a request to the server is performed, the request body data (regardless of its data type) is automatically written to the php://input stream, in raw format (string). The information can later be read from it by calling StreamInterface::getContents, StreamInterface::__toString, or StreamInterface::read (which would probably make use of stream_get_contents(), or similar, in their implementation).
Note: The method StreamInterface::__toString is automatically called when the object representing the message body, e.g. the instance of the class implementing StreamInterface is cast to a string. For example, like this - see Type Casting in PHP:
$messageBodyObject = $request->getBody(); // implements StreamInterface
$contentOfMessageBody = (string) $messageBodyObject; // cast to string => StreamInterface::__toString is called
echo $contentOfMessageBody;
Parsed body:
In regard of the PSR-7, the parsed body is a "characteristic" of the applications where PHP is "used as a server-side application to fulfill HTTP requests" (in comparison with the applications where PHP is used as "an HTTP client") - see Summary of the PSR-7 Meta Document. So, the parsed body is a component of the ServerRequestInterface only.
The parsed body (read the comments of ServerRequestInterface::getParsedBody and ServerRequestInterface::withParsedBody) is thought as a representation in a "parsed" form (array, or object) of the raw data (a string) saved in the php://input stream as the result of performing a request. For example, the $_POST variable, which is an array, holds the parsed body of a POST request, under the conditions presented bellow.
Relevant use-cases:
If a POST request is performed and the header Content-Type is application/x-www-form-urlencoded (for example when submitting a normal HTML form), the content of the request body is automatically saved into both the php://input stream (serialized) and the $_POST variable (array). So, in the PSR-7 context, calling both StreamInterface::getContents (or StreamInterface::__toString, or StreamInterface::read) and ServerRequestInterface::getParsedBody will return "valid" values.
If a POST request is performed and the header Content-Type is multipart/form-data (for example when performing a file(s) upload), the content of the request body is NOT saved into the php://input stream at all, but only into the $_POST variable (array). So, in the PSR-7 context, only calling ServerRequestInterface::getParsedBody will return a "valid" value.
If a POST request is performed and the header Content-Type has other value than the two presented above (for example application/json, or text/plain; charset=utf-8), the content of the request body is saved only into the php://input stream. So, in the PSR-7 context, only calling StreamInterface::getContents (or StreamInterface::__toString, or StreamInterface::read) will return a "valid" value.
Resources:
PSR-7: HTTP message interfaces
PSR-7 Meta Document
How to get body of a POST in php?
PHP "php://input" vs $_POST
php://input - what does it do in fopen()? (the comment to the answer)
The answer by #dakis is correct, but I find it a bit ambiguous in answering the original question of why Scenario 2 failed.
From a PSR standpoint, the behaviour is correct (as #dakis said):
body returns a stream to the request body
parsedBody is a characteristic of the request and can contain a parsed representation of the body (but is not required to), as mentioned in the PHPDoc of ServerRequestInterface::getParsedBody:
Otherwise, this method may return any results of deserializing the request body content; ...
From a usefulness perspective, laminas-diactoros is lacking and, in my opinion, half-baked. This library doesn't seem to do a lot more than passing around data already parsed by PHP ($_GET/$_POST..). A better implementation would have handled the specific content-type for use with parsedBody and would have automatically thrown or handled bad POST data.

How to get "php://input" data if I'm using Content-Type multipart/form-data

Is there an alternative?
I'm using Advanced Rest Client for testing an API I'm developing.
I send a JSON with POST.
In code, $_FILES is fine, but file_get_contents("php://input") is empty.
If I don't send any files, then I can use file_get_contents("php://input")
PHP version: 5.6.4
As GhostGambler states, php://input is not available with enctype="multipart/form-data".
You should not attach the JSON as a file to your request, you should add it as the request body to the post request, setting the Content-Type header (application/json). Then it will be available in php://input.
Ok, so I ended up giving a name to my JSON data, like 0=[{"q":"w"}] and then get it with $_POST['0']. And the files with $_FILES
Here's how it looks in Advanced REST Client:
Most likely accessing any of the POST/FILES superglobals consumes php://input.
In any case, if you send a JSON payload you cannot have a multipart-formdata payload too so $_FILES should be empty. If you need to handle both on the same page (bad idea IMO) make sure to check the content type header or some other information outside the request's body before accessing either $_FILES or php://input
php://input is a read-only stream that allows you to read raw data
from the request body. In the case of POST requests, it is preferable
to use php://input instead of $HTTP_RAW_POST_DATA as it does not
depend on special php.ini directives. Moreover, for those cases where
$HTTP_RAW_POST_DATA is not populated by default, it is a potentially
less memory intensive alternative to activating
always_populate_raw_post_data. php://input is not available with
enctype="multipart/form-data".
http://php.net/manual/de/wrappers.php.php
Since HTTP_RAW_POST_DATA is marked deprecated, I guess you are somewhat unlucky. I do not know alternatives.
Edit: Well, you could try php://stdin / STDIN, although I do not know if this works with PHP in a webserver ... maybe just try it out.

PHP Get the Raw HTTP Request (php://input not working)

I'm trying to access the raw HTTP request sent to the server in PHP.
However, all the input/output streams are not working.
I can't use php://input, and I don't want to have to "interpolate" the request from the arrays such as $_COOKIES, $_POST, etc. $_POST, $_GET and the other arrays are working fine. I'm using WAMPServer on Windows 7.
Can anyone help me fix the problem with the input/output streams or find another way to get the raw request data?
From the PHP docs:
php://input is a read-only stream that allows you to read raw data from the request body
which means you can only read body data, not headers or the raw request. If you're running under Apache, you can use the function apache_request_headers to get all the headers. To get the "request" line (the first line of the request), I suppose you need to concat the strings you can get from the $_SERVER variable.

PHP cookie handling

A centain web client that I need to support, is sending back the Cookies header to my application twice in the HTTP headers, this in turn is making PHP unable to read the correct value for the cookie thus ignoring the session.
Here is the relevant part of the request I am seeing:
GET / HTTP/1.1
Cache-Control: max-age=0
Accept-Language: en-US
Cookie: PHPSESSID=49af82ddf12740e6a35b15985e93d91a
Connection: Keep-Alive
Cookie: PHPSESSID=49af82ddf12740e6a35b15985e93d91a
[...] Other irrelevant headers
I have two questions:
Is that a PHP bug? or is the behavior undefined when the client sends that same header twice?
Is there a quick workaround to make things work without having to manually parse the HTTP headers so I can read the right value of the cookie (and session) in my application? Or should I manually parse the HTTP header to set the session to its correct value?
According to the HTTP spec, a double header simply concatenates the values together with a comma, making it:
Cookie: PHPSESSID=49af82ddf12740e6a35b15985e93d91a, PHPSESSID=49af82ddf12740e6a35b15985e93d91a
PHP should be able to parse the cookies, but the behavior of sessions is undefined when there are two session IDs.
I strongly recommend fixing the client. If that's not an option, you'll have to parse the headers manually.

URL encoding seems to get in the way of properly json encoding/decoding in my PHP program

I'm implementing a PHP script which receives an HTTP POST message with in the body a json string, tied to a 'report' parameter. So HTTP POST report=.
I'm testing this out with SimpleTest (PHP Unit Testing).
I build the json:
$array = array("type" => "start"); // DEBUG
$report = json_encode($array);
I send the POST:
$this->post(LOCAL_URL, array("report"=>$json));
(calls a method in the WebTestCase class from SimpleTest).
SimpleTest says it sends this:
POST /Receiver/web/report.php HTTP/1.0
Host: localhost:8888
Connection: close
Content-Length: 37
Content-Type: application/x-www-form-urlencoded
report=%7B%22type%22%3A%22start%22%7D
I receive as such:
$report = $_POST['report'];
$logger->debug("Content of the report parameter: $report");
$json = json_decode($report);
The debug statement above gives me:
Content of the report parameter: {\"type\":\"start\"}
And when I decode, it gives the error
Syntax error, malformed JSON
The 'application/x-www-form-urlencoded' content-type is automatically selected by SimpleTest. When I set it to 'application/json', my PHP script doesn't see any parameters and as such, can't find the 'report' variable.
I suppose something is going wrong with the url encoding, but I'm lost here as to how I should get the json accross.
Also, what is the usual practice here? Does one use the key/value approach even if you just send an entire json body? Or can I just dump the json string in the body of the HTTP POST and read it out somehow? (I had no success in actually reading it out without a variable to point to).
Anyway, I hope the problem is somewhat clearly stated.
Thanks a bunch in advance.
Dieter
It sounds like you have magic quotes enabled (which is a big no-no). I would suggest you disable this, otherwise, run all your input through stripslashes().
However, it is better practice to reference the POST data as a key/value pair, otherwise you will have to read the php://input stream.
For the quick fix, try:
$report = stripslashes($_POST['report']);
Better, disable magic quotes GPC. G=Get, P=Post, C=Cookie.
In your case Post. Post values get automatically ("magic") quoted with a single slash.
Read here how to disable magic quotes.

Categories