The City of knowledge
How To: Making a PHP REST client to call REST resources
A lot of companies these days (including Amazon and Yahoo!) are exposing their web services in the form of REST resources. At a high level REST is pretty easy to understand, all you’re doing is exposing a web service in the form of a URL. Users can then query this URL, through HTTP methods like GET and POST. REST calls generally return some type of XML or Object Encoding like JSON.
An example would be Yahoo!’s Geocoding API, with the following URL:
1 |
http://api.local.yahoo.com/MapsService/V1/geocode?appid=YahooDemo&street=701+First+Street&city=Sunnyvale&state=CA
I would get:
1 |
<Result precision="address"> <Latitude>37.416384</Latitude> <Longitude>-122.024853</Longitude> <Address>701 FIRST AVE</Address> <City>SUNNYVALE</City> <State>CA</State> <Zip>94089-1019</Zip> <Country>US</Country> </Result>
So Yahoo! exposes the Geocode URL and allows you to query this resource using URL parameters like appid and street. Dynamically building your URL to query a given resource is OK, generally that’s what people do, like the following:
1 |
$base = 'http://xml.amazon.com/onca/xml3';
$query_string = "";
$params = array( 'ManufacturerSearch' => "O'Reilly",
'mode' => 'books',
'sort' => '+salesrank',
'page' => 1,
'type' => 'lite',
'f' => 'xml',
't' => 'trachtenberg-20' ,
'dev-t' => 'XXXXXXXXXXXXXX' ,
);
foreach ($params as $key => $value) {
$query_string .= "$key=" . urlencode($value) . "&";
}
$url = "$base?$query_string";
$output = file_get_contents($url);
The problem here, is that REST is meant to take advantage of HTTP methods GET, POST, PUT, DELETE, etc.. When people are showing examples which dynamically build queries and call file_get_contents, the average user doesn’t appreciate (understand) what type of request is being made. Do they care? Should they care? We’ll that’s another story. Eventually though, more intense REST resources will become widely available, and it will be critical the user (developer) understands if their making a POST or PUT request.
I came across a great PEAR package the other day called HTTP_REQUEST, which among many things supports GET/POST/HEAD/TRACE/PUT/DELETE, basic authentication, proxy, proxy authentication, SSL, file uploads and more. Using this package, I got started on a simple wrapper class called RESTclient, which gives intuitive support for making REST resource calls.
So if I was going to use RESTclient to call the Geocode API above, it would look like this:
1 |
<?php
require_once "RESTclient.php";
$rest = new RESTclient();
$inputs = array();
$inputs["appid"] = "YahooDemo";
$inputs["street"] = "701 First Street";
$inputs["city"] = "Sunnyvale";
$inputs["state"] = "CA";
$url = "http://api.local.yahoo.com/MapsService/V1/geocode/"
$rest->createRequest("$url","POST",$inputs);
$rest->sendRequest();
$output = $rest->getResponse();
echo $output;
?>
At this point, you might be thinking, who cares — what’s the difference between using a loop to dynamically generate the URL or RESTclient. There are lots of reasons, first off, I can easily call the Geocode resource again using another address just by changing:
1 |
$inputs["street"] = "1600 Amphitheatre Parkway"; $inputs["city"] = "Mountain View";
There was no need to re-generate the URL. Furthermore, I’m explicitly specifying a POST request. Just as easily I can make a PUT, DELETE, etc. request on a given resource by changing the createRequest method parameters.
The class below is RESTclient. Note, I put this together to prove a point, it still needs some work if you plan on using it.
1 |
<?php
require_once "HTTP/Request.php";
class RESTClient {
private $root_url = "";
private $curr_url = "";
private $user_name = "";
private $password = "";
private $response = "";
private $responseBody = "";
private $req = null;
public function __construct($root_url = "", $user_name = "", $password = "") {
$this->root_url = $this->curr_url = $root_url;
$this->user_name = $user_name;
$this->password = $password;
if ($root_url != "") {
$this->createRequest("GET");
$this->sendRequest();
}
return true;
}
public function createRequest($url, $method, $arr = null) {
$this->curr_url = $url;
$this->req =& new HTTP_Request($url);
if ($this->user_name != "" && $this->password != "") {
$this->req->setBasicAuth($this->user_name, $this->password);
}
switch($method) {
case "GET":
$this->req->setMethod(HTTP_REQUEST_METHOD_GET);
break;
case "POST":
$this->req->setMethod(HTTP_REQUEST_METHOD_POST);
$this->addPostData($arr);
break;
case "PUT":
$this->req->setMethod(HTTP_REQUEST_METHOD_PUT);
// to-do
break;
case "DELETE":
$this->req->setMethod(HTTP_REQUEST_METHOD_DELETE);
// to-do
break;
}
}
private function addPostData($arr) {
if ($arr != null) {
foreach ($arr as $key => $value) {
$this->req->addPostData($key, $value);
}
}
}
public function sendRequest() {
$this->response = $this->req->sendRequest();
if (PEAR::isError($this->response)) {
echo $this->response->getMessage();
die();
} else {
$this->responseBody = $this->req->getResponseBody();
}
}
public function getResponse() {
return $this->responseBody;
}
}
?>
| Print article | This entry was posted by moustafa farhat on 29/10/2009 at 8:33 pm, and is filed under php & mysql. Follow any responses to this post through RSS 2.0. You can leave a response or trackback from your own site. |
