PHP get PUT request body - php

I'm currently developing a Restful Json-API in PHP. I want to send a PUT-Request to items/:id to update a record. The data will be transferred as application/json.
I want to call the API with
curl -H "Content-Type: application/json" -X PUT -d '{"example" : "data"}' "http://localhost/items/someid"
On the server side, I'm not able the retrieve the request body. I tried
file_get_contents("php://input");
but this returns an empty string. Also a fopen()/fread() combination doesn't work.
When calling via POST, everything works great, I can read the json perfectly on the server side. But the API isn't Restful anymore. Does anyone have a solution for this? Is there another way to send and receive Json?
btw, I'm developing the API with the Slim Framework.

php://input is only readable once for PUT requests:
Note: A stream opened with php://input can only be read once; the stream does not support seek operations. However, depending on the SAPI implementation, it may be possible to open another php://input stream and restart reading. This is only possible if the request body data has been saved. Typically, this is the case for POST requests, but not other request methods, such as PUT or PROPFIND.
http://php.net/manual/en/wrappers.php.php
The Slim framework already reads the data upon request. Take the data from the Request object, into which it has been read.

On the server side, I'm not able the retrieve the request body. I tried file_get_contents("php://input");
You can only use file_get_contents( 'php://input', 'r' ); once per request. Retrieving its values will truncate the values as well, so if you call it twice, it'll return an empty string. Slim's request object contains the values you need, so:
<?php
$app = new Slim( );
$app->put( '/items/someid', function () use ( $app ) {
echo $app->request( )->put( 'example' ); // should display "data".
});

The example from the PHP manual uses fopen to access php://input in read mode. Have you tried doing it that way instead?
EDIT: The manual page for PHP:// says some stuff that seems to suggest that PUT data might not be available in some cases!
Note: A stream opened with php://input can only be read once; the
stream does not support seek operations. However, depending on the
SAPI implementation, it may be possible to open another php://input
stream and restart reading. This is only possible if the request body
data has been saved. Typically, this is the case for POST requests,
but not other request methods, such as PUT or PROPFIND.
I don't know where this will leave you regarding PUT processing. One page seems to say it's possible, the other seems to imply that it won't work under the wrong set of circumstances

I was reading the SLIM framework documentation the other day and it said that some browsers have problems with PUT and DELETE.
Excerpt:
Unfortunately, modern browsers do not provide native support for PUT requests. To work around this limitation, ensure your HTML form’s method is “post”, then add a method override parameter to your HTML form like this:
<form action="/books/1" method="post">
... other form fields here...
<input type="hidden" name="_METHOD" value="PUT"/>
<input type="submit" value="Update Book"/>
</form>
Source: http://www.slimframework.com/documentation/stable

Related

file_get_contents('php://input') returning empty string with a PATCH request

My environment is PHP 5.5.9, Nginx 1.4.6-1ubuntu3.2, localhost.
I'm trying to get data from PATCH method, but that just return an empty string...
With POST method that's work fine, this is a piece of my script :
case 'POST':
case 'PATCH':
$this->data = file_get_contents("php://input");
$this->data is empty when PATCH method is used and complete when POST, i use the POSTMAN chrome extension and i push RAW data (not multipart/form-data)
I think Nginx was in fault...but nothing into the log file...
Any help will be much appreciated !
I recently ran into a similar issue trying to access php://input for a PUT request. The issue was simply that I had already accessed it once previously (in a logging function that ran prior to the code in question).
POST acts differently than the other methods, which explains the discrepancy:
Note: Prior to PHP 5.6, a stream opened with php://input could only be read once; the stream did not support seek operations. However, depending on the SAPI implementation, it may be possible to open another php://input stream and restart reading. This is only possible if the request body data has been saved. Typically, this is the case for POST requests, but not other request methods, such as PUT or PROPFIND.
source: http://php.net/manual/en/wrappers.php.php
The solution was simple: Store the value of php://input from the initial grab to a PHP variable, and don't try to run file_get_contents("php://input") more than once for a non-POST request.

HTTP protocol's PUT and DELETE and their usage in PHP

