Custom Request Methods And PHP
By default, PHP (along with web browsers and HTML in general) uses two of the eight defined HTTP request methods. The eight methods are: HEAD, GET, POST, PUT, DELETE, TRACE, OPTIONS, and CONNECT. GET is the most widely used, followed by POST. Whenever you type a URL into your browser’s address bar, select a bookmark, or click a link you’re doing a GET. When you submit a form on a web page the method will be either GET or POST.
RESTfully designed web-services use POST, GET, PUT, and DELETE.
Behind the scenes a basic GET request looks like this:
GET /path/to/file.php HTTP/1.1 Host: www.domain.com
If you were to type this URL into your browser it would look like http://www.domain.com/path/to/file.php
Similarly, a POST to the same URL would look like this:
POST /path/to/file.php HTTP/1.1 Host: www.domain.com
(Note: For the purpose of these examples I’m leaving out other request headers such as User-Agent, Keep-Alive, Connection, etc. that you might normally see when using a web browser)
When you do a GET or POST you can also pass in variables. Consider a web page with a form that asks you to submit your first and last name.
The GET request might look like this:
GET /path/to/file.php?FirstName=John&LastName=Doe HTTP/1.1 Host: www.domain.com
As you can see, the variables are attached to the URL. In your browser this would look like http://www.domain.com/path/to/file.php?FirstName=John&LastName=Doe. These type of request variables are sometimes known as GET variables.
The POST version of this request would look like this:
POST /path/to/file.php HTTP/1.1 Host: www.domain.com Content-Type: application/x-www-form-urlencoded Content-Length: 27 FirstName=John&LastName=Doe
As you can see, the variables aren’t appended to the URL in a POST, but instead submitted following the request headers. Variables submitted in this way are sometimes known as POST variables. In this example I also set the Content-Length and Content-Type headers indicating that this POST was submitting name/value pairs. You can also submit other data via POST such as uploading a file (and you would want to set the Content-Type appropriately).
PHP has two superglobals called $_GET and $_POST. These arrays are automatically populated by PHP based on the request.
It should be noted that it’s technically possible to submit a POST request with both GET and POST-type variables.
POST /path/to/file.php?variable1=value1&variable2=value2 HTTP/1.1 Host: www.domain.com Content-Type: application/x-www-form-urlencoded Content-Length: 27 FirstName=John&LastName=Doe
In this case, $_GET would be populated with:
Array
(
[variable1] => value1
[variable2] => value2
)
And $_POST would be populated with:
Array
(
[FirstName] => John
[LastName] => Doe
)
Using A Custom Request Method
What happens if you want to create a RESTful web-service and submit a PUT request to a PHP page? What if you want to make up your own request method; how do you handle that with PHP?
Consider the following request:
MYMETHOD /path/to/file.php HTTP/1.1 Host: www.domain.com Content-Length: 27 FirstName=John&LastName=Doe
In this example I’m submitting POST-style variables with my own made-up request method called MYMETHOD. I could just as easily substitute MYMETHOD with PUT. You can easily submit this type of request with a Firefox extension called RestTest. You can verify what is actually being sent with another extension called Live HTTP Headers.
(Note: Some servers may be locked down to only allow certain request methods. If you make a request with a method that’s not allowed then the server will respond back with a 405 Method Not Allowed error. A 405 response will include a response header letting you know which methods are allowed.)
If you don’t want to rely on the convenience of $_GET and $_POST then you can always parse out the raw data yourself.
- $_SERVER['QUERY_STRING'] is the raw version of $_GET
- php://input is the raw version of $_POST
You should never have to parse out the query string manually, but $_POST is only populated if the request method is POST and an appropriate Content-Type is set.
To get our raw MYMETHOD data we could do something like this:
if ($_SERVER['REQUEST_METHOD'] == 'MYMETHOD')
{
$raw_data = file_get_contents('php://input');
}
We could then parse out the raw data into a $_MYMETHOD variable (to mimic $_GET and $_POST).
$array = explode('&', $raw_data);
foreach($array as $item)
{
list($key, $value) = explode('=', $item);
$_MYMETHOD[$key] = $value;
}
Putting It All Together
Besides using a tool like RestTest, we can also create a web page that submits a MYMETHOD request. We’ll do this by using some JavaScript and making an Ajax request with the XMLHttpRequest object.
<?php
if ($_SERVER['REQUEST_METHOD'] == 'MYMETHOD' && strpos($_SERVER['CONTENT_TYPE'], 'application/x-javascript') !== FALSE):
$raw_data = file_get_contents('php://input');
$array = explode('&', $raw_data);
foreach($array as $item)
{
list($key, $value) = explode('=', $item);
$_MYMETHOD[$key] = $value;
}
print_r($_MYMETHOD);
else:
?>
<html>
<head>
<script type="text/javascript">
/**
* Bridge XMLHTTP to XMLHttpRequest in pre-7.0 Internet Explorers
*/
if( typeof XMLHttpRequest == "undefined" )
XMLHttpRequest = function() {
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch(e) {}
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch(e) {}
try { return new ActiveXObject("Msxml2.XMLHTTP") } catch(e) {}
try { return new ActiveXObject("Microsoft.XMLHTTP") } catch(e) {}
throw new Error( "This browser does not support XMLHttpRequest or XMLHTTP." )
};
function ajax(url, vars) {
var request = new XMLHttpRequest();
request.open("MYMETHOD", url, true);
request.setRequestHeader("Content-Type", "application/x-javascript;");
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
if (request.responseText) {
alert(request.responseText);
}
}
};
request.send(vars);
}
</script>
</head>
<body>
<input type="button" value="click for ajax" onclick="javascript:ajax('<?php echo $_SERVER['PHP_SELF']; ?>', 'FirstName=John&LastName=Doe');" />
</body>
</html>
<?php endif; ?>
If you copy this into a PHP file (i.e. mymethod.php), upload it to a PHP-enabled web server, and navigate to it with your web browser you’ll see a button that says click for ajax. If you click the button, an asynchronous MYMETHOD request will be sent, the server will parse out the sent name/value pairs and send the processed data back to the browser. The user will then be presented with an alert popup containing the following text:
Array
(
[FirstName] => John
[LastName] => Doe
)
Fernando Vía Canel (Mar 18, 2009)
Excellent post! REST is amazing, and it was a shame not to have a
way to use it in PHP. Good news for us REST fans Thanks!
Pingback (Jan 20, 2010)
Cross-Domain XmlHttpRequest-Foo mit Custom HTTP Methods « BenBE's humble thoughts