31

I'm building a PHP RESTful API, following this tutorial. The following function, which should return the data sent with the request when the 'put' method is used, returns null every time:

file_get_contents('php://input').

I've even downloaded and tested the full code example in the tutorial and it still returns null.

I'm using cURL and the following command in order to test the 'put' method:

curl -i -X PUT -d '{"address":"Sunset Boulevard"}' http://localhost/clients/ryan.

I've wasted days on this and still haven't gotten it to read the json data. What am I doing wrong?

Igor
  • 1,532
  • 4
  • 23
  • 44
  • 1
    You aren't by any chance reading from `php://input` more than once? See the **Note** here - http://php.net/manual/wrappers.php.php#refsect2-wrappers.php-unknown-unknown-descriptiop – Phil Oct 03 '13 at 00:41
  • @Phil Nope, I tried reading just once. – Igor Oct 03 '13 at 07:58

16 Answers16

46

As noted elsewhere on the web, php://input content does not get through if request is redirected. Maybe your web server has a redirect from an URL starting with www to an URL without www, or from URLs using HTTP to URLs using HTTPS. Check your web server logs to verify this, and act accordingly to avoid the redirection when making calls to the web service.

Giulio Piancastelli
  • 15,368
  • 5
  • 42
  • 62
  • 3
    I didn't believe this could be my problem, but testing from curl confirmed there was indeed a 301 from example.com/test to example.com/test/ (added a trailing slash) :facepalm: – rymo Aug 30 '19 at 20:08
  • Yesssah, you saved my day. Been searching all over the internet and found this. Mine was redirecting from http:// to https:// through htaccess. Thanks a bunch. – Jeaf Gilbert Dec 02 '19 at 12:42
  • 1
    You're a life saver. I created an index.php file inside a folder called "extraction", and supplied the URL in Postman as `http://my-project/extraction`. Changing index.php to extract.php, and the URL to `http://my-project/extraction/extract.php` solved it for me. I deduce that a *HTTP redirect* occurs to any index.php file in a folder when only the folder name is provided in the application URL – Benny64 Jan 10 '20 at 07:21
  • Star for you. Thanks so much – Anos K. Mhazo Mar 19 '20 at 08:57
  • You are really a life saver!! Thanks Giulio – Chayan C Jul 22 '20 at 14:13
  • In my case it's another problem and i solve it by decod/encode 2 times, it's ridiculous but it works... $inputs = json_decode(file_get_contents("php://input"), true); $inputs = json_decode(json_encode($inputs)); – bArraxas Dec 19 '22 at 11:06
  • My silly web server redirect `/api/path` to `/api/path/` wow – Deepak Kamat Feb 27 '23 at 21:03
13

Make sure there are no redirects.