Introduction
I've read the following:
Hypertext Transfer Protocol (HTTP) is the life of the web. It's used every time you transfer a document, or make an AJAX request. But HTTP is surprisingly a relative unknown among some web developers.
The HTTP verbs comprise a major portion of our “uniform interface” constraint and provide us the action counterpart to the noun-based resource. The primary or most-commonly-used HTTP verbs (or methods, as they are properly called) are POST, GET, PUT, and DELETE.
Huh?
Well, we came to the point I lost track of things.
PUT and DELETE, they say. I've only ever heard of POST and GET and never saw something like $_PUT or $_DELETE passing by in any PHP code I've ever viewed.
My question
What are these methods (PUT) and (DELETE) for and if it's possible to use them in PHP, how would I go about this.
Note: I know this is not really a problem but I always grab a learning opportunity if I see one and would very much like to learn to use these methods in PHP if this is possible.
What are these methods (PUT) and (DELETE) for...
There are a lot of words to spend to explain this, and I'm not skilled enough to do it, but as already posted, a quick recap of what the HTTP specification describes.
The protocol basically says this:
use GET when you need to access a resource and retrieve data, and you don't have to modify or alter the state of this data.
use POST when you need to send some data to the server. Ex. from a form to save these data somewhere.
use HEAD when you need to access a resource and retrieve just the Headers from the response, without any resource data.
use PUT when you need to replace the state of some data already existing on that system.
use DELETE when you need to delete a resource (relative to the URI you've sent) on that system.
use OPTIONS when you need to get the communication options from a resource, so for checking allowed methods for that resource. Ex. we use it for CORS request and permissions rules.
You can read about the remaining two methods on that document, sorry I've never used it.
Basically a protocol is a set of rules you should use from your application to adhere to it.
... and if it's possible to
use them in PHP, how would I go about this.
From your php application you can retrieve which method was used by looking into the super global array $_SERVER and check the value of the field REQUEST_METHOD.
So from your php application you're now able to recognize if this is a DELETE or a PUT request, ex. $_SERVER['REQUEST_METHOD'] === 'DELETE' or $_SERVER['REQUEST_METHOD'] === 'PUT'.
* Please be also aware that some applications dealing with browsers that don't support PUT or DELETE methods use the following trick, a hidden field from the html form with the verb specified in its value attribute, ex.:
<input name="_method" type="hidden" value="delete" />
Follow an example with a small description on a possible way to handle those 2 http requests
When you (your browser, your client) request a resource to an HTTP server you must use one of the method that the protocol (HTTP) accepts. So your request needs to pass:
A METHOD
An Uri of the resource
Request Headers, like User-Agent, Host, Content-Length, etc
(Optional body of the request)
Now, while you would be able to get data from POST and GET requests with the respective globals ($_GET, $_POST), in case of PUT and DELETE requests PHP doesn't provide these fast access globals; But you can use the value of $_SERVER['REQUEST_METHOD'] to check the method in the request and handle your logic consequently.
So a PUT request would look like:
PUT /something/index.php
(body) maybe=aparameter
and you can access those data in PHP by reading the php://input stream, ex. with something like:
if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
$myEntireBody = file_get_contents('php://input'); //Be aware that the stream can only be read once
}
and a DELETE request would look like:
DELETE /something/index.php?maybe=aparameter
and again you can build your logic after have checked the method:
if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
// do something
}
Please pay attention that a DELETE request has no Body and pay very attention to Response Status Code too (ex. if you received a PUT request and you've updated that resource without error you should return a 204 status -No content-).
Way to use PUT data from PHP:
$method = $_SERVER['REQUEST_METHOD'];
if ('PUT' === $method) {
parse_str(file_get_contents('php://input'), $_PUT);
var_dump($_PUT); //$_PUT contains put fields
}
PHP's $_GET and $_POST are poorly named. $_GET is used to access the values of query string parameters, and $_POST lets you access the request body.
Using query string parameters is not limited to GET requests, and other kinds of requests than just POST can come with a request body.
If you want to find out the verb used to request the page, use $_SERVER['REQUEST_METHOD'].
Most suitable place to use these (PUT and DELETE) methods is REST API. Where we use http methods to define the mode of operation for example you want to fetch any resources then you can use following:
GET http://api.example.com/employee/<any_id>
to add a new item:
POST http://api.example.com/employee/
to Update or Edit:
PUT http://api.example.com/employee/
to Delete an existing resource:
DELETE http://api.example.com/employee/1
etc.
Now on PHP side you just need to read what HTTP method used so that you can make an action according to that.
There are lots of libraries available which can do that for you.
What are these methods (PUT) and (DELETE)
There are described in the HTTP spec.
In a nutshell, and simplifying somewhat, PUT is for uploading a file to a URL and DELETE is for deleting a file from a URL.
never sawy something like $_PUT or $_DELETE passing by in any PHP code I've ever viewed
$_POST and $_GET are terribly named superglobals. $_POST is for data parsed from the request body. $_GET is for data parsed from the URL. There's nothing that strictly ties data in either of those places (especially the URL) to a particular request method.
DELETE requests only care about the URL's path, so there is no data to parse.
PUT requests usually care about the entire request body (not a parsed version of it) which you would access with file_get_contents('php://input');.
for and if it's possible to use them in PHP, how would I go about this.
You'd need to map the URL onto a PHP script (e.g. with URL rewriting), test the request method, work out what URL you were actually dealing with, and then write code to do the appropriate action.
$GLOBALS["_PUT"]=null;
if($_SERVER['REQUEST_METHOD'] == 'PUT') {
$form_data= json_encode(file_get_contents("php://input"));
$key_size=52;
$key=substr($form_data, 1, $key_size);
$acc_params=explode($key,$form_data);
array_shift($acc_params);
array_pop($acc_params);
foreach ($acc_params as $item){
$start_key=' name=\"';
$end_key='\"\r\n\r\n';
$start_key_pos=strpos($item,$start_key)+strlen($start_key);
$end_key_pos=strpos($item,$end_key);
$key=substr($item, $start_key_pos, ($end_key_pos-$start_key_pos));
$end_value='\r\n';
$value=substr($item, $end_key_pos+strlen($end_key), -strlen($end_value));
$_PUT[$key]=$value;
}
$GLOBALS["_PUT"]=$_PUT;
}
if (!function_exists("getParameter")){
function getParameter($parameter)
{
$value=null;
if(($_SERVER['REQUEST_METHOD'] == 'POST')&& (isset($_POST[$parameter]))){
$value=$_POST[$parameter];
}
else if(($_SERVER['REQUEST_METHOD'] == 'PUT')&& (isset($GLOBALS["_PUT"][$parameter])))
{
$value=$GLOBALS["_PUT"][$parameter];
}
else if(($_SERVER['REQUEST_METHOD'] == 'DELETE')&& (isset($_DELETE[$parameter]))){
$value=$_DELETE[$parameter];
}
else if(($_SERVER['REQUEST_METHOD'] == 'PATCH')&& (isset($_PATCH[$parameter]))){
$value=$_PATCH[$parameter];
}
else if(isset($_GET[$parameter])){
$value=$_GET[$parameter];
}
return $value;
}
}

Save binary request body as file

I wrote a program that reads a binary file into the RAM and then sends it using an HTTP request to my server. It uses the PUT method and the binary file is (in) the body.
Now how can I tell my server to receive and safe the file in a folder?
If possible without any additional libraries that I would need to download (unless it's more efficient).
I know, there are some similar threads to this one, but they either they where about receiving text or they were about doing it with libraries or there simply was no sufficient answer.
I'd also like to know, if it would be more efficient or smarter to use the POST method or any other instead of PUT.
You can get at the data by opening a stream to php://input, like so:
$datastr = fopen('php://input',rb);
if ($fp = fopen('outputfile.bin', "wb")){
while(!feof($datastr)){
fwrite($fp,fread($datastr,4096)) ;
}
}
As to whether to use POST or anything else depends on what is happening with the data, and whether you care about being RESTful or such. See other questions/answers, indempotency.
The advantage I would see with using POST is that it's more commonly used (on most submission forms where you upload a file), and therefore has more support from within PHP and html.

php://input returns empty

I have Backbone application. When model updates PUT request goes to my server.
Client-side looks good. But on the server side (PHP) I have some trouble. When I first time test this request in PHPStorm:
file_get_contents('php://input')
return what I expect. But on the other times it always return empty. I restarted Apache - nothing helps.
I know that
Note: A stream opened with php://input can only be read once;
But I thinked that mean one by request. Not once per life :) Where is my mistake?
It was Kohana problem. In Kohana_Request class there is piece of code, that already opened php://input stream
if ($method !== HTTP_Request::GET)
{
// Ensure the raw body is saved for future use
$body = file_get_contents('php://input');
}
You should be using $this->request->body() to get the request body.

Receiving and handling a GET request in PHP

I'm trying to replace RSS polling with PubSubHubbub on my site. I'm able to use the subscriber library that google offers to send the subscription request. From the code it looks like it sends a post request via cURL with the RSS URL and a callback URL.
So this is where I need some direction:
In order to complete the subscription request my callback URL has to receive a GET request and then echo back a value from the GET request along with a 200 response. How do I get the parameters from the GET request? Is the echo done again via cURL? If so what option should include the 200 response?
This very simple script should be a start:
echo $_GET["request_name"];
this will output the GET parameter request_name and (implicitly) send a 200.
It's also a good idea to explicitly declare a content type before echoing, to prevent the default content type (usually "text/html") from kicking in:
header("Content-type: text/plain");
Note that when echoing external data, you may need to sanitize the output first - if the for example the output format is HTML, you would want to do something like echo htmlspecialchars($_GET["request_name"]); to prevent Cross-Site Scripting.
There was recently a thread on the php-dev mailing list about this. The reason you can't access 'hub.challenge' in the $_GET superglobal is due to register_globals. Basically PHP cleans up any argument names before creating the superglobals. Any dots will be converted to underscores. It's looking to be 'fixed' in PHP 6, but not before due to BC issues.
Here's the thread about it.

Categories