On $_GET and $_POST

    Troels Knak-Nielsen

    The PHP manual has the following to say about the notorious superglobals, $_GET and $_POST:

    An associative array of variables passed to the current script via the HTTP GET method.
    Source: php.net

    An associative array of variables passed to the current script via the HTTP POST method.
    Source: php.net

    Let’s see how a typical PHP application works, shall we?

    When a PHP script is invoked by a web server, it is as the result of a HTTP request. A HTTP request has a target URI and that URI consists of different parts. One of these parts is the query. That is the part that comes after any question mark. Or more formally:

      absoluteURI   = scheme ":" ( hier_part | opaque_part )
      hier_part     = ( net_path | abs_path ) [ "?" query ]
      net_path      = "//" authority [ abs_path ]
      abs_path      = "/"  path_segments

    Source: rfc 2396

    As the PHP process starts up, the query gets parsed into an associative array. And for some reason, somebody decided on the unfortunate $_GET, because it’s what you use for GET requests – right?


    All HTTP requests – GET, POST and the lesser known ones – have a URI. Since they all have a URI, they also have a query, and thus the $_GET array is available for all of them. In other words, a POST request also has $_GET parameters. No wonder people get tripped up about HTTP and REST. But it gets worse yet.

    In addition to a URI, some HTTP requests may contain a message body; Not all requests (A GET may not, for example) – but some. The most common example of request that contains a message body is the POST type. Enter the $_POST variable. Like its cousin $_GET, this is populated at startup, but with the message body parsed as application/x-www-form-urlencoded. Unlike $_GET this isn’t part of the HTTP specification, but specifically the kind of encoding that browsers will submit HTML <form> in. In practise, this is a fairly safe bet, because browsers only are capable of submitting forms as GET and POST, but technically there’s nothing stopping someone from sending a PUT type request with a form-encoded message-body.

    Then there is $_FILES, which is similar to $_POST, in that it is an interpretation of the message body, but this time according to rfc 1867, which by the way is the recommended encoding for POST requests. Yay!

    So now that all the specifications have been summed up, here is my gripe with PHP.

    The problem with $_GET and $_POST is that a lot of people assume – understandably, but wrongly – that $_GET is somehow tied to GET requests, and that $_POST correspondingly is what you use for POST requests. But that’s not the case. A POST request can well have relevant data in $_GET. Or in $_FILES. And form-encoding is not restricted to POST requests – Thus $_POST may contain relevant data for a PUT request.

    The current names are confusing and obscures the intention of HTTP; More descriptive names would have been $_QUERY instead of $_GET and $_FORM* instead of $_POST (Which would match the etymology of the name $_FILES).

    * As has been pointed out in the comments, perhaps $_FORM isn’t the best of names either.

    If it seems like a hassle to change that, allow me to remind that something similar was done a couple of years ago and that didn’t seem to bother anybody back then. But for starters, a clarification of the documentation would be a step in the right direction.

    And I won’t even comment on the nastiness of $_REQUEST.