file_get_contents(php://input) doesn't pass the body content through redirects.

One quick way to check for redirects is to check the url with curl. On the command line: curl -IL http://example.com/api

billrichards
  • 2,041
  • 4
  • 25
  • 35
  • 1
    This really helped. A 301 redirect could be changed to a 308 (if redirects are necessary and if the requirements permit). See the documentation on MDN https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301 "It is therefore recommended to use the 301 code only as a response for GET or HEAD methods and to use the 308 Permanent Redirect for POST methods instead, as the method change is explicitly prohibited with this status." – Wilbur Omae Mar 20 '20 at 13:18
  • 1
    My http requests were being redirected to https hence dropping the form content. Thanks for the hint – Mwas Jun 22 '20 at 09:42
  • @WilburOmae does this mean that a 308 redirect passes body content? If so I think the answer should be updated to specify which redirect types don't pass body content. – billrichards May 07 '22 at 13:28
10

First off, i was able to run this code and it worked fine:

--Terminal---------
//I ran this curl request against my own php file:
curl -i -X PUT -d '{"address":"Sunset Boulevard"}' http://localhost/test.php


--PHP--------------
//get the data
$json = file_get_contents("php://input");

//convert the string of data to an array
$data = json_decode($json, true);

//output the array in the response of the curl request
print_r($data);

If that doesn't work, check the console for errors and your php settings:

  1. the curl url you used, make sure that url is actually working and not returning errors.
  2. open up another terminal / console window and run tail -f /path/to/the/php/log/file so you can actually see the output of these php calls.
  3. often people get this error: file_get_contents(file://input): failed to open stream: no suitable wrapper could be found which can indicate either a typo of the "file://input" string or the fact that allow_url_fopen is disabled in php (see #5 if unsure)
  4. make sure your code is correct, and by that I mean make sure you're not typing in incorrect arguments and things... stuff that doesn't necessarily get underlined in netbeans.
  5. remember, file_get_contents only works when allow_url_fopen is set to true in your PHP settings. thats something that is set in php.ini, but you can also change settings at run time by writing something along the lines of the following code before the other code:

    ini_set("allow_url_fopen", true);
    
Kristian
  • 21,204
  • 19
  • 101
  • 176
  • I took your code and ran it and this is all I get: `C:\Users\Igor\Desktop\curl>curl -i -X PUT -d '{"address":"Sunset"}' http://local host/server.php HTTP/1.1 200 OK Date: Thu, 03 Oct 2013 07:53:54 GMT Server: Apache/2.2.21 (Win64) PHP/5.3.8 X-Powered-By: PHP/5.3.8 Content-Length: 0 Content-Type: text/html ` Still nothing. allow_url_fopen is on too. – Igor Oct 03 '13 at 07:55
  • try running curl without `-i` flag and add the `-v` flag to see what's going on. i practically never run *amp stacks on windows, so there may be something funky going on there. also, try just outputting an arbitrary string in that response just to make sure you can see output at all. Also, did you look in your php_log to see if there were any errors occurring? – Kristian Oct 03 '13 at 16:17
  • I replaced the `-i` flag with `-v` and still got the same result. I also added print_r('error') to see if it would show and it did, but `$data` was still empty. The log file is empty too. Maybe I should reinstall wamp??? – Igor Oct 03 '13 at 18:07
  • I even installed XAMP and WAMP on another machine and ran this code and I got the empty string. Using the `-v` flag showed me that all bytes have been sent. I don't get it. – Igor Oct 03 '13 at 22:04
  • you still haven't answered the question about viewing your php error logs – Kristian Oct 03 '13 at 22:14
  • teamviewer? like the shared desktop application? – Kristian Oct 04 '13 at 16:34
  • no thank you. i wish you well on your endeavors. make sure you `tail` your logs while you work. thats the best advice i can give you. – Kristian Oct 04 '13 at 19:46
  • I have the same problem. Could you tell me if you found a solution! – Gacci Nov 22 '17 at 03:24
6

I had got this error too, my solution is to change the data format to raw.

I get it from php doc where it says

php://input is not available with enctype="multipart/form-data".

gronostaj
  • 2,231
  • 2
  • 23
  • 43
Timtcng.sen
  • 363
  • 4
  • 3
5

I wrote a headerfile with this code:

if($_SERVER["REQUEST_METHOD"] == "POST" && $_SERVER["CONTENT_TYPE"] == "application/json")
{
  $data = file_get_contents("php://input", false, stream_context_get_default(), 0, $_SERVER["CONTENT_LENGTH"]);
  global $_POST_JSON;
  $_POST_JSON = json_decode($_REQUEST["JSON_RAW"],true);

  // merge JSON-Content to $_REQUEST 
  if(is_array($_POST_JSON)) $_REQUEST   = $_POST_JSON+$_REQUEST;
}

It checks for the correct content-type and it reads only as much post input, like specified in Content-Length header. When receiving a valid JSON it created an global Array $_POST_JSON.

So you can work with your JSON-Content the similiar like you do it with url-encoded POST values.

Example:

 echo $_POST_JSON["address"];
 // or
 echo $_REQUEST["address"];
Radon8472
  • 4,285
  • 1
  • 33
  • 41
  • 3
    the function example with all parameters was useful for me: `$data = file_get_contents("php://input", false, stream_context_get_default(), 0, $_SERVER["CONTENT_LENGTH"]);` – Silvan Feb 10 '21 at 20:03
5

for me this problem started, suddenly.
i had installed ssl certificate.
when i checked the response code it showed me 301, then i realized and changed
http://
to
https://
and got all the request back with file_get_contents('php://input').

in your example curl call:
put s after http as shown below:
curl -i -X PUT -d '{"address":"Sunset Boulevard"}' https://localhost/clients/ryan

sifr_dot_in
  • 3,153
  • 2
  • 33
  • 42
3

FWIW I was using Postman to send the values. On the Body tab I should have used the "Raw" tab to send the json {"ID":1234,"Type":"Blah"} but I was foolishly using the form-data tab to put in the Key/Value which made the php://input result in NULL. Once I switched to using the raw tab with the JSON then all worked as expected.

kackleyjm
  • 595
  • 1
  • 5
  • 12
2

I had the same problem. Eventually, I found that the problem was that in the client side I didn't stringify the json object (previously I used nodejs server and it was OK). Once I did that, I received the data in $_POST (not as json), as regular parameters.

Shlomo
  • 357
  • 3
  • 11
2

Edit as you wish

function getPostObject() {
    $str = file_get_contents('php://input');
    $std = json_decode($str);
    if ($std === null) {
        $std = new stdClass();
        $array = explode('&', $str);
        foreach ($array as $parm) {
            $parts = explode('=', $parm);
            if(sizeof($parts) != 2){
                continue;
            }
            $key = $parts[0];
            $value = $parts[1];
            if ($key === NULL) {
                continue;
            }
            if (is_string($key)) {
                $key = urldecode($key);
            } else {
                continue;
            }
            if (is_bool($value)) {
                $value = boolval($value);
            } else if (is_numeric($value)) {
                $value += 0;
            } else if (is_string($value)) {
                if (empty($value)) {
                    $value = null;
                } else {
                    $lower = strtolower($value);
                    if ($lower === 'true') {
                        $value = true;
                    } else if ($lower === 'false') {
                        $value = false;
                    } else if ($lower === 'null') {
                        $value = null;
                    } else {
                        $value = urldecode($value);
                    }
                }
            } else if (is_array($value)) {
                // value is an array
            } else if (is_object($value)) {
                // value is an object
            }
            $std->$key = $value;
        }
        // length of post array
        //$std->length = sizeof($array);
    }
    return $std;
}
  • when you want call `$post->p1`, `json_decode(file_get_contents('php://input'))` return empty or null when parameters like **p1=1&p2=blabla$p3=false**. **E.g: JQUERY** `$.post(url, $('form').serialize());` _Because it expect a JSON types_ – Shanaka Madusanka Jun 28 '19 at 08:06
  • 2
    You should really take on "Return Early Principles" most `else` and `else if` can be avoided and indentation can be flattened significantly, only in rare scenarios should you ever need `else` or `else if` - change your approach :) – zanderwar Jul 18 '19 at 04:51
1

The correct function call is:

file_get_contents("php://input");

You should be getting an error message saying that php:input doesn't exist...

In any case, PUT is intended for uploading files to the server, assuming the server supports it. Usually you'd use POST with a Content-Type header appropriate to the content.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • I'm sorry I am using `file_get_contents('php://input')`, I've edited the question. – Igor Oct 02 '13 at 21:23
  • sorry again it's late and I'm tired – Igor Oct 02 '13 at 21:25
  • Looking at the notes in the [manual](http://www.php.net/manual/en/wrappers.php.php), it looks like `PUT` requests aren't really ideal for this. Try `POST` instead. – Niet the Dark Absol Oct 02 '13 at 21:25
  • Just did. It returned null once again. – Igor Oct 02 '13 at 21:28
  • 1
    But... `null` is not a valid return value for [`file_get_contents`](http://php.net/file-get-contents). It can return a string, or `false`. Never `null`. Are you sure you don't have a typo somewhere? Like maybe the variable name you use to store the string? – Niet the Dark Absol Oct 02 '13 at 21:29
  • Actually the full code is this: `$data = json_decode(file_get_contents("php://input", true));`, so that's why it returns `null`. But after that I'm doing `print_r(file_get_contents("php://input"));` and that return an empty string. – Igor Oct 02 '13 at 21:34
  • Okay, with that new information... hmm... What `content-type` header is being sent to the server? – Niet the Dark Absol Oct 02 '13 at 21:55
  • It should be application/json. I just tried adding `-H "Content-Type:application/json"` to the command and it still returns empty. – Igor Oct 02 '13 at 22:08
  • @Igor Wait, why are you passing `true` as the 2nd arg to `file_get_contents`? You really don't want to use the include path for the PHP input stream – Phil Oct 03 '13 at 00:46
  • @Phil I even tried running the code without it, still get nothing. – Igor Oct 03 '13 at 07:59
1

On Windows the combination of 'single "double" quotes' does not seem to work. Use escape for quotes in your json data (as below) & it should work

curl -X PUT -d "{\"address\":\"Sunset Boulevard\"}" http://localhost/clients/ryan
sambha
  • 1,333
  • 3
  • 17
  • 30
0

I see the problem,

You need to add filename.php in the end of the request url or need to rewrite the server rules in .htaccess file to get around this.

curl -i -X PUT -d '{"address":"Sunset Boulevard"}' http://localhost/clients/ryan/{filename.php}

replace {filename.php} with appropriate file name. :)

0
{
  "action":"get_events_by_category",
  "category_id":2
}

Your row string data must be a Double quote "" not use single ''

kishan maru
  • 31
  • 1
  • 3
  • 8
0
{"id": 1, "username":"surecoder", "password":"surecoder"}

Your key and value strings data must be a Double quote "" not use single ''

0

Check your CSRF permissions. This one got me - I was hoping for Webhooks to be posted to the server but had forgotten to turn off CSRF requirements so the server (rightly) wasn't able to post to my page. Simple issue but check that you can post content to your page if there are no redirects in place.

Antony
  • 3,875
  • 30
  • 32
0

I had the same issue, because I was using the default document, instead of specifying the php file.

/dynarax/php/index.php

vs

/dynarax/php/

Sam Washington
  • 626
  • 4
  • 12