Archive for June, 2008

Progress with April

Monday, June 9th, 2008

Progress April has been coming along nicely. I’ve written most of the HTTP layer (Client, Request and Response). There are a few “extras” left to code, but essentially I’ve now got a functional “80/20″ HTTP client.

Usage looks like this:-

$request = new April_Request("http://www.google.com/search");
$request->GET['q'] = “REST frameworks”;
$request->GET['hl'] = ‘en’;
$request->setHeader(’user-agent’, ‘Mozilla/5.0′);

$client = new April_Client();
$response = $client->request($request);

if($response->isOk()){
    echo $response->getBody();
}

If you’re familiar with Zend_Http_Client then the API will be familiar (although it’s quite different under-the-hood).

I’ve also started implementing the “REST Framework” part of the project. This part has gone through quite a lot of refactoring recently as I’m not 100% sure how best to implement it (as a design challenge). The primary goals are:-

  1. Make it easy for someone to implement a REST API from the client perspective*
  2. Respect a “Resource Orientated Architecture” (ROA)

* I’m deferring the “server” aspect for now (I think it’s an area that gets blurry with “standard frameworks” and I don’t want to go there!).

Respecting an ROA basically means restraining from creating RPC-like services (à la Zend Framework). So the service doesn’t expose methods like $s3->ListAllMyBuckets(), but allows you to perform an HTTP-verb action (HEAD/GET/PUT/POST/DELETE) upon a resource: $s3->getBuckets()

I’ve come up with a nifty solution that uses PHP’s magic __call() method. It breaks apart the verb and resource (”get” and “Buckets”) and can then take the appropriate action in order to “get Buckets”. So if the REST service has a resource called Object the service would let you call:-

  • getObject(…)
  • putObject(…)
  • headObject(…)
  • postObject(…)
  • deleteObject(…)

Internally the service object has an HTTP client, so when one of these methods is called it must:-

  1. Build an HTTP request object, based upon the verb, resourceType, resource, url and any other parameters that it needs.
  2. Execute the HTTP request and return the Response

How an HTTP Request is built depends mostly on the service (an Amazon S3 request will be different to a Flickr request) and on the Resource (an S3 object-resource is represented differently than a Flickr image).

I’ve reflected this in the class-design: you extend the base April_Service and override the buildRequest() method to apply any service-specfic logic when building a Request. The resource-specific logic is handled slightly differently: we use the Strategy Pattern and load in a ResourceHelper object. The reason for this twofold:-

  1. A service might have many resources and it’s easier to maintain the code if their nuances are self-contained (imagine a single My_Service class trying to handle a dozen types of resources).
  2. Resource-specific logic is also required when decomposing the Response to our HTTP Request (ie we have an HTTP Response with some XML… how do we convert that to a PHP representation of the resource?).

So the ResourceHelper essentially converts a “resource” to/from PHP and “service” representations.

At the moment I’m really pleased with how this design has played out. In order to implement a particular REST service you just need to create a Service class and then a ResourceHelper for each type of resource.

Here’s some code to demonstrate all this:-

April_Service: April_Service.phps
The base service, extend this to implement a REST API.

April_Paste2: April_Paste2.phps
An service implementation for a Paste2.org API

April_Paste2_Paste_Helper: April_Paste2_Paste_Helper.phps
A ResourceHelper for a “Paste” resource within the Paste2 service

paste2test: paste2test.phps
A simple use-case showing how the Paste2 service could be used