6

I am writing a restful api library from scratch and now I'm stuck with a common problem: Read raw data from multipart/form-data from a request.

For POST requests, I am aware that I should use $_FILE / $_POST variables. But what if there is a PUT, PATCH, or whatever request type, other than POST?

  • Is this scenario possible?
  • If so, how can I read the raw body contents, since, according to the documentation it is not avaliable in php://input?

Note: I searched about the input format and how to read it already, I just want to access the RAW data.

CarlosCarucce
  • 3,420
  • 1
  • 28
  • 51
  • i've tested and `php://input` is available with PUT request, and not seeing anything contradictious in documentation – cske Aug 12 '16 at 08:00
  • "according to the documentation it is not available in php://input" — I can't see where it says that in the documentation. – Quentin Aug 12 '16 at 10:10
  • check this link if it is helpful, [link](http://stackoverflow.com/questions/9464935/php-multipart-form-data-put-request). – Raunak Gupta Aug 12 '16 at 10:17
  • 1
    Hi. Please, check this out: `php://input is not available with enctype="multipart/form-data".` in the end of this paragraph: http://php.net/manual/en/wrappers.php.php#wrappers.php.input – CarlosCarucce Aug 12 '16 at 11:15
  • @cske What server/php version are you running ? – CarlosCarucce Aug 12 '16 at 11:16
  • [here it is](https://gist.github.com/anonymous/b424aa4d786a70a75e601652277d156e) – cske Aug 12 '16 at 11:32
  • 1
    @CarlosCarucce, if you are using apache there is a trick [here](http://stackoverflow.com/questions/1361673/get-raw-post-data) to change the "multipart/form-data" in order to force php to get the actual data (so you can read it with `php://input`). Since you said you are developing an open source lib I don't think you would like that solution, but it's worth reading. – Dekel Aug 12 '16 at 15:31
  • @Dekel Wow I didn't even know that it is possible to change the request body like that through apache. Cool trick – CarlosCarucce Aug 12 '16 at 16:49
  • @CarlosCarucce, yesterday night I thought about adding this as an answer here, but it's only a dup of another answer here on SO. – Dekel Aug 12 '16 at 16:50

3 Answers3

9

But what if there is a PUT, PATCH, or whatever request type, other than POST?

Well, since you are the one designing the API, then you are the one who decides whether it accepts only POST, PUT, POST + PUT or any other combination of request headers.

The API should not be designed to "accept-and-try-to-handle" everything a third-party app is submitting to your API. It's the app's job (I mean, the app that connects to API) to prepare a request in such a way, that the API accepts it.

Keep in mind, that enabling multiple request methods (especially those which have to be treated differently) comes with multiple ways to treat a request (e.g. security, types etc). It basically means that either you have to smartly design a request-handling process, or you'll encounter problems with API methods called with different request-types differently, which will be troublesome.

If you need to get raw contents of the request - @Adil Abbasi seems to be on the right track (as far as parsing php://input is concerned). But note that php://input is not available with enctype="multipart/form-data" as described in the docs.

<?php
$input = file_get_contents('php://input');
// assuming it's JSON you allow - convert json to array of params
$requestParams = json_decode($input, true);
if ($requestParams === FALSE) {
   // not proper JSON received - set response headers properly
   header("HTTP/1.1 400 Bad Request"); 
   // respond with error
   die("Bad Request");
}

// proceed with API call - JSON parsed correctly

If you need to use enctype="multipart/form-data" - read about STDIN in I/O Streams docs, and try it like this:

<?php
$bytesToRead = 4096000;
$input = fread(STDIN, $bytesToRead ); // reads 4096K bytes from STDIN
if ($input === FALSE) {
   // handle "failed to read STDIN"
}
// assuming it's json you accept:
$requestParams = json_decode($input , true);
if ($requestParams === FALSE) {
   // not proper JSON received - set response headers properly
   header("HTTP/1.1 400 Bad Request"); 
   // respond with error
   die("Bad Request");
}
Kleskowy
  • 2,648
  • 1
  • 16
  • 19
  • Thank you for I/O Stream references. Actually, I am writing an open source library, and [this](https://github.com/Corviz/framework/blob/master/src/Http/RequestParser/MultipartFormDataParser.php#L21) is how I am handling 'multipart/form-data' requests... As it could be used in third-party apps, I would like to give support to those requests. – CarlosCarucce Aug 12 '16 at 11:48
  • 1
    As I said - it is your choice to do that, and perhaps it's a reasonable one. Just remember to carefully design the request-handling process, and you'll be fine. – Kleskowy Aug 12 '16 at 11:51
1

PUT data comes from stdin, just parse the raw data to a variable:

        // Parse the PUT variables
        $putdata = fopen("php://input", "r");
        $put_args    = parse_str($putdata);

Or like :

        // PUT variables
        parse_str(file_get_contents('php://input'), $put_args);
Amar Pratap
  • 1,000
  • 7
  • 20
0

you can utilize this code snippet, to get put params in array format.

  $params = array();
  $method = $_SERVER['REQUEST_METHOD'];

  if ($method == "PUT" || $method == "DELETE") {
      $params = file_get_contents('php://input');

      // convert json to array of params
      $params = json_decode($params, true);
  }
Adil Abbasi
  • 3,161
  • 1
  • 40
  • 35
  • reasons of downvaote ? – Adil Abbasi Aug 12 '16 at 10:11
  • @CarlosCarucce please try this updated code snippet, working fine for me :) – Adil Abbasi Aug 12 '16 at 10:44
  • Hi. Very interesting, but, what about 'multipart/form-data' requests?? BTW, I am sorry, but wasn't me who downvoted it. – CarlosCarucce Aug 12 '16 at 11:29
  • I will suggest that it will be good to use base64 encoded (image data), probably you are looking to upload image on this put call. – Adil Abbasi Aug 12 '16 at 11:49
  • I am writing a request parser that could receive any kind of content (Assuming it receives a complete form, not just a file contents). When it is a file, it would create an 'UploadedFile' object representation. When it countain input data, it is parsed to an array like php itself does for POST requests – CarlosCarucce Aug 12 '16 at 11:57