%PDF- %PDF-
Direktori : /home/emergentqj/jugement/wp-content/themes/Newspaper/includes/wp_booster/wp-admin/external/ |
Current File : /home/emergentqj/jugement/wp-content/themes/Newspaper/includes/wp_booster/wp-admin/external/twitter-client.php |
<?php /** * Simple Twitter API 1.1 REST Client using cURL extension * @author Tim Whitlock, http://timwhitlock.info * @license MIT */ define('TWITTER_API_TIMEOUT', 5 ); define('TWITTER_API_USERAGENT', 'PHP/'.PHP_VERSION.'; http://github.com/timwhitlock/php-twitter-api' ); define('TWITTER_API_BASE', 'https://api.twitter.com/1.1' ); define('TWITTER_OAUTH_REQUEST_TOKEN_URL', 'https://twitter.com/oauth/request_token'); define('TWITTER_OAUTH_AUTHORIZE_URL', 'https://twitter.com/oauth/authorize'); define('TWITTER_OAUTH_AUTHENTICATE_URL', 'https://twitter.com/oauth/authenticate'); define('TWITTER_OAUTH_ACCESS_TOKEN_URL', 'https://twitter.com/oauth/access_token'); /* define('YOUR_CONSUMER_KEY', 'zhPQJqyZmCu4Zko5kEqDkw'); define('YOUR_CONSUMER_SECRET', 'ka0zVIioPVGJ44qhvDj3APSSfoS0WbvYM98FKjxV0k'); define('SOME_ACCESS_KEY', '890610409-HxxCsHmA6BvIwP8j0dBdtG0BzndUAHy9My1zD4hk'); define('SOME_ACCESS_SECRET', 'Dr2Je1rpeLitq0ub81L1bAyLScAiX2yasEFwMjRSLg'); */ define('YOUR_CONSUMER_KEY', 'Nbq9WGD3Zf06k7ZPXSjpq07Qk'); define('YOUR_CONSUMER_SECRET', '8Alzy97ncMlVQApvxKfGpmykIwLI87EK90jnNmwjRBBtpB9Mnq'); define('SOME_ACCESS_KEY', '811898918267211776-RyiPsypCfYu18OBqV3w9jc0DaGNTuso'); define('SOME_ACCESS_SECRET', 'TYXhENQWQYHPpkaxv1ISUrYgHh5G4TLNwigXDKSHeWTGr'); /** * Client for the Twitter REST API 1.1 */ class TwitterApiClient { /** * Consumer key token for application * @var TwitterOAuthToken */ private $Consumer; /** * Authenticated access token * @var TwitterOAuthToken */ private $AccessToken; /** * Whether caching API GET requests * @var int */ private $cache_ttl = null; /** * Namespace/prefix for cache keys * @var string */ private $cache_ns; /** * Registry of last rate limit arrays by full api function call * @var array */ private $last_rate = array(); /** * Last api function called, e.g. "direct_messages/sent" * @var string */ private $last_call; /** * @internal */ public function __sleep(){ return array('Consumer','AccessToken'); } /** * Enable caching of subsequent API calls * @return TwitterApiClient */ public function enable_cache( $ttl = 0, $namespace = 'twitter_api_' ){ if( function_exists('apc_store') ){ $this->cache_ttl = (int) $ttl; $this->cache_ns = $namespace; return $this; } trigger_error( 'Cannot enable Twitter API cache without APC extension' ); return $this->disable_cache(); } /** * Disable caching for susequent API calls * @return TwitterApiClient */ public function disable_cache(){ $this->cache_ttl = null; $this->cache_ns = null; return $this; } /** * Test whether the client has full authentication data. * Warning: does not validate credentials * @return bool */ public function has_auth(){ return $this->AccessToken instanceof TwitterOAuthToken && $this->AccessToken->secret; } /** * Unset all logged in credentials - useful in error situations * @return TwitterApiClient */ public function deauthorize(){ $this->AccessToken = null; return $this; } /** * Set currently logged in user's OAuth access token * @param string consumer api key * @param string consumer secret * @param string access token * @param string access token secret * @return TwitterApiClient */ public function set_oauth( $consumer_key, $consumer_secret, $access_key = '', $access_secret = '' ){ $this->deauthorize(); $this->Consumer = new TwitterOAuthToken( $consumer_key, $consumer_secret ); if( $access_key && $access_secret ){ $this->AccessToken = new TwitterOAuthToken( $access_key, $access_secret ); } return $this; } /** * Set consumer oauth token by object * @param TwitterOAuthToken * @return TwitterApiClient */ public function set_oauth_consumer( TwitterOAuthToken $token ){ $this->Consumer = $token; return $this; } /** * Set access oauth token by object * @param TwitterOAuthToken * @return TwitterApiClient */ public function set_oauth_access( TwitterOAuthToken $token ){ $this->AccessToken = $token; return $this; } /** * Contact Twitter for a request token, which will be exchanged for an access token later. * @param string callback URL or "oob" for desktop apps (out of bounds) * @return TwitterOAuthToken Request token */ public function get_oauth_request_token( $oauth_callback = 'oob' ){ $params = $this->oauth_exchange( TWITTER_OAUTH_REQUEST_TOKEN_URL, compact('oauth_callback') ); return new TwitterOAuthToken( $params['oauth_token'], $params['oauth_token_secret'] ); } /** * Exchange request token for an access token after authentication/authorization by user * @param string verifier passed back from Twitter or copied out of browser window * @return TwitterOAuthToken Access token */ public function get_oauth_access_token( $oauth_verifier ){ $params = $this->oauth_exchange( TWITTER_OAUTH_ACCESS_TOKEN_URL, compact('oauth_verifier') ); $token = new TwitterOAuthToken( $params['oauth_token'], $params['oauth_token_secret'] ); $token->user = array ( 'id' => $params['user_id'], 'screen_name' => $params['screen_name'], ); return $token; } /** * Basic sanitation of api request arguments * @param array original params passed by client code * @return array sanitized params that we'll serialize */ private function sanitize_args( array $_args ){ // transform some arguments and ensure strings // no further validation is performed $args = array(); foreach( $_args as $key => $val ){ if( is_string($val) ){ $args[$key] = $val; } else if( true === $val ){ $args[$key] = 'true'; } else if( false === $val || null === $val ){ $args[$key] = 'false'; } else if( ! is_scalar($val) ){ throw new TwitterApiException( 'Invalid Twitter parameter ('.gettype($val).') '.$key, -1 ); } else { $args[$key] = (string) $val; } } return $args; } /** * Call API method over HTTP and return serialized data * @param string API method, e.g. "users/show" * @param array method arguments * @param string http request method * @return array unserialized data returned from Twitter * @throws TwitterApiException */ public function call( $path, array $args = array(), $http_method = 'GET' ){ $args = $this->sanitize_args( $args ); // Fetch response from cache if possible / allowed / enabled if( $http_method === 'GET' && isset($this->cache_ttl) ){ $cachekey = $this->cache_ns.$path.'_'.md5( serialize($args) ); if( preg_match('/^(\d+)-/', $this->AccessToken->key, $reg ) ){ $cachekey .= '_'.$reg[1]; } $data = apc_fetch( $cachekey ); if( is_array($data) ){ return $data; } } $http = $this->rest_request( $path, $args, $http_method ); // Deserialize response $status = $http['status']; $data = json_decode( $http['body'], true ); // unserializable array assumed to be serious error if( ! is_array($data) ){ $err = array( 'message' => $http['error'], 'code' => -1 ); TwitterApiException::chuck( $err, $status ); } // else could be well-formed error if( isset( $data['errors'] ) ) { while( $err = array_shift($data['errors']) ){ $err['message'] = $err['message']; if( $data['errors'] ){ $message = sprintf('Twitter error #%d', $err['code'] ).' "'.$err['message'].'"'; trigger_error( $message, E_USER_WARNING ); } else { TwitterApiException::chuck( $err, $status ); } } } if( isset($cachekey) ){ apc_store( $cachekey, $data, $this->cache_ttl ); } return $data; } /** * Call API method over HTTP and return raw response data without caching * @param string API method, e.g. "users/show" * @param array method arguments * @param string http request method * @return array structure from http_request * @throws TwitterApiException */ public function raw( $path, array $args = array(), $http_method = 'GET' ){ $args = $this->sanitize_args( $args ); return $this->rest_request( $path, $args, $http_method ); } /** * Perform an OAuth request - these differ somewhat from regular API calls * @internal */ private function oauth_exchange( $endpoint, array $args ){ // build a post request and authenticate via OAuth header $params = new TwitterOAuthParams( $args ); $params->set_consumer( $this->Consumer ); if( $this->AccessToken ){ $params->set_token( $this->AccessToken ); } $params->sign_hmac( 'POST', $endpoint ); $conf = array ( 'method' => 'POST', 'headers' => array( 'Authorization' => $params->oauth_header() ), ); $http = self::http_request( $endpoint, $conf ); $body = trim( $http['body'] ); $stat = $http['status']; if( 200 !== $stat ){ // Twitter might respond as XML, but with an HTML content type for some reason if( 0 === strpos($body, '<?') ){ $xml = simplexml_load_string($body); $body = (string) $xml->error; } throw new TwitterApiException( $body, -1, $stat ); } parse_str( $body, $params ); if( ! is_array($params) || ! isset($params['oauth_token']) || ! isset($params['oauth_token_secret']) ){ throw new TwitterApiException( 'Malformed response from Twitter', -1, $stat ); } return $params; } /** * Sign and execute REST API call * @return array */ private function rest_request( $path, array $args, $http_method ){ // all calls must be authenticated in API 1.1 if( ! $this->has_auth() ){ throw new TwitterApiException( 'Twitter client not authenticated', 0, 401 ); } // prepare HTTP request config $conf = array ( 'method' => $http_method, ); // build signed URL and request parameters $endpoint = TWITTER_API_BASE.'/'.$path.'.json'; $params = new TwitterOAuthParams( $args ); $params->set_consumer( $this->Consumer ); $params->set_token( $this->AccessToken ); $params->sign_hmac( $http_method, $endpoint ); if( 'GET' === $http_method ){ $endpoint .= '?'.$params->serialize(); } else { $conf['body'] = $params->serialize(); } // var_dump($endpoint); // die('this is the args value'); $http = self::http_request( $endpoint, $conf ); // remember current rate limits for this endpoint $this->last_call = $path; if( isset($http['headers']['x-rate-limit-limit']) ) { $this->last_rate[$path] = array ( 'limit' => (int) $http['headers']['x-rate-limit-limit'], 'remaining' => (int) $http['headers']['x-rate-limit-remaining'], 'reset' => (int) $http['headers']['x-rate-limit-reset'], ); } return $http; } /** * Abstract HTTP call, currently just uses cURL extension * @return array e.g. { body: '', error: '', status: 200, headers: {} } */ public static function http_request( $endpoint, array $conf ){ $conf += array( 'body' => '', 'method' => 'GET', 'headers' => array(), ); $ch = curl_init(); // fix curl "error setting certificate verify locations: .." curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt( $ch, CURLOPT_URL, $endpoint ); curl_setopt( $ch, CURLOPT_TIMEOUT, TWITTER_API_TIMEOUT ); curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, TWITTER_API_TIMEOUT ); curl_setopt( $ch, CURLOPT_USERAGENT, TWITTER_API_USERAGENT ); curl_setopt( $ch, CURLOPT_HEADER, true ); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); switch ( $conf['method'] ) { case 'GET': break; case 'POST': curl_setopt( $ch, CURLOPT_POST, true ); curl_setopt( $ch, CURLOPT_POSTFIELDS, $conf['body'] ); break; default: throw new TwitterApiException('Unsupported method '.$conf['method'] ); } foreach( $conf['headers'] as $key => $val ){ $headers[] = $key.': '.$val; } if( isset($headers) ) { curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers ); } // execute and parse response $response = curl_exec( $ch ); if ( 60 === curl_errno($ch) ) { // CURLE_SSL_CACERT curl_setopt( $ch, CURLOPT_CAINFO, __DIR__.'/ca-chain-bundle.crt'); $response = curl_exec($ch); } $status = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); $headers = array(); $body = ''; if( $response && $status ){ list( $header, $body ) = preg_split('/\r\n\r\n/', $response, 2 ); if( preg_match_all('/^(Content[\w\-]+|X-Rate[^:]+):\s*(.+)/mi', $header, $r, PREG_SET_ORDER ) ){ foreach( $r as $match ){ $headers[ strtolower($match[1]) ] = $match[2]; } } curl_close($ch); } else { $error = curl_error( $ch ) or $error = 'No response from Twitter'; // var_dump($error); // die; is_resource($ch) and curl_close($ch); throw new TwitterApiException( $error ); } return array ( 'body' => $body, 'status' => $status, 'headers' => $headers, ); } /** * Get current rate limit, if known. does not look it up */ public function last_rate_limit_data( $func = '' ){ $func or $func = $this->last_call; return isset($this->last_rate[$func]) ? $this->last_rate[$func] : array( 'limit' => 0 ); } /** * Get rate limit allowance for last endpoint request */ public function last_rate_limit_allowance( $func = '' ){ $data = $this->last_rate_limit_data($func); return isset($data['limit']) ? $data['limit'] : null; } /** * Get number of requests remaining this period for last endpoint request */ public function last_rate_limit_remaining( $func = '' ){ $data = $this->last_rate_limit_data($func); return isset($data['remaining']) ? $data['remaining'] : null; } /** * Get rate limit reset time for last endpoint request */ public function last_rate_limit_reset( $func = '' ){ $data = $this->last_rate_limit_data($func); return isset($data['reset']) ? $data['reset'] : null; } } /** * Simple token class that holds key and secret * @internal */ class TwitterOAuthToken { public $key; public $secret; public $verifier; public $user; public function __construct( $key, $secret = '' ){ if( ! $key ){ throw new Exception( 'Invalid OAuth token - Key required even if secret is empty' ); } $this->key = $key; $this->secret = $secret; $this->verifier = ''; } public function get_authorization_url(){ return TWITTER_OAUTH_AUTHORIZE_URL.'?oauth_token='.rawurlencode($this->key); } public function get_authentication_url(){ return TWITTER_OAUTH_AUTHENTICATE_URL.'?oauth_token='.rawurlencode($this->key); } } /** * Class for compiling, signing and serializing OAuth parameters * @internal */ class TwitterOAuthParams { private $args; private $consumer_secret; private $token_secret; private static function urlencode( $val ){ return str_replace( '%7E', '~', rawurlencode($val) ); } public function __construct( array $args = array() ){ $this->args = $args + array ( 'oauth_version' => '1.0', ); } public function set_consumer( TwitterOAuthToken $Consumer ){ $this->consumer_secret = $Consumer->secret; $this->args['oauth_consumer_key'] = $Consumer->key; } public function set_token( TwitterOAuthToken $Token ){ $this->token_secret = $Token->secret; $this->args['oauth_token'] = $Token->key; } private function normalize(){ $flags = SORT_STRING | SORT_ASC; ksort( $this->args, $flags ); foreach( $this->args as $k => $a ){ if( is_array($a) ){ sort( $this->args[$k], $flags ); } } return $this->args; } public function serialize(){ $str = http_build_query( $this->args ); // PHP_QUERY_RFC3986 requires PHP >= 5.4 $str = str_replace( array('+','%7E'), array('%20','~'), $str ); return $str; } public function sign_hmac( $http_method, $http_rsc ){ $this->args['oauth_signature_method'] = 'HMAC-SHA1'; $this->args['oauth_timestamp'] = sprintf('%u', time() ); $this->args['oauth_nonce'] = sprintf('%f', microtime(true) ); unset( $this->args['oauth_signature'] ); $this->normalize(); $str = $this->serialize(); $str = strtoupper($http_method).'&'.self::urlencode($http_rsc).'&'.self::urlencode($str); $key = self::urlencode($this->consumer_secret).'&'.self::urlencode($this->token_secret); $this->args['oauth_signature'] = base64_encode( hash_hmac( 'sha1', $str, $key, true ) ); return $this->args; } public function oauth_header(){ $lines = array(); foreach( $this->args as $key => $val ){ $lines[] = self::urlencode($key).'="'.self::urlencode($val).'"'; } return 'OAuth '.implode( ",\n ", $lines ); } } /** * HTTP status codes with some overridden for Twitter-related messages. * Note these do not replace error text from Twitter, they're for complete API failures. * @param int HTTP status code * @return string HTTP status text */ function _twitter_api_http_status_text( $s ){ static $codes = array ( 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Authorization Required', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', // .. 429 => 'Twitter API rate limit exceeded', 500 => 'Twitter server error', 501 => 'Not Implemented', 502 => 'Twitter is not responding', 503 => 'Twitter is too busy to respond', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported', ); return isset($codes[$s]) ? $codes[$s] : sprintf('Status %u from Twitter', $s ); } /** * Exception for throwing when Twitter responds with something unpleasant */ class TwitterApiException extends Exception { /** * HTTP Status of error * @var int */ protected $status = 0; /** * Throw appropriate exception type according to HTTP status code * @param array Twitter error data from their response */ public static function chuck( array $err, $status ){ $code = isset($err['code']) ? (int) $err['code'] : -1; $mess = isset($err['message']) ? trim($err['message']) : ''; static $classes = array ( 404 => 'TwitterApiNotFoundException', 429 => 'TwitterApiRateLimitException', ); $eclass = isset($classes[$status]) ? $classes[$status] : __CLASS__; throw new $eclass( $mess, $code, $status ); } /** * Construct TwitterApiException with addition of HTTP status code. * @overload */ public function __construct( $message, $code = 0 ){ if( 2 < func_num_args() ){ $this->status = (int) func_get_arg(2); } if( ! $message ){ $message = _twitter_api_http_status_text($this->status); } parent::__construct( $message, $code ); } /** * Get HTTP status of error * @return int */ public function getStatus(){ return $this->status; } } /** 404 */ class TwitterApiNotFoundException extends TwitterApiException { } /** 429 */ class TwitterApiRateLimitException extends TwitterApiException { }