Location: PHPKode > scripts > TwitPic API for PHP > meltingice-TwitPic-API-for-PHP/includes/TwitPic_API.php
<?php
/*
 * TwitPic API for PHP
 * Copyright 2010 Ryan LeFevre - @meltingice
 * PHP version 5.3.0+
 *
 * Licensed under the New BSD License, more info in LICENSE file
 * included with this software.
 *
 * Source code is hosted at http://github.com/meltingice/TwitPic-API-for-PHP
 */
 
class TwitPic_API {
	private $api, $options;
	private $category = null;
	private $method = null;
	private $format = null;
	
	public function __construct() {		
		$xmlApi = simplexml_load_file(dirname(__FILE__) .'/../api/api.xml');
		foreach ($xmlApi->category as $category) {
			$catName = (string)$category['name'];
			$this->api[$catName] = array();
			foreach ($category->endpoint as $endpoint) {
				$this->api[$catName][(string)$endpoint['name']] = $endpoint;
			}
		}
	}
	
	/*
	 * Executes an API call only after the category and method
	 * have been validated.
	 */
	private function execute($args) {
		if(count($args) >= 1) {
			$method_args = array_shift($args);
		} else {
			$method_args = array();
		}
		
		$this->options = $args[0];
		if($this->api_call()->attributes()->method == 'POST') {
			return $this->executePOST($method_args);
		} else { // assume GET?
			return $this->executeGET($method_args);	
		}
	}
	
	private function upload_photo($args) {
		if(count($args) >= 1) {
			$method_args = array_shift($args);
		} else {
			$method_args = array();
		}
		
		$this->options = $args[0];
		$this->category = 'upload';
		
		if(TwitPic::mode() == TwitPic_Config::MODE_READONLY) {
			throw new TwitPicAPIException("Uploading a photo requires an API key and Twitter credentials");
		}
		
		if(!isset($method_args['message']) || !isset($method_args['media'])) {
			throw new TwitPicAPIException("Missing required parameter for photo upload");
		}
		
		if(!is_file($method_args['media']) || !is_readable($method_args['media'])) {
			throw new TwitPicAPIException("Unable to find or read file");
		}
		
		$this->format = $this->get_format();
		
		$header = $this->build_header();
		$url = "http://api.twitpic.com/2/upload.{$this->format}";
		$r = new Http_Request2($url, Http_Request2::METHOD_POST);
		$r->setHeader('X-Verify-Credentials-Authorization', $header);
		$r->addPostParameter('key', TwitPic_Config::getAPIKey());
		$r->addPostParameter('message', $method_args['message']);
		$r->addUpload('media', $method_args['media']);
		
		$res = $r->send();
		
		if($res->getStatus() == 200) {
			if($this->options['tweet']) {
				return $this->post_to_twitter($res->getBody());
			} else {
				return $this->respond($res->getBody());
			}
			
		} else {
			throw new TwitPicAPIException($res->getBody());
		}
	}
	
	/*
	 * Performs a GET API method.
	 */
	private function executeGET($method_args) {
		$this->validate_args($method_args);

		$this->format = $this->get_format();
		
		$url = $this->get_request_url();
		$args = $this->get_url_args($method_args);
		$url .= "?$args";
		
		$r = new Http_Request2($url, Http_Request2::METHOD_GET);
		$res = $r->send();
		
		if($res->getStatus() == 200) {
			return $this->respond($res->getBody());
		} else {
			throw new TwitPicAPIException($res->getBody());
		}
	}
	
	/*
	 * Performs a POST API method
	 */
	private function executePOST($method_args) {
		$this->validate_args($method_args);
		$this->check_for_authentication();
		$header = $this->build_header();
		
		$url = $this->get_request_url();
		$r = new Http_Request2($url, Http_Request2::METHOD_POST);
		$r->setHeader('X-Verify-Credentials-Authorization', $header);
		$r->addPostParameter('key', TwitPic_Config::getAPIKey());
		foreach($method_args as $arg=>$val) {
			$r->addPostParameter($arg, $val);
		}
		
		$res = $r->send();
		if($res->getStatus() == 200) {
			if(strlen($res->getBody() > 0)) {
				return $this->respond($res->getBody());
			} else {
				return true;
			}
		} else {
			throw new TwitPicAPIException($res->getBody());
		}
	}
	
