uid = $uid; } /** * @var string The user secret assoicataed with the UID for the user * accessing the Thumber API. */ protected $userSecret; /** * @param $userSecret string The user secret assoicataed with the UID for the user * accessing the Thumber API. */ public function setUserSecret($userSecret) { $this->userSecret = $userSecret; } /** * @var string Fully-qualified system path to this file. */ private $thumberClientPath; /** * @var object The subscription object. */ private static $subscription; /** * @var array The supported MIME types. */ private static $mime_types; /** * @var ThumberClient Backs the getter. */ private static $instance; /** * @return ThumberClient The singleton instance. */ public static function getInstance() { return isset( self::$instance ) ? self::$instance : ( self::$instance = new ThumberClient() ); } /** * Initialized class members. */ protected function __construct() { $this->thumberUserAgent = 'Thumber Client 1.0 (PHP ' . phpversion() . '; ' . php_uname() . ')'; $this->thumberClientPath = dirname( __FILE__ ) . '/'; } /** * Sends the provided request to Thumber for processing. * * NOTE: Caller should consider testing file size against getSubscription()->file_size_limit to avoid unnecessary * HTTP requests when the file will be rejected. * * @param ThumberThumbReq $req The request to be sent. * @return array The results of sending the request. */ public function sendThumbRequest($req) { if (!($req instanceof ThumberThumbReq)) { $err = 'Request must be of type ThumberThumbReq.'; $this->handleError($err); return array('error' => $err); } return $this->sendRequest($req, 'POST', self::ThumberServerCreatePath); } /** * Processes the POST request, generating a ThumberResponse, validating, and passing the result to $callback. * If not using client.php as the webhook, whoever receives webhook response should first invoke this method to * validate response. */ public function receiveThumbResponse() { include_once $this->thumberClientPath . 'thumb-response.php'; $json = stream_get_contents(fopen('php://input', 'r')); $resp = ThumberThumbResp::parseJson($json); if (is_null($resp)) { $this->handleError('Failed to parse JSON in POST body: ' . $json); return null; } if (!$resp->isValid($this->userSecret)) { $this->handleError('Received invalid response: ' . $json); return null; } // This method should be overridden in order to use response return $resp; } /** * @return object|null Returns an object with subscription information. Object includes the following: * name, monthly_cost, watermark, direct_upload, thumbs_per_hr_limit, file_size_limit, and thumb_size_limit. */ public function getSubscription() { include_once $this->thumberClientPath . 'base-transaction.php'; if (isset(self::$subscription)) { return self::$subscription; } else { $resp = $this->sendRequest(new ThumberBaseTransaction(), 'GET', self::ThumberSubscriptionPath); if (!array_key_exists('error', $resp)) { self::$subscription = json_decode($resp['body']); return self::$subscription; } else { $this->handleError('Failed to retrieve subscription: ' . $resp['body']); return null; } } } /** * Retrieves the supported MIME types from Thumber. * @return array The supported MIME types reported by the Thumber server. */ public function getMimeTypes() { if (!isset(self::$mime_types)) { $headers = array('Content-Type: application/json', 'Content-Length: 0'); $result = $this->sendToThumber('GET', self::getThumberUrl(self::ThumberServerMimeTypesPath), $headers); self::$mime_types = !array_key_exists('error', $result) ? json_decode($result['body'], true) : array(); } return self::$mime_types; } /** * Sends the provided request to the API endpoint. * * @param ThumberBaseTransaction $req The request to be sent. UID, callback, and timestamp * will be written by client. Additionally, nonce will be set if not already set. * @param string $type The type of HTTP request (GET/POST/etc.) * @param string $path The relative path on Thumber server to contact. * @return array containing data about success of the cURL request. */ public function sendRequest($req, $type, $path) { include_once $this->thumberClientPath . 'base-transaction.php'; if (!is_a($req, 'ThumberBaseTransaction')) { $err = 'Request must be of type ThumberBaseTransaction.'; $this->handleError($err); return array('error' => $err); } // whether we've changed any field, requiring recompute of checksum $changed = false; $timestamp = $req->getTimestamp(); if (empty($timestamp)) { $req->setTimestamp(time()); $changed = true; } $uid = $req->getUid(); if (empty($uid)) { $req->setUid($this->uid); $changed = true; } $nonce = $req->getNonce(); if (empty($nonce)) { $req->setNonce(); $changed = true; } $checksum = $req->getChecksum(); if ($changed || empty($checksum)) { $req->setChecksum($req->computeChecksum($this->userSecret)); } if (!$req->isValid($this->userSecret)) { $err = 'Invalid request provided.'; $this->handleError($err); return array('error' => $err); } $body = ''; if ($type == 'GET') { $path .= '?' . $req->toQuery(); } else { $body = $req->toJson(); } $headers = array('Content-Type: application/json', 'Content-Length: ' . strlen($body)); $result = $this->sendToThumber($type, self::getThumberUrl($path), $headers, $body); $result['nonce'] = !array_key_exists('error', $result) ? $req->getNonce() : ''; // caller should handle errors sensibly return $result; } /** * Sends cURL request to Thumber server. * @param $type string GET or POST * @param $url string The URL endpoint being targeted. * @param $httpHeaders array The headers to be sent. * @param $body string The POST body. Ignored if type is GET. * @return array The result of the request. */ protected function sendToThumber($type, $url, $httpHeaders, $body = '') { // open connection $ch = curl_init(); curl_setopt($ch, CURLOPT_USERAGENT, $this->thumberUserAgent); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders); if ($type == 'POST') { curl_setopt($ch, CURLOPT_POSTFIELDS, $body); } // execute post, storing useful information about result $response = curl_exec($ch); $error = curl_error($ch); $result = array ( 'header' => '', 'body' => '', 'error' => '', 'http_code' => '', 'last_url' => '' ); if ($error === '') { $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $result['header'] = substr($response, 0, $header_size); $result['body'] = substr($response, $header_size); $result['http_code'] = curl_getinfo($ch, CURLINFO_HTTP_CODE); $result['last_url'] = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); } else { $result ['error'] = $error; } curl_close($ch); return $result; } /** * @param string $path The path at Thumber to generate URL from. * @return string The full URL to the given relative path. */ private static function getThumberUrl($path) { return 'http://' . self::ThumberServerHost . $path; } /** * @param $err string Fires on fatal error. */ protected function handleError($err) { echo $err; } }