php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #55815 PUT request data should be parsed just like POST
Submitted: 2011-09-29 16:51 UTC Modified: 2024-02-08 11:19 UTC
Votes:389
Avg. Score:4.7 ± 0.6
Reproduced:361 of 363 (99.4%)
Same Version:77 (21.3%)
Same OS:215 (59.6%)
From: catch dot dave at gmail dot com Assigned: ilutov (profile)
Status: Closed Package: Streams related
PHP Version: 5.4.0beta1 OS: All
Private report: No CVE-ID: None
 [2011-09-29 16:51 UTC] catch dot dave at gmail dot com
Description:
------------
Data that is posted to PHP via the PUT method is not parsed at all and is not 
available to PHP.
This is particularly problematic for data sent encoded as 'multipart/form-data'.

Basically, a request sent (with files and/or non-file data) via PUT should be 
parsed using the same functions used for requests sent via 
POST.
Ideally, $_FILES and *either* $_POST or a new $_PUT superglobals should be 
populated in PUT requests.


The answer is *not* to simply use parse_str() because that does not handle 
multipart/form-data requests.

This is something that would help every RESTful interface that people are trying 
to do with PHP.
There are many people who have these problems and have to implement (usually 
incomplete and/or buggy) PHP solutions, 
eg: 
* Example of someone's wrong and incomplete solution: 
http://stackoverflow.com/questions/5483851/manually-parse-raw-
http-data-with-php
* Example of other people having problems: http://drupal.org/node/1270190


I ended up having to write half a page of code just to parse file and normal 
data out of php://input stream which is not going to be as 
well tested or as stable as PHP's existing C code that parses the data when 
using the POST method.


This could (possibly) be as simple as changing lines such as:
    `if(!strcmp(SG(request_info).request_method, "POST"))`
to
   `if(!strcmp(SG(request_info).request_method, "POST") || 
!strcmp(SG(request_info).request_method, "PUT"))`

or even adding a new superglobal called $_PUT (but still re-using $_FILES).


Test script:
---------------
The request:

#!/bin/sh
curl "https://localhost/restful_server/" -X PUT -F "photo=@my_image.jpg;type=image/jpg" -F "foo=bar"

The php code:

<?php
echo "POST: \n"; var_dump($_POST);
echo "PUT: \n"; @var_dump($_PUT); // obviously this won't exist yet in php 5.4/5.3
echo "FILES: \n"; var_dump($_FILES);



Expected result:
----------------
POST: 
	array(0) {
}
PUT: 
	array(1) {
  ["foo"]=>
  string(3) "bar"
}
FILES: 
	array(1) {
  ["photo"]=>
  array(5) {
    ["name"]=>
    string(6) "my_image.jpg"
    ["type"]=>
    string(9) "image/jpg"
    ["tmp_name"]=>
    string(26) "/private/var/tmp/my_image.jpg"
    ["error"]=>
    int(0)
    ["size"]=>
    int(1)
  }
}

Actual result:
--------------
POST: 
	array(0) {
}
PUT: 
	NULL
FILES: 
	array(0) {
}

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-12-29 17:47 UTC] thomas dot mery at gmail dot com
Hi,

was wondering if this had been considered

thanks
 [2012-04-11 01:52 UTC] theanomaly dot is at gmail dot com
First, I'd like to start by noting that the PHP manual already addresses file 
uploads via PUT method by: 

http://php.net/manual/en/features.file-upload.put-method.php

Second, I want to point out that what you're asking is actually a violation of 
the HTTP specification as per RFC 2616 specifically Section 9.6

http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6

"The fundamental difference between the POST and PUT requests is reflected in 
the different meaning of the Request-URI. The URI in a POST request identifies 
the resource that will handle the enclosed entity. That resource might be a 
data-accepting process, a gateway to some other protocol, or a separate entity 
that accepts annotations. In contrast, the URI in a PUT request identifies the 
entity enclosed with the request -- the user agent knows what URI is intended 
and the server MUST NOT attempt to apply the request to some other resource. If 
the server desires that the request be applied to a different URI, it MUST send 
a 301 (Moved Permanently) response; the user agent MAY then make its own 
decision regarding whether or not to redirect the request."

