A PHP lib for CouchDB

30 Mar 2009

class CouchDB {

    function __construct($db, $host = ''localhost'', $port = 5984, $adapter = ''socket'') {

        $this->db = $db;
        $this->host = $host;
        $this->port = $port;

        switch ($adapter) {
            case ''curl'':
                $this->_adapter = new CurlAdapter($host, $port);
                break;
            case ''socket'':
                $this->_adapter = new SocketAdapter($host, $port);
                break;
        }

        // init
        $this->send('''', ''get'');
        if ($this->_adapter->getCode() == ''404'') { // database not found, create it
            $this->send('''', ''put'');
        }

        $this->_docs = array();
    }

    public function add($data) {
        $this->_docs[] = $data;
    }

    public function commit() {
        $result = $this->send(''_bulk_docs/'', ''post'', json_encode(array(''all_or_nothing'' => true, ''docs'' => $this->_docs)));
        $this->_docs = array();
        return $result;
    }

    public function reset() {
        $this->send('''', ''delete'');
    }

    function send($url, $method, $data = NULL) {
        // check params
        $url = ''/'' . $this->db . (substr($url, 0, 1) == ''/'' ? $url : ''/'' . $url);
        $method = strtoupper($method);

        // send
        $result = $this->_adapter->send($url, $method, $data);

        // check response code
        $code = $this->_adapter->getCode();
        if ($code == 0 || $code > 400) {
            error_log("error $code $method $url");
        }

        return $result;
}

}

class AbstractAdapter {

    public function __construct($host = ''localhost'', $port = 5984) {
        $this->host = $host;
        $this->port = $port;
    }

}

class SocketAdapter extends AbstractAdapter {

    public function send($url, $method, $data) {
        $this->_connect();

        // build request
        $request = "{$method} {$url} HTTP/1.0\r\nHost: {$this->host}\r\n";
        if ($data) {
            $request .= ''Content-Length: '' . strlen($data) . "\r\n";
            $request .= ''Content-Type: application/json'' . "\r\n\r\n";
            $request .= $data . "\r\n";
        } else {
            $request .= "\r\n";
        }

        // send request
        if (!fwrite($this->_sock, $request)) {
            throw new Exception(''Could not send request "'' . $request . ''"'');
        }

        // read response
        $response = '''';
        while (!feof($this->_sock)) {
            $response .= fgets($this->_sock);
        }

        $this->_disconnect();

        return $this->_parseResponse($response);
    }

    public function getCode() {
        return (int)$this->_code;
    }

    private function _parseResponse($response) {
        list($header, $content) = explode("\r\n\r\n", $response);

        $tmp = explode('' '', $header);
        $this->_code = $tmp[1];

        return json_decode($content, true);
    }

    private function _connect() {
        // open socket
        $this->_sock = @fsockopen($this->host, $this->port, $err_num, $err_string);
        if (!$this->_sock) {
            throw new Exception(''Could not open connection to '' . $this->host . '':'' . $this->port . '' ('' . $err_string .'')'');
        }
    }

    private function _disconnect() {
        // close socket
        fclose($this->_sock);
    }

}

class CurlAdapter extends AbstractAdapter {

    public function send($url, $method, $data) {
        $this->_init($url);

        switch ($method) {
            case ''GET'':
                curl_setopt($this->_curl, CURLOPT_HTTPGET, true);
                break;
            case ''POST'':
                curl_setopt($this->_curl, CURLOPT_POST, true);
                curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $data);
                break;
            case ''PUT'':
                curl_setopt($this->_curl, CURLOPT_CUSTOMREQUEST, $method);
                curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $data);
                break;
            case ''DELETE'':
                curl_setopt($this->_curl, CURLOPT_CUSTOMREQUEST, $method);
                break;
        }

        // set headers
        $headers = array();
        $headers[] = ''Content-Length: '' . strlen($data);
        $headers[] = ''Content-Type: application/json'';
        curl_setopt($this->_curl, CURLOPT_HTTPHEADER, $headers);

        // execute request
        $response = curl_exec($this->_curl);

        // get result code
        $this->_code = curl_getinfo($this->_curl, CURLINFO_HTTP_CODE);

        curl_close($this->_curl);

        return json_decode($response, true);
    }

    public function getCode() {
        return $this->_code;
    }

    private function _init($url) {
        $this->_curl = curl_init();

        $url = ''http://'' . $this->host . '':'' . $this->port . $url;
        curl_setopt_array($this->_curl, array(
            CURLOPT_FAILONERROR     => false,
            CURLOPT_FOLLOWLOCATION  => true,
            CURLOPT_CONNECTTIMEOUT  => 10,
            CURLOPT_TIMEOUT         => 5,
            CURLOPT_URL             => $url,
            CURLOPT_HEADER          => false,
            CURLOPT_RETURNTRANSFER  => true,
        ));
    }
}