13

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.

aladin
  • 133
  • 1
  • 1
  • 4
  • Did you try fopen("php://input", "r")? – sethcall Mar 13 '12 at 13:00
  • 1
    Awesome that you're using slim. I hope you're having good experiences. I've really enjoyed my SLIM restful api creation – Jake Mar 13 '12 at 13:05
  • [This comment](http://www.php.net/manual/en/features.file-upload.put-method.php#59720) in the manual suggests that you might need to sent the Content-Length header (not sure if cURL will do this automatically...) and use `fopen`, rather than `file_get_contents`. No idea how accurate that is, but it might be worth an attempt... – lonesomeday Mar 13 '12 at 13:14
  • curl does send the Content-Length header, and also when sending it manually, nothing changes. Also fopen doesn't work. – aladin Mar 13 '12 at 13:18

4 Answers4

14

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.

Community
  • 1
  • 1
deceze
  • 510,633
  • 85
  • 743
  • 889
  • 4
    Thank you very much, this solved my issue. I can get the raw data with $app->request->getBody(); and then do a json_decode. I wasn't aware of this method, since it isnt in the documentation. – aladin Mar 13 '12 at 13:30
  • +1 this was my problem using the restTonic PHP framework. The $this->request->data variable already held the PUT body. I had read php://input in my POST handler and couldn't see why the PUT handler couldn't use the same method to handle its body. – Neek Nov 22 '13 at 12:14
2

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".
});
Berry Langerak
  • 18,561
  • 4
  • 45
  • 58
  • 1
    This is true for url-encoded data, but not for raw json. if I send the data url encoded, I can access it via Slim's request object. doesn't work for json tough. – aladin Mar 13 '12 at 13:21
  • The issue remains; you can only read from php://input *once*, and Slim has already done that. You'll have to use Slim's request object to get your values. – Berry Langerak Mar 13 '12 at 13:24
  • 1
    Apparently the Slim Framework reads the request body upon construction, so php://input was already deleted. thx! – aladin Mar 13 '12 at 13:32
  • @aladin That was what I was trying to convey, yes. – Berry Langerak Mar 13 '12 at 13:33
0

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

GordonM
  • 31,179
  • 15
  • 87
  • 129
-1

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

Jake
  • 4,134
  • 2
  • 16
  • 20
  • The OP is using Curl to perform the HTTP PUT operation. – GordonM Mar 13 '12 at 13:05
  • I'm a bit new to this as well, but does cURL just forgo the browser issue? What if he is doing the curl request via PHP and that is the command line equivalent? (I'm not saying he is, I'm saying would the command line cURL benefit still exist) – Jake Mar 13 '12 at 13:11
  • @Jake: yes, using cURL forgoes the browser issue. No browser is involved the way he's calling the service. – WWW Mar 13 '12 at 13:21
  • curl generates raw HTTP requests and you have full control over every last detail of the request. PUT/POST is just an HTTP verb which you can set with `-X PUT` on the command line and similar flags in other wrappers. Yes, curl supports PUT, only browsers don't. – deceze Mar 13 '12 at 13:23