Mercurial > hg > ywww
diff twitter/EpiTwitter.php @ 6:077b0a0a3e6d
remaining originals according to dependency walk
author | Robert Boland <robert@markup.co.uk> |
---|---|
date | Thu, 16 Feb 2017 22:29:02 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/twitter/EpiTwitter.php Thu Feb 16 22:29:02 2017 +0000 @@ -0,0 +1,285 @@ +<?php +/* +* Class to integrate with Twitter's API. +* Authenticated calls are done using OAuth and require access tokens for a user. +* API calls which do not require authentication do not require tokens (i.e. search/trends) +* +* Full documentation available on github +* http://wiki.github.com/jmathai/twitter-async +* +* @author Jaisen Mathai <jaisen@jmathai.com> +*/ +class EpiTwitter extends EpiOAuth +{ + const EPITWITTER_SIGNATURE_METHOD = 'HMAC-SHA1'; + const EPITWITTER_AUTH_OAUTH = 'oauth'; + const EPITWITTER_AUTH_BASIC = 'basic'; + protected $requestTokenUrl= 'http://twitter.com/oauth/request_token'; + protected $accessTokenUrl = 'http://twitter.com/oauth/access_token'; + protected $authorizeUrl = 'http://twitter.com/oauth/authorize'; + protected $authenticateUrl= 'http://twitter.com/oauth/authenticate'; + protected $apiUrl = 'http://twitter.com'; + protected $apiVersionedUrl= 'http://api.twitter.com'; + protected $searchUrl = 'http://search.twitter.com'; + protected $userAgent = 'EpiTwitter (http://github.com/jmathai/twitter-async/tree/)'; + protected $apiVersion = '1'; + protected $isAsynchronous = false; + + /* OAuth methods */ + public function delete($endpoint, $params = null) + { + return $this->request('DELETE', $endpoint, $params); + } + + public function get($endpoint, $params = null) + { + return $this->request('GET', $endpoint, $params); + } + + public function post($endpoint, $params = null) + { + return $this->request('POST', $endpoint, $params); + } + + /* Basic auth methods */ + public function delete_basic($endpoint, $params = null, $username = null, $password = null) + { + return $this->request_basic('DELETE', $endpoint, $params, $username, $password); + } + + public function get_basic($endpoint, $params = null, $username = null, $password = null) + { + return $this->request_basic('GET', $endpoint, $params, $username, $password); + } + + public function post_basic($endpoint, $params = null, $username = null, $password = null) + { + return $this->request_basic('POST', $endpoint, $params, $username, $password); + } + + public function useApiVersion($version = null) + { + $this->apiVersion = $version; + } + + public function useAsynchronous($async = true) + { + $this->isAsynchronous = (bool)$async; + } + + public function __construct($consumerKey = null, $consumerSecret = null, $oauthToken = null, $oauthTokenSecret = null) + { + parent::__construct($consumerKey, $consumerSecret, self::EPITWITTER_SIGNATURE_METHOD); + $this->setToken($oauthToken, $oauthTokenSecret); + } + + public function __call($name, $params = null/*, $username, $password*/) + { + $parts = explode('_', $name); + $method = strtoupper(array_shift($parts)); + $parts = implode('_', $parts); + $endpoint = '/' . preg_replace('/[A-Z]|[0-9]+/e', "'/'.strtolower('\\0')", $parts) . '.json'; + /* HACK: this is required for list support that starts with a user id */ + $endpoint = str_replace('//','/',$endpoint); + $args = !empty($params) ? array_shift($params) : null; + + // calls which do not have a consumerKey are assumed to not require authentication + if($this->consumerKey === null) + { + $username = null; + $password = null; + + if(!empty($params)) + { + $username = array_shift($params); + $password = !empty($params) ? array_shift($params) : null; + } + + return $this->request_basic($method, $endpoint, $args, $username, $password); + } + + return $this->request($method, $endpoint, $args); + } + + private function getApiUrl($endpoint) + { + if(preg_match('@^/(trends|search)[./]?(?=(json|daily|current|weekly))@', $endpoint)) + return "{$this->searchUrl}{$endpoint}"; + elseif(!empty($this->apiVersion)) + return "{$this->apiVersionedUrl}/{$this->apiVersion}{$endpoint}"; + else + return "{$this->apiUrl}{$endpoint}"; + } + + private function request($method, $endpoint, $params = null) + { + $url = $this->getUrl($this->getApiUrl($endpoint)); + $resp= new EpiTwitterJson(call_user_func(array($this, 'httpRequest'), $method, $url, $params, $this->isMultipart($params)), $this->debug); + if(!$this->isAsynchronous) + $resp->responseText; + + return $resp; + } + + private function request_basic($method, $endpoint, $params = null, $username = null, $password = null) + { + $url = $this->getApiUrl($endpoint); + if($method === 'GET') + $url .= is_null($params) ? '' : '?'.http_build_query($params, '', '&'); + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, $this->requestTimeout); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + if($method === 'POST' && $params !== null) + { + if($this->isMultipart($params)) + curl_setopt($ch, CURLOPT_POSTFIELDS, $params); + else + curl_setopt($ch, CURLOPT_POSTFIELDS, $this->buildHttpQueryRaw($params)); + } + if(!empty($username) && !empty($password)) + curl_setopt($ch, CURLOPT_USERPWD, "{$username}:{$password}"); + + $resp = new EpiTwitterJson(EpiCurl::getInstance()->addCurl($ch), $this->debug); + if(!$this->isAsynchronous) + $resp->responseText; + + return $resp; + } +} + +class EpiTwitterJson implements ArrayAccess, Countable, IteratorAggregate +{ + private $debug; + private $__resp; + public function __construct($response, $debug = false) + { + $this->__resp = $response; + $this->debug = $debug; + } + + // ensure that calls complete by blocking for results, NOOP if already returned + public function __destruct() + { + $this->responseText; + } + + // Implementation of the IteratorAggregate::getIterator() to support foreach ($this as $...) + public function getIterator () + { + if ($this->__obj) { + return new ArrayIterator($this->__obj); + } else { + return new ArrayIterator($this->response); + } + } + + // Implementation of Countable::count() to support count($this) + public function count () + { + return count($this->response); + } + + // Next four functions are to support ArrayAccess interface + // 1 + public function offsetSet($offset, $value) + { + $this->response[$offset] = $value; + } + + // 2 + public function offsetExists($offset) + { + return isset($this->response[$offset]); + } + + // 3 + public function offsetUnset($offset) + { + unset($this->response[$offset]); + } + + // 4 + public function offsetGet($offset) + { + return isset($this->response[$offset]) ? $this->response[$offset] : null; + } + + public function __get($name) + { + $accessible = array('responseText'=>1,'headers'=>1,'code'=>1); + $this->responseText = $this->__resp->data; + $this->headers = $this->__resp->headers; + $this->code = $this->__resp->code; + if(isset($accessible[$name]) && $accessible[$name]) + return $this->$name; + elseif(($this->code < 200 || $this->code >= 400) && !isset($accessible[$name])) + EpiTwitterException::raise($this->__resp, $this->debug); + + // Call appears ok so we can fill in the response + $this->response = json_decode($this->responseText, 1); + $this->__obj = json_decode($this->responseText); + + if(gettype($this->__obj) === 'object') + { + foreach($this->__obj as $k => $v) + { + $this->$k = $v; + } + } + + if (property_exists($this, $name)) { + return $this->$name; + } + return null; + } + + public function __isset($name) + { + $value = self::__get($name); + return !empty($name); + } +} + +class EpiTwitterException extends Exception +{ + public static function raise($response, $debug) + { + $message = $response->data; + + switch($response->code) + { + case 400: + throw new EpiTwitterBadRequestException($message, $response->code); + case 401: + throw new EpiTwitterNotAuthorizedException($message, $response->code); + case 403: + throw new EpiTwitterForbiddenException($message, $response->code); + case 404: + throw new EpiTwitterNotFoundException($message, $response->code); + case 406: + throw new EpiTwitterNotAcceptableException($message, $response->code); + case 420: + throw new EpiTwitterEnhanceYourCalmException($message, $response->code); + case 500: + throw new EpiTwitterInternalServerException($message, $response->code); + case 502: + throw new EpiTwitterBadGatewayException($message, $response->code); + case 503: + throw new EpiTwitterServiceUnavailableException($message, $response->code); + default: + throw new EpiTwitterException($message, $response->code); + } + } +} +class EpiTwitterBadRequestException extends EpiTwitterException{} +class EpiTwitterNotAuthorizedException extends EpiTwitterException{} +class EpiTwitterForbiddenException extends EpiTwitterException{} +class EpiTwitterNotFoundException extends EpiTwitterException{} +class EpiTwitterNotAcceptableException extends EpiTwitterException{} +class EpiTwitterEnhanceYourCalmException extends EpiTwitterException{} +class EpiTwitterInternalServerException extends EpiTwitterException{} +class EpiTwitterBadGatewayException extends EpiTwitterException{} +class EpiTwitterServiceUnavailableException extends EpiTwitterException{} +