What this means is the HTTP RFC does not allow us to identify multipart/form-
data in a PUT request.

Thus your proposed implementation to how PHP already handles requests would then 
put PHP in a position of having not complied with the spec. PUT is used for a 
different specification than POST and thus modifying PHP to treat them equally 
would not be a sane solution.

However, there are some simple steps to accomplishing what you want even though 
it would be in violation of the HTTP specification. Namely they would be:

1) You can detect whether or not PUT was the REQUEST method via 
$_SERVER['REQUEST_METHOD'] for example (depends on your SAPI).
2) You may then proceed to process the 'php://input' stream through something 
like file_get_contents('php://input'); - as an example.
3) You may then proceed to break up the multipart form data according to its 
defined boundaries in PHP user-space code -- according to the specification at 
http://tools.ietf.org/html/rfc2388
4) Finally you may chose to do with the file as you'd like (save, discard, 
otherwise).

However, it would make no sense at all to create a new superglobal in PHP called 
$_PUT since the HTTP PUT verb does not allow for multi-part/form data to begin 
with. So whoever is saying PHP is making it difficult to be RESTful is 
misleading.
 [2012-04-16 19:15 UTC] catch dot dave at gmail dot com
Hi theanomaly,

I'd like to address your points.

1. As per my original request the manual page of 
"http://php.net/manual/en/features.file-upload.put-
method.php" *does not* solve the problem, as it does not handle multipart form 
requests.

2. Implementing the parsing of multipart form data in PHP seems to be re-
inventing the wheel when the 
code to correctly parse this already exists in PHP itself (to parse POST data).

3. Perhaps I am misunderstanding, but reading through both your quote and the 
rest of the 2616 spec, I 
fail to see how the spec precludes handling form-data in a PUT method, The 
section you quoted is saying 
that:
      * PUT must operate on the URI provided and not another one; and
      * that the difference between PUT and POST, is that POST URI defines the 
resource that operates on 
an entity, whereas a PUT request's URI identifies the entity itself. It is 
common to use the PUT verb to 
operate on existing entities in restful APIs (e.g. modifying the title of an 
existing book, you would send 
the new title as data, and not in the URI itself), which does not seem to 
violate this point either.

The multipart form data is part of the original user request and should 
therefore be parseable--the user 
is sending the data (in multipart form) to operate on the URI using the PUT 
method.

I don't think the spec is saying that you are not allowed to use/consider the 
data sent along with it--
as opposed to your interpretation which suggests the RFC is saying that all data 
other than the URI 
itself (whether multipart form encoded or not) should be ignored.

4. I had originally had some code that does exactly what you described, but it 
was nowhere near as 
robust as the existing code in PHP, which lead to me writing this request (re-
invent the wheel). 
Furthermore, other languages (e.g. Ruby) do parse multipart data in PUT (not 
that that should be the 
primary reason).
 [2012-06-27 00:57 UTC] phazei at gmail dot com
Has this been reconsidered at all? Any update?
 [2012-07-08 18:38 UTC] johnston dot joshua at gmail dot com
Hi,

in regards to RFC 2616 and the comment:

"What this means is the HTTP RFC does not allow us to identify multipart/form-
data in a PUT request."

This section addresses the meaning of the request URI and NOT the request body. 

In a POST request, the request URI should point to an entity that HANDLES the 
request body in whichever way it sees fit. This COULD be by appending the 
enclosed entity as an annotation to the given request URI, or appending a new 
entity to a collection, etc.

In a PUT request, the enclosed entity should BE STORED AT or REPLACE the entity 
that exists at the given request URI.

There is no reason why the Entity in question cannot be a multipart entity.

Here is a real-world example to illustrate this point using a messageboard as an 
example.

A User creates a new topic:

(minimal headers shown for brevity)