	/*
	 * Validates all of the arguments given to the API
	 * call and makes sure that required args are present.
	 */
	private function validate_args($args) {
		$api_call = $this->api_call();
		foreach($api_call->param as $param) {
			$attrs = $param->attributes();
			
			if(array_key_exists((string)$attrs->name, $args)) {
				if(!$this->validate_arg((string)$attrs->type, $args[(string)$attrs->name])) {
					throw new TwitPicAPIException("Invalid datatype for {$attrs->name} while calling {$this->category}/{$this->method}");
				}
			}
			
			if(isset($attrs->required)) {
				if($attrs->required == 'true' && !array_key_exists((string)$attrs->name, $args)) {
					throw new TwitPicAPIException("Missing required parameter '{$attrs->name}' for {$this->category}/{$this->method}");
				}
			}
		}
	}
	
	/*
	 * Validates the datatype of all arguments based on
	 * the type defined in the API. Returns true if value
	 * is valid, false otherwise.
	 */
	private function validate_arg($type, $value) {
		switch($type) {
			case 'integer':
				return is_integer($value);
			case 'string':
				return is_string($value);
			case 'short_id':
				return (bool) !preg_match('/([^A-Za-z0-9]+)/', $value);
			case 'username':
				if(mb_strlen($value, 'UTF-8') > 15 || mb_strlen($value, 'UTF-8') == 0) {
					return false;
				}
				
				return (bool) !preg_match('/([^A-Za-z0-9_]+)/', $value);
			case 'hashtag':
				return (bool) !preg_match('/([^A-Za-z0-9_])+/', $value);
		}
	}
	
	/*
	 * Checks to see if this API call requires authentication,
	 * and if we have all the required info.
	 */
	private function check_for_authentication() {
		/* if auth_required is not set, assume false */
		if(!isset($this->api_call()->attributes()->auth_required)){ return; }
		
		if($this->api_call()->attributes()->auth_required == 'true' && TwitPic::mode() == TwitPic_Config::MODE_READONLY){
			throw new TwitPicAPIException("API call {$this->category}/{$this->method} requires an API key and OAuth credentials");
		}
	}
	
	/*
	 * When making an authenticated API call, we need
	 * to build the header that is sent with the authorization
	 * information.
	 */
	private function build_header($tweet=false) {
		$consumer = TwitPic_Config::getConsumer();
		$oauth = TwitPic_Config::getOAuth();
		
		$signature = HTTP_OAuth_Signature::factory('HMAC_SHA1');
		$timestamp = gmdate('U');
		$nonce = uniqid();
		$version = '1.0';
		
		if(is_string($tweet)) {
			$params = array( 'oauth_consumer_key' => $consumer['key'] ,
				'oauth_signature_method' => 'HMAC-SHA1' ,
				'oauth_token' =>  $oauth['token'],
				'oauth_timestamp' => $timestamp ,
				'oauth_nonce' => $nonce ,
				'oauth_version' => $version,
				'status' => $tweet);
	
			$sig_text = $signature->build( 'POST',"http://api.twitter.com/1/statuses/update.{$this->format}", $params, $consumer['secret'], $oauth['secret'] );
	
			$params['oauth_signature'] = $sig_text;
		} else {
			$params = array( 'oauth_consumer_key' => $consumer['key'] ,
				'oauth_signature_method' => 'HMAC-SHA1' ,
				'oauth_token' =>  $oauth['token'],
				'oauth_timestamp' => $timestamp ,
				'oauth_nonce' => $nonce ,
				'oauth_version' => $version );
	
			$sig_text = $signature->build( 'GET','https://api.twitter.com/1/account/verify_credentials.json', $params, $consumer['secret'], $oauth['secret'] );
	
			$params['oauth_signature'] = $sig_text;
		}
		

		$realm = 'http://api.twitter.com/';
		$header = 'OAuth realm="' . $realm . '"';
		foreach ($params as $name => $value) {
			$header .= ", " . HTTP_OAuth::urlencode($name) . '="' . HTTP_OAuth::urlencode($value) . '"';
		}

		return $header;
	}
	
	/*
	 * Builds the TwitPic API request URL
	 */
	private function get_request_url() {
		$this->format = $this->get_format();
		return "http://api.twitpic.com/2/{$this->category}/{$this->method}.{$this->format}";
	}
	
