PHP request and response lifecycle

In abstract the job of every web programming language is to process an incomming HTTP request and produce a HTTP response based on the request data. Every language has its own API to expose the request data to the userland code and to provide a way to produce an response. With this post I want to explain the basic PHP API (which is quickly forgotten under all the abstraction layers which are used today).

In PHP the incomming request data is stored in superglobal variables and you can use i.e. the echo and header function to produce an response. Lets take a look at the following HTTP request.

POST /tests/test.php?bar=foo HTTP/1.1
Host: 127.0.0.1
User-Agent: Foolib
Content-Type: application/x-www-form-urlencoded
Connection: Keep-Alive
Cookie: baz=foo
Content-Length: 7

foo=bar

If such an request arrives to our script we could access the request data with the following variables:

  • $_GET

    $_GET = [
        'bar' => 'foo',
    ];
    

    The $_GET variable get filled by the query parameters from the url. PHP uses the parse_str function to convert the query string into an array. Because of that it is not possible to have multiple GET parameters with the same name i.e. /test.php?bar=foo&bar=bar which is unusual but possible. Also GET parameter which contain an '.' in the name are replaced by an underscore i.e. /test.php?foo.bar=value becomes foo_bar. It is possible to create nested arrays by appending "[]" to the parameter name i.e. /test.php?bar[]=foo&bar[]=bar. This would create an array like array("foo", "bar").

  • $_POST

    $_POST = [
        'foo' => 'bar',
    ];
    

    The parsing of the POST body into an array works only if the Content-Type is application/x-www-form-urlencoded. If this is the case PHP also uses the parse_str function. Because of that all restrictions described for the GET parameters are also applied to the POST parameters.

  • $_COOKIE

    $_COOKIE = [
        'baz' => 'foo',
    ];
    

    The $_COOKIE variable contains the key value pairs of the Cookie header.

  • $_FILES

    $_FILES = [];
    

    The $_FILES variable is only filled if the request contains an multipart/form-data file upload.

  • $_REQUEST

    $_REQUEST = [
        'bar' => 'foo',
        'foo' => 'bar',
    ];
    

    The $_REQUEST variable contains by default the GET, POST and COOKIE values. On my php.ini I have: request_order = "GP" which means it contains only GET and POST values.

  • $_SERVER

    $_SERVER = [
        'HTTP_HOST' => '127.0.0.1',
        'HTTP_USER_AGENT' => 'Foolib',
        'HTTP_CONNECTION' => 'Keep-Alive',
        'HTTP_COOKIE' => 'baz=foo',
        'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
        'CONTENT_LENGTH' => '7',
        'REMOTE_ADDR' => '127.0.0.1',
        'SERVER_PROTOCOL' => 'HTTP/1.1',
        'REQUEST_METHOD' => 'POST',
        'QUERY_STRING' => 'bar=foo',
        'REQUEST_URI' => '/tests/test.php?bar=foo',
    ];
    

    The $_SERVER values contain all headers in the HTTP_* keys and also meta variables from the webserver. There is an RFC which specifies these meta variables.

  • Body

    $body = file_get_contents('php://input');

    In order to access the raw request body we can use the input stream in this case $body would have the value "foo=bar". This is useful for parsing non application/x-www-form-urlencoded content i.e. JSON or XML.

To produce an response we could use the following PHP code:

<?php

header('HTTP/1.1 200 OK');
header('Content-Type: text/plain');

setcookie('baz', 'foo');

echo 'foobar';

The header function writes an raw header to the response. The setcookie function adds an Set-Cookie header. With echo it is possible to output the response body. There are also some other ways i.e. you could use the print function or write to the PHP output stream with i.e. file_put_contents('php://output', 'foobar');.

Note normally you can only use the header and setcookie function before you output any content. In case output buffering is enabled it is also possible to send an header after sending an output because every response produced by the script gets written into an internal buffer. The buffer is sent either if the script ends or flushed

In the end this would produce the following HTTP response:

HTTP/1.1 200 OK
Date: Sat, 27 Dec 2014 21:53:19 GMT
X-Powered-By: PHP/5.5.11
Set-Cookie: baz=foo
Content-Length: 6
Connection: Keep-Alive
Content-Type: text/plain

foobar

As conclusion I think the PHP API is not beautiful but its simple and is probably the reason why PHP is so popular.