POST /topics HTTP/1.1
Content-Type: multipart/form-data; boundary=--------------------------
-11401160922046879112964562566
Content-Length: ???
-----------------------------11401160922046879112964562566
Content-Disposition: form-data; name="comment"

This is a comment here
-----------------------------11401160922046879112964562566
Content-Disposition: form-data; name="attachment"; filename="attachment.png"
Content-Type: image/png

[binary image data here]
-----------------------------11401160922046879112964562566--


HTTP/1.1 201 Created
Location: /topics/1


Now we have a new topic referenced by /topics/1. Lets say the original user wants 
to change his topic. HTTP says that you can use a PUT request to a given request 
URI to replace an entity. If I wanted to replace the entity containing an image 
and the comment I would issue

PUT /topics/1 HTTP/1.1
Content-Type: multipart/form-data; boundary=--------------------------
-11401160922046879112964562566
Content-Length: ???
-----------------------------11401160922046879112964562566
Content-Disposition: form-data; name="comment"

This is a comment here
-----------------------------11401160922046879112964562566
Content-Disposition: form-data; name="attachment"; filename="attachment.png"
Content-Type: image/png

[binary image data here]
-----------------------------11401160922046879112964562566--


As you can see here, the multipart/form-data request body is 100% valid.
 [2012-10-22 05:23 UTC] tylerromeo at gmail dot com
The http/1.1 RFC does not specify any data type for the request body of any 
request type, nor does the RFC for multipart/form-data specify the request type 
it must be used with in HTTP. And as has been demonstrated by previous comments, 
there exist legitimate cases where multipart/form-data would be useful in a PUT 
request.

Let me first say that putting PUT data into $_POST is a bad idea. Hopefully that 
is obvious enough. If this were to be implemented, it should use $_PUT (and 
$_FILES, if necessary, along with it).

The real question that needs to be asked is whether it's worth implementing. 
Technically, POST requests do not have any restriction on data types either. So 
really we could just tell all web developers to parse their POST requests from 
stdin like is suggested here for PUT. The reason PHP doesn't do that is because 
POST data is so often encoded in a standard data format and used as such that it 
helps developers tremendously to not force them to do such transformations 
themselves.

