Google AJAX Services: A Simple PHP Wrapper
Recently I had the pleasure of utilizing Google’s cool AJAX services in some functional testing for a PHP application. Rather than implement a flat procedural approach, I opted to write a thin wrapper class to encapsulate the basics that Google needs to execute a request against its API.
There’s two classes/files — GoogleServices (GoogleServices.class.php), and GoogleServicesResponse (GoogleServicesResponse.class.php), which you can download here.
One at a time, please
Notice GoogleServices implements the singleton pattern — meaning we want a single instance of GoogleServices wrapper everywhere in our code. Why? GoogleServices implement a RESTful interface. From our RESTful client perspective, there is only a single representation of interfaces of the services (Google).
It simply would not make sense for the same Google API wrapper to behave in two markedly different ways in our application, since the underlying Google services it accesses always provides the same interface. We (the client) must fully rely on the returned responses as full and complete representations of a resources state (being sure to adhere to the specified cache policy, of course).
Here’s how to instantiate the singleton:
// if you're using Symfony, use the yaml parser which looks for the 'api' key // example: // api: // key: ABCDEFGHIJKL1234567890 // url: http://ajax.google.com/services/ // valid_for: http://www.mysitename.com $filename = 'google-config.yml'; $gsi = GoogleServices::getInstance( $filename ); // OR // configure using arrays $gsi = GoogleServices::getInstance( Array( 'api' => Array( 'key' => 'ABCDEFGHIJKL1234567890', 'url' => 'http://ajax.google.com/services/', 'valid_for' => 'http://www.mysitename.com' ));
In practice: language detection using Google’s language service
Most articles I found linking back to Google’s documentation focused on the use of the language translation service. Contrarily in my particular project, I had no want of actually translating the source content, I just needed to verify that a provided translation WAS in fact the desired language.
Here’s a snippet showing how to detect the language of some content:
// get instance
$filename = 'google-config.yml';
$gsi = GoogleServices::getInstance( $filename );
// guess localized language
$res = $gsi->execute( 'language', 'detect', Array(
'q' => "Hello world. This is a test of the Google AJAX services API. How neat is that?",
'v' => "1.0"
));
if( $res->wasSuccessful() ) {
echo "Language was ".$res->get()->language . "\n";
echo "Google says this detection was ".
( $res->get()->isReliable ) ? "reliable" : "UN-reliable";
echo " with a ".$res->get()->confidence." degree of confidence.";
} else {
throw new Exception( "Google said: '".
$res->getErrorTitle()." -- ".$res->getErrorMessage()."'\n" );
}
You can see from the above example, that GoogleServicesResponse is merely a wrapper for the most relevant pieces of information returned by Google. Success is determined by the content type returned:
/**
* Tells whether response succceeded based on Google returning
* either javascript content type on success or html on failure
*
* @return Boolean Whether the request that produced this response object succeeded or not.
*/
public function wasSuccessful() {
if( false !== stripos( $this->ct, "text/html" ))
return false;
if( false !== stripos( $this->ct, "text/javascript" ))
return true;
return false;
}
In the sad event that Google chokes on our request (which more than likely be a fault of ours), we can grab the most relevant bits of error information via a few convenience getter functions:
/**
* Get the title of the error returned by Googl.e Throws an exception if request succeeded.
* @return String A summary of the reason the request failed.
*/
public function getErrorTitle() {
if( $this->wasSuccessful() )
throw new Exception( "Cannot call ".__FUNCTION__." on a successful response." );
return (String)$this->asSimpleXMLElement()->head->title;
}
/**
* Get the error message returned by Google.
* Throws an exception if request succeeded.
* @return String A message describing why the request failed.
*/
public function getErrorMessage() {
if( $this->wasSuccessful() )
throw new Exception( "Cannot call ".__FUNCTION__." on a successful response." );
return (String)$this->asSimpleXMLElement()->body->blockquote->H1;
}
/**
* Get the error code returned by Google.
* Throws an exception if request succeeded.
* @return Int The error code, if returned by Google (usually 0)
*/
public function getErrorCode() {
if( $this->wasSuccessful() )
throw new Exception( "Cannot call ".__FUNCTION__." on a successful response." );
return (Int)$this->asSimpleXMLElement()->body->blockquote->code;
}
Add a dash of spice
If you need some special/fancy/custom result parsing (for example: implement Iteratable on GoogleServicesResponse ?), you can easily extend GoogleServicesResponse to suit your needs. The base class provides several convenient data output types. Note that these depend on the conditional success or failure of the request (e.g. you can’t turn successful response JSON data into an XML tree and visa versus).
/**
* Returns the response body as a JSON object String
* @return String JSON string produced by json_decode native JSON parser.
*/
public function asJson() {
if( !$this->wasSuccessful() )
throw new Exception( "Cannot call ".__FUNCTION__." on an un-successful request: call GoogleServicesRequest::wasSuccessful() to check for a successful response." );
if( !$this->resJson )
$this->resJson = json_decode( $this->b );
return $this->resJson;
}
/**
* Returns the response body XML string as produced by SimpleXMLElement
* @return String The String produced by the SimpleXMLELement::asXml() of the response.
* @see http://php.net/manual/en/book.simplexml.php
*/
public function asXml() {
if( !$this->wasSuccessful() )
throw new Exception( "Cannot call ".__FUNCTION__." on an un-successful request: call GoogleServicesRequest::wasSuccessful() to check for a successful response." );
return $this->asSimpleXMLElement()->asXml();
}
/**
* Returns the response as a native SimpleXMLElement
* @return SimpleXMLELement The response.
*/
public function asSimpleXMLElement() {
if( !$this->resXml ) {
$domDoc = new DOMDocument();
// silence load warnings due to Google's poorly formatted HTML response
try {
$domDoc->loadHtml( $this->b );
} catch( Exception $e ) {
if( false === stripos( $e->getMessage(), "mismatch" ))
throw $e;
}
$this->resXml = simplexml_load_string( $domDoc->saveXml() );
}
return $this->resXml;
}
“Language forces us to perceive the world as man presents it to us.” ~Julia Penelope
Categories: Development, Free Stuff, How To's, Open Source, PHP, Symfony
Tags: AJAX, Google, L10n, L18n, php, Web Services