	/*
	 * Gets the response format, defaults to json
	 * if none is given.
	 */
	private function get_format() {
		if(!isset($this->api_call()->formats) || $this->category == 'upload') {
			$allowed = array('xml','json');
		} else {
			$allowed = explode(',', $this->api_call()->formats);
			foreach($allowed as $i=>$var){
				$allowed[$i] = trim($var);
			}
		}
		
		if(isset($this->options['format'])) {
			if(!in_array($this->options['format'], $allowed)) {
				throw new TwitPicAPIException("Invalid response format requested for {$this->category}/{$this->method}");
			}
			
			return $this->options['format'];
		}
		
		if(in_array('json', $allowed)) {
			return 'json';
		}
		
		return $allowed[0];
	}
	
	/*
	 * Builds the query part of the URL and
	 * escapes any data necessary.
	 */
	private function get_url_args($args) {
		$pairs = array();
		foreach($args as $i=>$val) {
			$pairs[] = $i ."=". urlencode($val);
		}
		
		return implode("&", $pairs);
	}
	
	/*
	 * Processes the API response and converts it to
	 * an object if requested (default). The response
	 * can be forced to be returned in its raw format using
	 * the 'process' argument.
	 */
	private function respond($data) {
		$data = htmlspecialchars_decode($data, ENT_QUOTES);
		if((isset($this->options['process']) && $this->options['process'] == true) || !isset($this->options['process'])) {
			if($this->format == 'json') {
				$data = json_decode($data);
			} elseif($this->format == 'xml') {
				$data = simplexml_load_string($data);
			}
		}
		
		$this->reset_settings();
		
		return $data;
	}
	
	private function post_to_twitter($resp_data) {
		if($this->format == 'json') {
			$data = json_decode($resp_data);
		} elseif($this->format == 'xml') {
			$data = simplexml_load_string($resp_data);
		}
		
		$tweet = $this->truncate((string)$data->text) . ' ' . (string)$data->url;
		
		$header = $this->build_header($tweet);
		$url = "http://api.twitter.com/1/statuses/update.{$this->format}";
		
		$r = new Http_Request2($url, Http_Request2::METHOD_POST);
		$r->setHeader('Authorization', $header);
		$r->addPostParameter('status', $tweet);
		$res = $r->send();
		
		if($res->getStatus() == 200) {
			return $this->respond($resp_data);
		} else {
			throw new TwitPicAPIException("Image uploaded, but error posting to Twitter");
		}
	}
	
	private function truncate($msg) {
		return mb_substr($msg, 0, 114, 'UTF-8');
	}
	
	/*
	 * Resets the variables in this class to avoid
	 * any conflicts if multiple API calls are made.
	 */
	 private function reset_settings() {
	 	$this->category = null;
	 	$this->method = null;
	 	$this->format = null;
	 	$this->options = null;
	 }
	
	/*
	 * Gets the current API call
	 */
	private function api_call() {
		return isset($this->api[$this->category][$this->method]) ? $this->api[$this->category][$this->method] : null;
	}
	
	/*
	 * Checks to make sure the API category in use
	 * is valid and returns $this.
	 */
	private function api_category($category) {
		if(!isset($this->api[$category])) {
			throw new TwitPicAPIException('API category not found');
		}
		
		$this->category = $category;
		
		return $this;
	}
	
	/*
	 * Checks to make sure the API method for this category
	 * is defined and begins the execution of the API call.
	 */
	private function api_method($method, $args) {
		if($method == 'upload') { // need to make an exception for upload
			return $this->upload_photo($args);
		} elseif(is_null($this->category)) {
			throw new TwitPicAPIException('WTF is goin on yo?');
		} elseif(!isset($this->api[$this->category][$method])) {
			throw new TwitPicAPIException("API method not found for category {$this->category}");
		}
		
		$this->method = $method;
		
		return $this->execute($args);
	}
	
	/*
	 * This function handles the API category that is
	 * being called and if valid, returns $this so that
	 * the __call() function can be called next.
	 */
	public function __get($key) {
		return $this->api_category($key);
	}
	
	/*
	 * The __call() function handles accessing the API
	 * method.
	 */
	public function __call($method, $args) {
		return $this->api_method($method, $args);
	}
	
}
Return current item: TwitPic API for PHP