So the issue is whether enough users will be using multipart/form-data in PUT 
requests to warrant developers implementing a feature for it. Personally, I'm a 
fan of uniformity, and I believe that if we're parsing the request body for a 
POST request, then a PUT request should be treated no differently unless the 
spec has a restriction (which it doesn't).
 [2012-10-31 15:09 UTC] evert at rooftopsolutions dot nl
I just wanted to chime in this one..

I personally think that while having a _POST superglobal is convenient (and needed, for BC) it's not necessarily a great design pattern. Especially in the case where you actually want access to php://input, but PHP pre-emptively decided to parse it, and I'm left with an empty stream.

So instead of extending this pattern to other HTTP methods (and don't kid yourself, there's a bunch more than just PUT [1]), I feel a much better alternative would be to expose the API that can actually parse multipart/form-data and takes a stream as input.

I feel this would solve the OP's use-case, is more flexible, and avoids the creation of additional evil super-globals.

  [1] http://tools.ietf.org/html/draft-ietf-httpbis-method-registrations-10
 [2012-10-31 16:38 UTC] catch dot dave at gmail dot com
Whilst evert's example of providing access to the API might solve my original 
issue, I would still lean 
towards creating a new superglobal called _PUT.

1. PHP already has too many inconsistencies, let's not introduce another one. 
POST, GET exist, adding PUT 
is an obvious and consistant addition. Unless you wanted to deprecate existing 
superglobals over time, I 
would strongly suggest against providing a new way to do the same thing.
2. Whilst there are other HTTP methods, very few of them require/support parsing 
multi-form data like POST 
and PUT do (think of PATCH, DELETE, HEAD, etc).
 [2013-04-17 01:50 UTC] joaoh88 at gmail dot com
I really think it would be very useful to expose the API for parsing multipart 
data, as restful services are increasing in popularity
 [2014-10-20 17:21 UTC] tad at tad-carlucci dot com
The issue is not constrained to PUT.

The HTTP specifications place no requirements upon the request body for ANY request methods (other than OPTIONS and CONNECT).

The HTTP **ALLOWS** ANY request method which parses as a 'token'. In fact, it specifically states that the methods listed (ie., GET, POST, etc.) are neither required nor the exclusive list.

It is quite possible, and fully compliant with the RFCs, for example, for a GET method to present a request body containing multipart/form, or application/json, or even a request body using a private MIME type.

Nor, I will admit that adding a request body to GET is unexpected. And, if one were to do so, support from normal browsers should not be expected. But lack of support by any given client software does NOT imply non-compliance. So, while it should be obvious, even though not explicitly specified in the RFCs, an origin server MAY process request bodies presented with GET, but SHOULD NOT require a request body, and SHOULD present a meaningful response when no request body is present. For proper cache control, such a server SHOULD include a Vary header in the response so that clients and proxies will be informed that the response to such a GET might vary depending upon the content type, etc.

For maximum compliance, and support, of the HTTP RFCs. PHP SHOULD process all superglobals regardless of request method. Specifically, $_GET, $_POST, $_REQUEST and $_FILES SHOULD be fully processed, regardless of the actual request method.
 [2014-10-20 17:41 UTC] googleguy@php.net
I think I have a better solution that should satisfy everyone involved.

I'm going to propose an RFC for PHP 7 that enables PHP user-land to implement an HttpRequest interface, which will make it possible to override PHP's normal behavior for handling the incoming HTTP request in whatever way they deem necessary.

This will mean doing away with GPCS superglobals altogether and have an HttpRequest object that will handle the entire request process. Superglobal variables like $_GET and $_POST are confusing and misleading, because they don't actually speak to the HTTP request VERB used in the request. So the actual request processing should be delegated to a class that implements an HttpRequest interface instead and the request can be handled directly by that class. To maintain default behavior we should implement a default HttpRequest class and people will be able to extend that class to override default behaviors (such as in the case of wanting to handle PUT requests differently than PHP handles them right now).

Since this breaks backwards compatibility in a major way I think the proposed changes for PHP 7 should be OK.
 [2015-04-13 16:20 UTC] mike@php.net
See also https://pecl.php.net/package/apfd
 [2015-05-31 04:08 UTC] drew at funkhaus dot us
Ember Data uses PUT when updating a REST API endpoint. It sends it as form-data.

PHP not working with PUT or DELETE means you can't use Ember Data, and thus big parts of the Ember framework.

I'd love it if PHP had someway of parsing PUT or the other HTTP methods. Currently I have to write a bunch of regex, which sucks.
 [2015-10-02 13:01 UTC] carsten at bleicker dot de
Parse body for any kind of requests.
Keep old superglobals, make them deprecated [get,post]
Introduce http_request_body_raw, http_request_body_parsed, http_request_arguments.
Its realy bad currently having no chance getting the request body for a patch request, f.e.
we definitly need proper http handling for the http-api world.
 [2018-10-05 08:23 UTC] a at anze dot com
Was there any development to allow multipart/form-data on PATCH, PUT, DELETE.. type of requests?

PHP is not suitable for RESTful API development not having this supported.
 [2019-01-19 15:50 UTC] arkemlar at gmail dot com
The but exising over 8 years! Really, for me it sounds like:
- Me: Hey PHP, I want to implement nice restful API
- PHP: OK
... over time ...
- Me: What? I cant use PUT to modify my entity? WTF dude?
- Reqeust parsing for PUT is not implemented
- Why?
- Because maintainers __believe__ it should not be done for PUT
- This literally means: go and f**k urself man becasue now I forced to broke my perfectly designed API by using POST whilest it must be PUT.

In age of RESTful API we cant simply use PUT, perfect!
 [2019-07-05 22:17 UTC] jasny@php.net
How ca, this be solved?
----

Change the type of ini setting `enable_post_data_reading` to a string. This setting takes a comma-separated list with methods. E.g. "POST,PUT,PATCH".

For BC also allow a boolean: `true` is "POST" and `false` is "".

---
This will likely need to be proposed as RFC rather than a simple patch.
 [2019-07-06 00:01 UTC] requinix@php.net
-Status: Open +Status: Suspended
 [2019-07-06 00:01 UTC] requinix@php.net
This needs to go through the RFC process. https://wiki.php.net/rfc/howto
 [2020-06-24 14:29 UTC] me at daz dot one
I know this has been said MANY times but I can't believe this is still not implemented.

I get that it may not have been so essential in 2011 but in 2020 we now live in an age of REST APIs.

Having to break RESTful convention due to this feature not being implemented is literally crazy.

Is there anything I (or anybody else that needs this) can do to speed up getting it implemented?

Thanks.
 [2020-06-25 13:55 UTC] me at daz dot one
I've been trying to figure out how to work with this issue without having to break RESTful convention and boy howdie, what a rabbit hole, let me tell you.

I'm adding this anywhere I can find in the hope that it will help somebody out in the future.

I've just lost a day of development firstly figuring out that this was an issue, then figuring out where the issue lay.

After trawling through a good few RFCs for php core, the core development team seem somewhat resistant to implementing anything to do with modernising the handling of HTTP requests. The issue was first reported in 2011, it doesn't look any closer to having a native solution.

That said, I managed to find a PECL extension called apfd (always populate form data). I'm not really very familiar with pecl, and couldn't seem to get it working using pear. but I'm using CentOS and Remi PHP which has a yum package.

I ran: 
yum install php-pecl-apfd 

and it literally fixed the issue straight away (well I had to restart my docker containers but that was a given).

I believe there are other packages in various flavours of linux and I'm sure anybody with more knowledge of pear/pecl/general php extensions could get it running on windows or mac with no issue.

If you're looking for a clean fix for this issue, I'd personally advise taking a look.
 [2022-02-05 18:38 UTC] svaneekelen at avt dot nl
It is unbelievable that this is still not implemented after 11 years. This is one of the main reasons why people leave php and consider it a old language. I think I don't need to repeat what the other members on this form said but this is ridiculous there is no reason to not implement a fix for this on the php side. The least that can be done is to upload in this case the file to $_FILES what doesn't even happen in php 7.4 with put request. While it seems that the php dev team does not even want to burn their fingers on this massive bug for 2 days because that is what it would take because the logic exists already elsewhere. The least that php should do is bring in a stable solution to read these files because this is basic functionality.
 [2023-01-05 10:07 UTC] cyril dot garsaud at gmail dot com
PHP 8.2 has been released, with no fixes for this critical issue. Developers are forced to find workarounds and move away from REST guidelines because of this bug. Eventually, they might abandon the PHP language...
 [2023-05-15 14:04 UTC] lucas at decrock dot me
As I was developing a REST API, I came across an issue that surprised me: the API doesn't support PATCH requests. It's disappointing to see such a basic feature missing in 2023, and it could potentially reflect poorly on the professionalism of the API's developers.
 [2023-12-10 19:08 UTC] dawidgora at icloud dot com
Any update on this one? 2011 was 12 year ago lol
 [2023-12-10 19:13 UTC] satoriestore at gmail dot com
Schade. Gibt es ein Update zu diesem Thema?
12 Jahre seit der Meldung...
 [2023-12-10 19:16 UTC] ilutov@php.net
Yes, there's an RFC that should be voted on in the coming weeks. https://wiki.php.net/rfc/rfc1867-non-post
 [2024-02-08 11:19 UTC] ilutov@php.net
-Status: Suspended +Status: Closed -Assigned To: +Assigned To: ilutov
 [2024-02-08 11:19 UTC] ilutov@php.net
Thank you for your bug report. This issue has already been fixed
in the latest released version of PHP, which you can download at
http://www.php.net/downloads.php

This is will be fixed in PHP 8.4:
https://wiki.php.net/rfc/rfc1867-non-post
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Dec 12 11:01:28 2024 UTC