Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
886 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
<?php | ||
class EpiCurl | ||
{ | ||
const timeout = 3; | ||
static $inst = null; | ||
static $singleton = 0; | ||
private $mc; | ||
private $msgs; | ||
private $running; | ||
private $requests = array(); | ||
private $responses = array(); | ||
private $properties = array(); | ||
|
||
function __construct() | ||
{ | ||
if(self::$singleton == 0) | ||
{ | ||
throw new Exception('This class cannot be instantiated by the new keyword. You must instantiate it using: $obj = EpiCurl::getInstance();'); | ||
} | ||
|
||
$this->mc = curl_multi_init(); | ||
$this->properties = array( | ||
'code' => CURLINFO_HTTP_CODE, | ||
'time' => CURLINFO_TOTAL_TIME, | ||
'length'=> CURLINFO_CONTENT_LENGTH_DOWNLOAD, | ||
'type' => CURLINFO_CONTENT_TYPE | ||
); | ||
} | ||
|
||
public function addCurl($ch) | ||
{ | ||
$key = (string)$ch; | ||
$this->requests[$key] = $ch; | ||
|
||
$res = curl_multi_add_handle($this->mc, $ch); | ||
|
||
// (1) | ||
if($res === CURLM_OK || $res === CURLM_CALL_MULTI_PERFORM) | ||
{ | ||
do { | ||
$mrc = curl_multi_exec($this->mc, $active); | ||
} while ($mrc === CURLM_CALL_MULTI_PERFORM); | ||
|
||
return new EpiCurlManager($key); | ||
} | ||
else | ||
{ | ||
return $res; | ||
} | ||
} | ||
|
||
public function getResult($key = null) | ||
{ | ||
if($key != null) | ||
{ | ||
if(isset($this->responses[$key])) | ||
{ | ||
return $this->responses[$key]; | ||
} | ||
|
||
$running = null; | ||
do | ||
{ | ||
$resp = curl_multi_exec($this->mc, $runningCurrent); | ||
if($running !== null && $runningCurrent != $running) | ||
{ | ||
$this->storeResponses($key); | ||
if(isset($this->responses[$key])) | ||
{ | ||
return $this->responses[$key]; | ||
} | ||
} | ||
$running = $runningCurrent; | ||
}while($runningCurrent > 0); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private function storeResponses() | ||
{ | ||
while($done = curl_multi_info_read($this->mc)) | ||
{ | ||
$key = (string)$done['handle']; | ||
$this->responses[$key]['data'] = curl_multi_getcontent($done['handle']); | ||
foreach($this->properties as $name => $const) | ||
{ | ||
$this->responses[$key][$name] = curl_getinfo($done['handle'], $const); | ||
curl_multi_remove_handle($this->mc, $done['handle']); | ||
} | ||
} | ||
} | ||
|
||
static function getInstance() | ||
{ | ||
if(self::$inst == null) | ||
{ | ||
self::$singleton = 1; | ||
self::$inst = new EpiCurl(); | ||
} | ||
|
||
return self::$inst; | ||
} | ||
} | ||
|
||
class EpiCurlManager | ||
{ | ||
private $key; | ||
private $epiCurl; | ||
|
||
function __construct($key) | ||
{ | ||
$this->key = $key; | ||
$this->epiCurl = EpiCurl::getInstance(); | ||
} | ||
|
||
function __get($name) | ||
{ | ||
$responses = $this->epiCurl->getResult($this->key); | ||
return $responses[$name]; | ||
} | ||
} | ||
|
||
/* | ||
* Credits: | ||
* - (1) Alistair pointed out that curl_multi_add_handle can return CURLM_CALL_MULTI_PERFORM on success. | ||
*/ | ||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
<?php | ||
class EpiOAuth | ||
{ | ||
public $version = '1.0'; | ||
|
||
protected $requestTokenUrl; | ||
protected $accessTokenUrl; | ||
protected $authenticateUrl; | ||
protected $authorizeUrl; | ||
protected $consumerKey; | ||
protected $consumerSecret; | ||
protected $token; | ||
protected $tokenSecret; | ||
protected $signatureMethod; | ||
|
||
public function getAccessToken() | ||
{ | ||
$resp = $this->httpRequest('GET', $this->accessTokenUrl); | ||
return new EpiOAuthResponse($resp); | ||
} | ||
|
||
public function getAuthenticateUrl() | ||
{ | ||
$token = $this->getRequestToken(); | ||
return $this->authenticateUrl . '?oauth_token=' . $token->oauth_token; | ||
} | ||
|
||
public function getAuthorizationUrl() | ||
{ | ||
$token = $this->getRequestToken(); | ||
return $this->authorizeUrl . '?oauth_token=' . $token->oauth_token; | ||
} | ||
|
||
public function getRequestToken() | ||
{ | ||
$resp = $this->httpRequest('GET', $this->requestTokenUrl); | ||
return new EpiOAuthResponse($resp); | ||
} | ||
|
||
public function httpRequest($method = null, $url = null, $params = null) | ||
{ | ||
if(empty($method) || empty($url)) | ||
return false; | ||
|
||
if(empty($params['oauth_signature'])) | ||
$params = $this->prepareParameters($method, $url, $params); | ||
|
||
switch($method) | ||
{ | ||
case 'GET': | ||
return $this->httpGet($url, $params); | ||
break; | ||
case 'POST': | ||
return $this->httpPost($url, $params); | ||
break; | ||
} | ||
} | ||
|
||
public function setToken($token = null, $secret = null) | ||
{ | ||
$params = func_get_args(); | ||
$this->token = $token; | ||
$this->tokenSecret = $secret; | ||
} | ||
|
||
protected function encode_rfc3986($string) | ||
{ | ||
return str_replace('+', ' ', str_replace('%7E', '~', rawurlencode(($string)))); | ||
} | ||
|
||
protected function addOAuthHeaders(&$ch, $url, $oauthHeaders) | ||
{ | ||
$_h = array('Expect:'); | ||
$urlParts = parse_url($url); | ||
$oauth = 'Authorization: OAuth realm="' . $urlParts['path'] . '",'; | ||
foreach($oauthHeaders as $name => $value) | ||
{ | ||
$oauth .= "{$name}=\"{$value}\","; | ||
} | ||
$_h[] = substr($oauth, 0, -1); | ||
|
||
curl_setopt($ch, CURLOPT_HTTPHEADER, $_h); | ||
} | ||
|
||
protected function generateNonce() | ||
{ | ||
if(isset($this->nonce)) // for unit testing | ||
return $this->nonce; | ||
|
||
return md5(uniqid(rand(), true)); | ||
} | ||
|
||
protected function generateSignature($method = null, $url = null, $params = null) | ||
{ | ||
if(empty($method) || empty($url)) | ||
return false; | ||
|
||
|
||
// concatenating | ||
$concatenatedParams = ''; | ||
foreach($params as $k => $v) | ||
{ | ||
$v = $this->encode_rfc3986($v); | ||
$concatenatedParams .= "{$k}={$v}&"; | ||
} | ||
$concatenatedParams = $this->encode_rfc3986(substr($concatenatedParams, 0, -1)); | ||
|
||
// normalize url | ||
$normalizedUrl = $this->encode_rfc3986($this->normalizeUrl($url)); | ||
$method = $this->encode_rfc3986($method); // don't need this but why not? | ||
$signatureBaseString = "{$method}&{$normalizedUrl}&{$concatenatedParams}"; | ||
return $this->signString($signatureBaseString); | ||
} | ||
|
||
protected function httpGet($url, $params = null) | ||
{ | ||
if(count($params['request']) > 0) | ||
{ | ||
$url .= '?'; | ||
foreach($params['request'] as $k => $v) | ||
{ | ||
$url .= "{$k}={$v}&"; | ||
} | ||
$url = substr($url, 0, -1); | ||
} | ||
$ch = curl_init($url); | ||
$this->addOAuthHeaders($ch, $url, $params['oauth']); | ||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | ||
$resp = $this->curl->addCurl($ch); | ||
|
||
return $resp; | ||
} | ||
|
||
protected function httpPost($url, $params = null) | ||
{ | ||
$ch = curl_init($url); | ||
$this->addOAuthHeaders($ch, $url, $params['oauth']); | ||
curl_setopt($ch, CURLOPT_POST, 1); | ||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params['request'])); | ||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | ||
$resp = $this->curl->addCurl($ch); | ||
return $resp; | ||
} | ||
|
||
protected function normalizeUrl($url = null) | ||
{ | ||
$urlParts = parse_url($url); | ||
$scheme = strtolower($urlParts['scheme']); | ||
$host = strtolower($urlParts['host']); | ||
$port = intval($urlParts['port']); | ||
|
||
$retval = "{$scheme}://{$host}"; | ||
if($port > 0 && ($scheme === 'http' && $port !== 80) || ($scheme === 'https' && $port !== 443)) | ||
{ | ||
$retval .= ":{$port}"; | ||
} | ||
$retval .= $urlParts['path']; | ||
if(!empty($urlParts['query'])) | ||
{ | ||
$retval .= "?{$urlParts['query']}"; | ||
} | ||
|
||
return $retval; | ||
} | ||
|
||
protected function prepareParameters($method = null, $url = null, $params = null) | ||
{ | ||
if(empty($method) || empty($url)) | ||
return false; | ||
|
||
$oauth['oauth_consumer_key'] = $this->consumerKey; | ||
$oauth['oauth_token'] = $this->token; | ||
$oauth['oauth_nonce'] = $this->generateNonce(); | ||
$oauth['oauth_timestamp'] = !isset($this->timestamp) ? time() : $this->timestamp; // for unit test | ||
$oauth['oauth_signature_method'] = $this->signatureMethod; | ||
$oauth['oauth_version'] = $this->version; | ||
|
||
// encoding | ||
array_walk($oauth, array($this, 'encode_rfc3986')); | ||
if(is_array($params)) | ||
array_walk($params, array($this, 'encode_rfc3986')); | ||
$encodedParams = array_merge($oauth, (array)$params); | ||
|
||
// sorting | ||
ksort($encodedParams); | ||
|
||
// signing | ||
$oauth['oauth_signature'] = $this->encode_rfc3986($this->generateSignature($method, $url, $encodedParams)); | ||
return array('request' => $params, 'oauth' => $oauth); | ||
} | ||
|
||
protected function signString($string = null) | ||
{ | ||
$retval = false; | ||
switch($this->signatureMethod) | ||
{ | ||
case 'HMAC-SHA1': | ||
$key = $this->encode_rfc3986($this->consumerSecret) . '&' . $this->encode_rfc3986($this->tokenSecret); | ||
$retval = base64_encode(hash_hmac('sha1', $string, $key, true)); | ||
break; | ||
} | ||
|
||
return $retval; | ||
} | ||
|
||
public function __construct($consumerKey, $consumerSecret, $signatureMethod='HMAC-SHA1') | ||
{ | ||
$this->consumerKey = $consumerKey; | ||
$this->consumerSecret = $consumerSecret; | ||
$this->signatureMethod = $signatureMethod; | ||
$this->curl = EpiCurl::getInstance(); | ||
} | ||
} | ||
|
||
class EpiOAuthResponse | ||
{ | ||
private $__resp; | ||
|
||
public function __construct($resp) | ||
{ | ||
$this->__resp = $resp; | ||
} | ||
|
||
public function __get($name) | ||
{ | ||
if($this->__resp->code != 200) | ||
EpiOAuthException::raise($this->__resp->data, $this->__resp->code); | ||
|
||
parse_str($this->__resp->data, $result); | ||
foreach($result as $k => $v) | ||
{ | ||
$this->$k = $v; | ||
} | ||
|
||
return $result[$name]; | ||
} | ||
} | ||
|
||
class EpiOAuthException extends Exception | ||
{ | ||
public static function raise($message, $code) | ||
{ | ||
switch($code) | ||
{ | ||
case 400: | ||
throw new EpiOAuthBadRequestException($message, $code); | ||
case 401: | ||
throw new EpiOAuthUnauthorizedException($message, $code); | ||
default: | ||
throw new EpiOAuthException($message, $code); | ||
} | ||
} | ||
} | ||
|
||
|
||
class EpiOAuthBadRequestException extends EpiOAuthException{} | ||
class EpiOAuthUnauthorizedException extends EpiOAuthException{} |
Oops, something went wrong.