add settee
[disclosr.git] / couchdb / settee / src / classes / SetteeRestClient.class.php
blob:a/couchdb/settee/src/classes/SetteeRestClient.class.php -> blob:b/couchdb/settee/src/classes/SetteeRestClient.class.php
<?php <?php
   
/** /**
* HTTP REST Client for CouchDB API * HTTP REST Client for CouchDB API
*/ */
class SetteeRestClient { class SetteeRestClient {
/** /**
* HTTP Timeout in Milliseconds * HTTP Timeout in Milliseconds
*/ */
const HTTP_TIMEOUT = 2000; const HTTP_TIMEOUT = 2000;
private $base_url; private $base_url;
private $curl; private $curl;
private static $curl_workers = array(); private static $curl_workers = array();
   
/** /**
* Singleton factory method * Singleton factory method
*/ */
static function get_instance($base_url) { static function get_instance($base_url) {
   
if (empty(self::$curl_workers[$base_url])) { if (empty(self::$curl_workers[$base_url])) {
self::$curl_workers[$base_url] = new SetteeRestClient($base_url); self::$curl_workers[$base_url] = new SetteeRestClient($base_url);
} }
return self::$curl_workers[$base_url]; return self::$curl_workers[$base_url];
} }
/** /**
* Class constructor * Class constructor
*/ */
private function __construct($base_url) { private function __construct($base_url) {
$this->base_url = $base_url; $this->base_url = $base_url;
   
$curl = curl_init(); $curl = curl_init();
curl_setopt($curl, CURLOPT_USERAGENT, "Settee CouchDB Client/1.0"); curl_setopt($curl, CURLOPT_USERAGENT, "Settee CouchDB Client/1.0");
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HEADER, 0); curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl, CURLOPT_TIMEOUT_MS, self::HTTP_TIMEOUT); curl_setopt($curl, CURLOPT_TIMEOUT_MS, self::HTTP_TIMEOUT);
curl_setopt($curl, CURLOPT_FORBID_REUSE, false); // Connection-pool for CURL curl_setopt($curl, CURLOPT_FORBID_REUSE, false); // Connection-pool for CURL
   
$this->curl = $curl; $this->curl = $curl;
} }
   
/** /**
* Class destructor cleans up any resources * Class destructor cleans up any resources
*/ */
function __destruct() { function __destruct() {
curl_close($this->curl); curl_close($this->curl);
} }
   
/** /**
* HTTP HEAD * HTTP HEAD
* *
* @return * @return
* Raw HTTP Headers of the response. * Raw HTTP Headers of the response.
* *
* @see: http://www.php.net/manual/en/context.params.php * @see: http://www.php.net/manual/en/context.params.php
* *
*/ */
function http_head($uri) { function http_head($uri) {
curl_setopt($this->curl, CURLOPT_HEADER, 1); curl_setopt($this->curl, CURLOPT_HEADER, 1);
   
$full_url = $this->get_full_url($uri); $full_url = $this->get_full_url($uri);
curl_setopt($this->curl, CURLOPT_URL, $full_url); curl_setopt($this->curl, CURLOPT_URL, $full_url);
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'HEAD'); curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
curl_setopt($this->curl, CURLOPT_NOBODY, true); curl_setopt($this->curl, CURLOPT_NOBODY, true);
   
   
$response = curl_exec($this->curl); $response = curl_exec($this->curl);
// Restore default values // Restore default values
curl_setopt($this->curl, CURLOPT_NOBODY, false); curl_setopt($this->curl, CURLOPT_NOBODY, false);
curl_setopt($this->curl, CURLOPT_HEADER, false); curl_setopt($this->curl, CURLOPT_HEADER, false);
$resp_code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE); $resp_code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
if ($resp_code == 404 ) { if ($resp_code == 404 ) {
throw new SetteeRestClientException("Couch document not found at: '$full_url'"); throw new SetteeRestClientException("Couch document not found at: '$full_url'");
} }
   
if (function_exists('http_parse_headers')) { if (function_exists('http_parse_headers')) {
$headers = http_parse_headers($response); $headers = http_parse_headers($response);
} }
else { else {
$headers = $this->_http_parse_headers($response); $headers = $this->_http_parse_headers($response);
} }
return $headers; return $headers;
} }
   
/** /**
* Backup PHP impl. for when PECL http_parse_headers() function is not available * Backup PHP impl. for when PECL http_parse_headers() function is not available
* *
* @param $header * @param $header
* @return array * @return array
* @source http://www.php.net/manual/en/function.http-parse-headers.php#77241 * @source http://www.php.net/manual/en/function.http-parse-headers.php#77241
*/ */
private function _http_parse_headers( $header ) { private function _http_parse_headers( $header ) {
$retVal = array(); $retVal = array();
$fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header)); $fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header));
foreach( $fields as $field ) { foreach( $fields as $field ) {
if( preg_match('/([^:]+): (.+)/m', $field, $match) ) { if( preg_match('/([^:]+): (.+)/m', $field, $match) ) {
$match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1]))); $match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1])));
if( isset($retVal[$match[1]]) ) { if( isset($retVal[$match[1]]) ) {
$retVal[$match[1]] = array($retVal[$match[1]], $match[2]); $retVal[$match[1]] = array($retVal[$match[1]], $match[2]);
} else { } else {
$retVal[$match[1]] = trim($match[2]); $retVal[$match[1]] = trim($match[2]);
} }
} }
} }
return $retVal; return $retVal;
} }
   
