Permalink
Browse files

Merge remote branch 'origin/master'

  • Loading branch information...
2 parents 71f1ac6 + 385942a commit e01ad0fa0bf4994540dbadfe7658fc2b31e41a25 @sbisbee committed Nov 7, 2011
View
@@ -8,21 +8,34 @@ New Features
* The HTTP socket code was abstracted out of the core Sag class into its own
HTTP module. It is called SagNativeHTTPAdapter and remains the default
- transport mechanism. This allows for additional drivers to be written that
- leverage other underlying systems, potentially adding extra functionality.
- The SagNativeHTTPAdapter is referenced by the Sag::$NATIVE_HTTP_ADAPTER
- static public variable and can be specified with the setHTTPAdapter()
- function.
+ transport mechanism. This allows for drivers to be written that leverage
+ different HTTP libraries, potentially adding extra functionality. The
+ SagNativeHTTPAdapter is referenced by the Sag::$HTTP_NATIVE_SOCKETS static
+ public variable and can be specified with the setHTTPAdapter() function.
+ (closes #12)
- * cURL support was added with the SagCURLHTTPAdapter. If your system has the
- cURL PHP extension installed then you tell Sag to leverage cURL by calling
- `$sag->setHTTPAdapter(Sag::$CURL_HTTP_ADAPTER)`.
+ * cURL can now be used when communicating with CouchDB, allowing for
+ additional functionality. If your system has the cURL PHP extension
+ installed then you can tell Sag to use it by calling
+ `$sag->setHTTPAdapter(Sag::$HTTP_CURL);`.
* SSL is now supported if you use cURL instead of native sockets. This
introduces setSSL() to turn SSL on/off, and setSSLCert() to specify a
certificate file to verify against. Verification is only supported if you
provide a certificate with setSSLCert(). HTTP libraries that do not support
- SSL (native sockets) will throw a SagException.
+ SSL (ie., native sockets) will throw a SagException if you call an SSL
+ function.
+
+ * You can now tell getAllDocs() to sort in descending order. Thanks to cygal
+ (github/cygal) for the patch and pull request. (closes #31)
+
+Fixed Bugs
+
+ * If you set the database name and then set it again with the same value, but
+ also specify to create the database if it doesn't exist, Sag was previously
+ not checking to see if it should create the database. This is not fixed:
+ even if you specify the currentDatabase() value as the name the check will
+ still run. (closes #33)
Version 0.6.1
-------------
View
@@ -38,8 +38,8 @@ class Sag {
*/
public static $AUTH_COOKIE = "AUTH_COOKIE";
- public static $NATIVE_HTTP_ADAPTER = 'NATIVE_HTTP_ADAPTER';
- public static $CURL_HTTP_ADAPTER = 'CURL_HTTP_ADAPTER';
+ public static $HTTP_NATIVE_SOCKETS = 'HTTP_NATIVE_SOCKETS';
+ public static $HTTP_CURL = 'HTTP_CURL';
private $db; //Database name to hit.
private $host; //IP or address to connect to.
@@ -50,10 +50,6 @@ class Sag {
private $authType; //One of the Sag::$AUTH_* variables
private $authSession; //AuthSession cookie value from/for CouchDB
- private $socketOpenTimeout; //The seconds until socket connection timeout
- private $socketRWTimeoutSeconds; //The seconds for socket I/O timeout
- private $socketRWTimeoutMicroseconds; //The microseconds for socket I/O timeout
-
private $cache;
private $staleDefault; //Whether or not to use ?stale=ok on all design doc calls
@@ -81,41 +77,71 @@ public function __construct($host = "127.0.0.1", $port = "5984")
$this->setHTTPAdapter();
}
+ /**
+ * Set which HTTP library you want to use for communicating with CouchDB.
+ *
+ * @param string $type The type of adapter you want to use. Should be one of
+ * the Sag::$HTTP_* variables.
+ * @return Sag Returns $this.
+ *
+ * @see Sag::$HTTP_NATIVE_SOCKETS
+ * @see Sag::$HTTP_CURL
+ */
public function setHTTPAdapter($type = null) {
if(!$type) {
- $type = self::$NATIVE_HTTP_ADAPTER;
+ $type = self::$HTTP_NATIVE_SOCKETS;
}
- //nothing to be done
+ // nothing to be done
if($type === $this->httpAdapterType) {
return true;
}
- //remember what was already set (ie., might have called decode() already)
+ // remember what was already set (ie., might have called decode() already)
if($this->httpAdapter) {
$prevDecode = $this->httpAdapter->decodeResp;
+ $prevTimeouts = $this->httpAdapter->getTimeouts();
}
+ // the glue
switch($type) {
- case self::$NATIVE_HTTP_ADAPTER:
+ case self::$HTTP_NATIVE_SOCKETS:
$this->httpAdapter = new SagNativeHTTPAdapter($this->host, $this->port);
break;
- case self::$CURL_HTTP_ADAPTER:
+ case self::$HTTP_CURL:
$this->httpAdapter = new SagCURLHTTPAdapter($this->host, $this->port);
break;
default:
throw SagException("Invalid Sag HTTP adapter specified: $type");
}
- if($prevDecode) {
+ // restore previous decode value, if any
+ if(is_bool($prevDecode)) {
$this->httpAdapter->decodeResp = $prevDecode;
}
+ // restore previous timeout vlaues, if any
+ if(is_array($prevTimeouts)) {
+ $this->httpAdapter->setTimeoutsFromArray($prevTimeouts);
+ }
+
+ $this->httpAdapterType = $type;
+
return $this;
}
+ /**
+ * Returns the current HTTP adapter being used.
+ *
+ * @return string Will be equal to Sag::$HTTP_NATIVE_SOCKETS or
+ * Sag::$HTTP_CURL.
+ */
+ public function currentHTTPAdapter() {
+ return $this->httpAdapterType;
+ }
+
/**
* Updates the login credentials in Sag that will be used for all further
* communications. Pass null to both $user and $pass to turn off
@@ -489,7 +515,7 @@ public function copy($srcID, $dstID, $dstRev = null) {
* @return Sag Returns $this. Throws on failure.
*/
public function setDatabase($db, $createIfNotFound = false) {
- if($this->db != $db) {
+ if($this->db != $db || $createIfNotFound) {
if(!is_string($db)) {
throw new SagException('setDatabase() expected a string.');
}
@@ -528,10 +554,12 @@ public function setDatabase($db, $createIfNotFound = false) {
* @param string $endKey The endkey variable (valid JSON). Defaults to null.
* @param array $keys An array of keys (strings) of the specific documents
* you're trying to get.
+ * @param bool $descending Whether to sort the results in descending order or
+ * not.
*
* @return mixed
*/
- public function getAllDocs($incDocs = false, $limit = null, $startKey = null, $endKey = null, $keys = null) {
+ public function getAllDocs($incDocs = false, $limit = null, $startKey = null, $endKey = null, $keys = null, $descending = false) {
if(!$this->db) {
throw new SagException('No database specified.');
}
@@ -570,6 +598,14 @@ public function getAllDocs($incDocs = false, $limit = null, $startKey = null, $e
$qry[] = 'limit='.urlencode($limit);
}
+ if($descending !== false) {
+ if(!is_bool($descending)) {
+ throw new SagException('getAllDocs() expected a boolean for descending.');
+ }
+
+ $qry[] = "descending=true";
+ }
+
$qry = '?'.implode('&', $qry);
if(isset($keys))
@@ -766,26 +802,22 @@ public function setAttachment($name, $data, $contentType, $docID, $rev = null) {
}
/**
- * Sets the connection timeout on the socket. See setOpenTimeout() for
- * settings the read/write timeout.
+ * Sets how long Sag should wait to establish a connection to CouchDB.
*
* @param int $seconds
* @return Sag Returns $this.
*/
public function setOpenTimeout($seconds) {
- if(!is_int($seconds) || $seconds < 1) {
- throw new Exception('setOpenTimeout() expects a positive integer.');
- }
-
- $this->socketOpenTimeout = $seconds;
+ //the adapter will take care of the validation for us
+ $this->httpAdapter->setOpenTimeout($seconds);
return $this;
}
/**
- * Sets the read/write timeout period on the socket to the sum of seconds and
- * microseconds. If not set, then the default_socket_timeout setting is used
- * from your php.ini config.
+ * How long Sag should wait to execute a request with CouchDB. If not set,
+ * then either default_socket_timeout from your php.ini or cURL's defaults
+ * are used depending on which adapter you're using.
*
* Use setOpenTimeout() to set the timeout on opening the socket.
*
@@ -794,24 +826,7 @@ public function setOpenTimeout($seconds) {
* @return Sag Returns $this.
*/
public function setRWTimeout($seconds, $microseconds = 0) {
- if(!is_int($microseconds) || $microseconds < 0) {
- throw new SagException('setRWTimeout() expects $microseconds to be an integer >= 0.');
- }
-
- //TODO make this better, including checking $microseconds
- //$seconds can be 0 if $microseconds > 0
- if(
- !is_int($seconds) ||
- (
- (!$microseconds && $seconds < 1) ||
- ($microseconds && $seconds < 0)
- )
- ) {
- throw new SagException('setRWTimeout() expects $seconds to be a positive integer.');
- }
-
- $this->socketRWTimeoutSeconds = $seconds;
- $this->socketRWTimeoutMicroseconds = $microseconds;
+ $this->httpAdapter->setRWTimeout($seconds, $microseconds);
return $this;
}
@@ -11,6 +11,10 @@ class SagCURLHTTPAdapter extends SagHTTPAdapter {
private $ch;
public function __construct($host, $port) {
+ if(!extension_loaded('curl')) {
+ throw new SagException('Sag cannot use cURL on this system: the PHP cURL extension is not installed.');
+ }
+
parent::__construct($host, $port);
$this->ch = curl_init();
@@ -29,24 +33,39 @@ public function procPacket($method, $url, $data = null, $headers = array()) {
);
// cURL wants the headers as an array of strings, not an assoc array
- if(sizeof($headers) > 0) {
- $curlHeaders = array();
+ if(is_array($headers) && sizeof($headers) > 0) {
+ $opts[CURLOPT_HTTPHEADER] = array();
foreach($headers as $k => $v) {
- $curlHeaders[] = "$k: $v";
+ $opts[CURLOPT_HTTPHEADER][] = "$k: $v";
}
-
- $opts[CURLOPT_HTTPHEADER] = $curlHeaders;
}
+ // send data through cURL's poorly named opt
if($data) {
$opts[CURLOPT_POSTFIELDS] = $data;
}
+ // special considerations for HEAD requests
if($method == 'HEAD') {
$opts[CURLOPT_NOBODY] = true;
}
+ // connect timeout
+ if(is_int($this->socketOpenTimeout)) {
+ $opts[CURLOPT_CONNECTTIMEOUT] = $this->socketOpenTimeout;
+ }
+
+ // exec timeout (seconds)
+ if(is_int($this->socketRWTimeoutSeconds)) {
+ $opts[CURLOPT_TIMEOUT] = $this->socketRWTimeoutSeconds;
+ }
+
+ // exec timeout (ms)
+ if(is_int($this->socketRWTimeoutMicroseconds)) {
+ $opts[CURLOPT_TIMEOUT_MS] = $this->socketRWTimeoutMicroseconds;
+ }
+
// SSL support: don't verify unless we have a cert set
if($this->proto === 'https') {
if(!$this->sslCertPath) {
@@ -64,20 +83,20 @@ public function procPacket($method, $url, $data = null, $headers = array()) {
$chResponse = curl_exec($this->ch);
if($chResponse !== false) {
- //prepare the response object
+ // prepare the response object
$response = new stdClass();
$response->headers = new stdClass();
$response->headers->_HTTP = new stdClass();
$response->body = '';
- //split headers and body
+ // split headers and body
list($headers, $response->body) = explode("\r\n\r\n", $chResponse);
- //split up the headers
+ // split up the headers
$headers = explode("\r\n", $headers);
for($i = 0; $i < sizeof($headers); $i++) {
- //first element will always be the HTTP status line
+ // first element will always be the HTTP status line
if($i === 0) {
$response->headers->_HTTP->raw = $headers[$i];
Oops, something went wrong.

0 comments on commit e01ad0f

Please sign in to comment.