Twitter API Made Easy

2

While working on NestWork, I needed to send Twitter API requests that were authenticated for different users. This excellent post got me about 95% of the way there. The two missing pieces were:

  1. Ability to send parameters with requests
  2. Ability to send multiple requests

The first was actually fairly simple to fix – once you understand that not only do the auth parameters go into generating the request signature, but the request parameters also must be part of the signature or the request will fail. Took a little trial and error to figure this out and the code change was very simple to put into effect.

The second item was tricky, since I could send one request and it would work but any subsequent requests always failed. For a few hours, I assumed that the second request failed and I was using the API wrong. Once I only sent that request, it worked fine – which led to the revelation that my class used the same $oauth array for signature calculation and request generation. That was ok, but after the first request, the signature had been added to the array. Any subsequent request would then include the old signature value in the new signature generation and then replace the old value in the array. This meant that the signature would be invalid. Once I deduced the nature of the problem, solving it was simple.

I redeveloped the code into a php class to make it modular and easier to work with which I’m happy to share.

The class starts by defining the key variables (such as the application tokens) as well as the constructor – which accepts the user tokens we are making requests on behalf of.

class tapi {
var $consumer_key = ‘CONSUMER_KEY_HERE;
var $consumer_secret = ‘CONSUMER_SECRET_HERE’;
var $access_token;
var $access_secret;
var $oauth;

function tapi($token, $secret) {
$this->access_token = $token;
$this->access_secret = $secret;

$this->oauth = array(
'oauth_consumer_key' => $this->consumer_key,
'oauth_token' => $this->access_token,
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_timestamp' => time(),
'oauth_nonce' => time(),
'oauth_version' => '1.0'
);
}

This is followed by two key methods from ErisDS which do a nice job building the request url and the authorization header:

function build_url($request, $params) {
$url = "http://api.twitter.com/1/$request";
if(count($params)) {
$query = array();
foreach($params as $key=>$value)
$query[] = "$key=" . rawurlencode($value);

$url .= '?' . implode('&', $query);
}

return $url;
}

function build_authorization_header()
{
$r = 'Authorization: OAuth ';
$values = array();
foreach($this->oauth as $key=>$value)
$values[] = "$key="" . rawurlencode($value) . """;

$r .= implode(', ', $values);
return $r;
}

Then I’ve added a convenience method to build the base string that is used when generating the signature. And I’ve created a build signature method from the ErisDS code. The difference is that I’m also allowing parameters to be included in the request. Also note that the “oauth_signature” element from the parameter array is ignored when building the base string. This is the fix for the subsequent request failure issue I mentioned above.

function build_base_string($request, $method, $params)
{
$r = array();
ksort($params);
foreach($params as $key=>$value){
if($key == "oauth_signature") continue;
$r[] = "$key=" . rawurlencode($value);
}

return $method."&".rawurlencode("http://api.twitter.com/1/$request").'&'.
rawurlencode(implode('&', $r)); //return complete base string
}

function build_signature($request, $params) {
$all_params = array_merge($params, $this->oauth);
$base_info = $this->build_base_string($request, 'GET', $all_params);

$composite_key = rawurlencode($this->consumer_secret) . '&' .
rawurlencode($this->access_secret);
$oauth_signature = base64_encode(hash_hmac('sha1', $base_info,
$composite_key, true));
$this->oauth['oauth_signature'] = $oauth_signature;
}

The most important method is send_request which takes the request and parameters and builds the url, generates the signature, sends the request and returns the result.

function send_request($request, $params) {
$url = $this->build_url($request, $params);
$this->build_signature($request, $params);

$header = array($this->build_authorization_header(), 'Expect:');
$options = array( CURLOPT_HTTPHEADER => $header,
CURLOPT_HEADER => false,
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false);
$feed = curl_init();
curl_setopt_array($feed, $options);
$json = curl_exec($feed);
curl_close($feed);

$twitter_data = json_decode($json);
return $twitter_data;
}

The class is essentially complete but I decided that I wanted helper methods for the different requests so I can default parameters and make the request as dummy proof as possible. Here are a few examples:


function verify_credentials() {
$request = "account/verify_credentials.json";
$params = array();

$result = $this->send_request($request, $params);
return($result);
}

function user_lookup($list) {
$request = "users/lookup.json";
$params = array('user_id'=>$list);
// where list is a comma delimited list of twitter ids – up to 100

$result = $this->send_request($request, $params);
return($result);
}

The entire class is available for download here: tapi.zip. Hope someone else finds it useful.

2

Comments

  • Chuck

    how to use this class sir? i think its very useful if there’s an example

  • Trionic

    First instantiate it:

    $tapi = new tapi($token, $secret);

    Then send a twitter API request:

    $request = "lists/members.json";
    $params = array('list_id'=>$list_id);
    $result = $tapi->send_request($request, $params);