/** /**
* HTTP GET * HTTP GET
*/ */
function http_get($uri, $data = array()) { function http_get($uri, $data = array()) {
$data = (is_array($data)) ? http_build_query($data) : $data; $data = (is_array($data)) ? http_build_query($data) : $data;
if (!empty($data)) { if (!empty($data)) {
$uri .= "?$data"; $uri .= "?$data";
} }
return $this->http_request('GET', $uri); return $this->http_request('GET', $uri);
} }
/** /**
* HTTP PUT * HTTP PUT
*/ */
function http_put($uri, $data = array()) { function http_put($uri, $data = array()) {
return $this->http_request('PUT', $uri, $data); return $this->http_request('PUT', $uri, $data);
} }
   
/** /**
* HTTP DELETE * HTTP DELETE
*/ */
function http_delete($uri, $data = array()) { function http_delete($uri, $data = array()) {
return $this->http_request('DELETE', $uri, $data); return $this->http_request('DELETE', $uri, $data);
} }
   
/** /**
* Generic implementation of a HTTP Request. * Generic implementation of a HTTP Request.
* *
* @param $http_method * @param $http_method
* @param $uri * @param $uri
* @param array $data * @param array $data
* @return * @return
* an array containing json and decoded versions of the response. * an array containing json and decoded versions of the response.
*/ */
private function http_request($http_method, $uri, $data = array()) { private function http_request($http_method, $uri, $data = array()) {
$data = (is_array($data)) ? http_build_query($data) : $data; $data = (is_array($data)) ? http_build_query($data) : $data;
   
if (!empty($data)) { if (!empty($data)) {
curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Content-Length: ' . strlen($data))); curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Content-Length: ' . strlen($data)));
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $data); curl_setopt($this->curl, CURLOPT_POSTFIELDS, $data);
} }
   
curl_setopt($this->curl, CURLOPT_URL, $this->get_full_url($uri)); curl_setopt($this->curl, CURLOPT_URL, $this->get_full_url($uri));
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $http_method); curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $http_method);
   
$response = curl_exec($this->curl); $response = curl_exec($this->curl);
$response_decoded = $this->decode_response($response); $response_decoded = $this->decode_response($response);
$response = array('json' => $response, 'decoded'=>$response_decoded); $response = array('json' => $response, 'decoded'=>$response_decoded);
   
$this->check_status($response,$uri); $this->check_status($response,$uri);
   
return $response; return $response;
} }
/** /**
* Check http status for safe return codes * Check http status for safe return codes
* *
* @throws SetteeRestClientException * @throws SetteeRestClientException
*/ */
private function check_status($response,$uri) { private function check_status($response,$uri) {
$resp_code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE); $resp_code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
   
if ($resp_code < 199 || $resp_code > 399 || !empty($response['decoded']->error)) { if ($resp_code < 199 || $resp_code > 399 || !empty($response['decoded']->error)) {
$msg = "CouchDB returned: \"HTTP 1.1. $resp_code\". ERROR: " . $response['json'] . $uri; $msg = "CouchDB returned: \"HTTP 1.1. $resp_code\". ERROR: " . $response['json'] . $uri;
throw new SetteeRestClientException($msg); throw new SetteeRestClientException($msg);
} }
} }
   
/** /**
* @param $path * @param $path
* Full path to a file (e.g. as returned by PHP's realpath function). * Full path to a file (e.g. as returned by PHP's realpath function).
* @return void * @return void
*/ */
public function file_mime_type ($path) { public function file_mime_type ($path) {
$ftype = 'application/octet-stream'; $ftype = 'application/octet-stream';
if (function_exists("finfo_file")) { if (function_exists("finfo_file")) {
$finfo = new finfo(FILEINFO_MIME_TYPE | FILEINFO_SYMLINK); $finfo = new finfo(FILEINFO_MIME_TYPE | FILEINFO_SYMLINK);
$fres = $finfo->file($path); $fres = $finfo->file($path);
if (is_string($fres) && !empty($fres)) { if (is_string($fres) && !empty($fres)) {
$ftype = $fres; $ftype = $fres;
} }
} }
   
return $ftype; return $ftype;
} }
   
/** /**
* @param $content * @param $content
* content of a file in a string buffer format. * content of a file in a string buffer format.
* @return void * @return void
*/ */
public function content_mime_type ($content) { public function content_mime_type ($content) {
$ftype = 'application/octet-stream'; $ftype = 'application/octet-stream';
   
if (function_exists("finfo_file")) { if (function_exists("finfo_file")) {
$finfo = new finfo(FILEINFO_MIME_TYPE | FILEINFO_SYMLINK); $finfo = new finfo(FILEINFO_MIME_TYPE | FILEINFO_SYMLINK);
$fres = $finfo->buffer($content); $fres = $finfo->buffer($content);
if (is_string($fres) && !empty($fres)) { if (is_string($fres) && !empty($fres)) {
$ftype = $fres; $ftype = $fres;
} }
} }
   
return $ftype; return $ftype;
} }
   
/** /**
* *
* @param $json * @param $json
* json-encoded response from CouchDB * json-encoded response from CouchDB
* *
* @return * @return
* decoded PHP object * decoded PHP object
*/ */
private function decode_response($json) { private function decode_response($json) {
return json_decode($json); return json_decode($json);
} }
   
/** /**
* Get full URL from a partial one * Get full URL from a partial one
*/ */
private function get_full_url($uri) { private function get_full_url($uri) {
// We do not want "/", "?", "&" and "=" separators to be encoded!!! // We do not want "/", "?", "&" and "=" separators to be encoded!!!
$uri = str_replace(array('%2F', '%3F', '%3D', '%26'), array('/', '?', '=', '&'), urlencode($uri)); $uri = str_replace(array('%2F', '%3F', '%3D', '%26'), array('/', '?', '=', '&'), urlencode($uri));
return $this->base_url . '/' . $uri; return $this->base_url . '/' . $uri;
} }
} }
   
class SetteeRestClientException extends Exception {} class SetteeRestClientException extends Exception {}