/lib/couchdb/CouchDB.php

https://github.com/Luzifer/PHPFramework · PHP · 265 lines · 167 code · 24 blank · 74 comment · 51 complexity · aaf4769d2123f94d83766f6e9eab5675 MD5 · raw file

  1. <?php
  2. /**
  3. * CouchDB wrapper
  4. */
  5. class CouchDB {
  6. private $host = null;
  7. private $port = null;
  8. private $database = null;
  9. private $config;
  10. /**
  11. * @param IConfigReader $config
  12. * @param string $connection
  13. */
  14. public function __construct($config, $connection = 'default') {
  15. $this->config = $config;
  16. $this->host = $this->config->get('db.couchdb.' . $connection . '.host', 'localhost');
  17. $this->port = $this->config->get('db.couchdb.' . $connection . '.port', 5984);
  18. $this->database = $this->config->get('db.couchdb.' . $connection . '.database', null);
  19. if($this->config->get('db.couchdb.' . $connection . '.user', null) !== null) {
  20. $user = $this->config->get('db.couchdb.' . $connection . '.user', null);
  21. $pass = $this->config->get('db.couchdb.' . $connection . '.password', null);
  22. $this->host = $user . ':' . $pass . '@' . $this->host;
  23. }
  24. if($this->database === null) {
  25. throw new CouchDBConfigurationError('Configration key "db.couchdb.' . $connection . '.database" is missing.');
  26. }
  27. }
  28. /**
  29. * @param array $data
  30. * @param string $method
  31. * @param null $custom_url
  32. * @return mixed
  33. * @throws CouchDBResultException
  34. */
  35. private function send($data = null, $method = 'GET', $custom_url = null) {
  36. $url = 'http://'. $this->host .':'. $this->port .'/'. $this->database;
  37. $ch = curl_init();
  38. $options = array(
  39. CURLOPT_URL => $url
  40. , CURLOPT_HEADER => 0
  41. , CURLOPT_RETURNTRANSFER => 1
  42. , CURLOPT_HTTPHEADER => array('Content-Type: application/json')
  43. );
  44. if($method == 'GET') {
  45. if(is_array($data) && count($data) > 0) {
  46. $options[CURLOPT_URL] = $options[CURLOPT_URL] .'/'. implode("/", $data);
  47. }
  48. }
  49. if($method == 'POST') {
  50. if(is_array($data) && count($data) > 0) {
  51. $options[CURLOPT_CUSTOMREQUEST] = 'POST';
  52. $options[CURLOPT_POSTFIELDS] = json_encode($data);
  53. }
  54. }
  55. if($method == 'PUT') {
  56. if(is_array($data)) {
  57. if(!empty($data['_id'])) {
  58. $options[CURLOPT_URL] = $options[CURLOPT_URL] .'/'. $data['_id'];
  59. unset($data['_id']);
  60. }
  61. $options[CURLOPT_CUSTOMREQUEST] = 'PUT';
  62. $options[CURLOPT_POSTFIELDS] = json_encode($data);
  63. }
  64. }
  65. if($method == 'DELETE') {
  66. if(!empty($data['id'])) {
  67. $options[CURLOPT_CUSTOMREQUEST] = 'DELETE';
  68. $options[CURLOPT_URL] = $options[CURLOPT_URL] .'/'. $data['id'];
  69. }
  70. }
  71. if($custom_url !== null) {
  72. $options[CURLOPT_URL] = $custom_url;
  73. }
  74. curl_setopt_array($ch, $options);
  75. $result = curl_exec($ch);
  76. $retval = json_decode($result);
  77. if(!is_object($retval)) {
  78. throw new CouchDBResultException('No valid result for '. $options[CURLOPT_URL] .': '. $result);
  79. }
  80. curl_close($ch);
  81. if(array_key_exists('error', $retval) && $retval['error'] === 'unauthorized') {
  82. throw new CouchDBAuthenticationError('Authentication failure: ' . $retval['reason']);
  83. }
  84. return $retval;
  85. }
  86. /**
  87. * Create current database from settings file
  88. *
  89. * @throws CouchDBAlreadyExistException when the database already exists
  90. * @throws CouchDBResultException when the database cannot be created
  91. * @return boolean
  92. */
  93. public function create_database() {
  94. $result = $this->send(array(), 'PUT');
  95. if(!empty($result->ok) && $result->ok === true) {
  96. return true;
  97. } elseif(!empty($result->error) && $result->error == 'file_exists') {
  98. throw new CouchDBAlreadyExistException('Database already exists');
  99. } else {
  100. throw new CouchDBResultException($result->reason);
  101. }
  102. }
  103. /**
  104. * @param string $category
  105. * @param array $views
  106. * @return mixed
  107. */
  108. public function create_views($category, $views = array()) {
  109. $data = array();
  110. $data['_id'] = '_design/'. $category;
  111. $data['language'] = 'javascript';
  112. $data['views'] = array();
  113. foreach($views as $name => $function) {
  114. $data['views'][$name] = array('map' => 'function(doc) { '. $function .' }');
  115. }
  116. return $this->send($data, 'POST');
  117. }
  118. /**
  119. * @param string $category
  120. * @param string $name
  121. * @param string $function
  122. * @return mixed
  123. */
  124. public function create_simple_view($category, $name, $function) {
  125. return $this->create_views($category, array(
  126. $name => $function
  127. ));
  128. }
  129. /**
  130. * Insert a new document to current database
  131. *
  132. * @param array $values Array of values
  133. * @throws CouchDBResultException when insert fails
  134. * @return string ID of the created document
  135. */
  136. public function insert($values) {
  137. $result = $this->send($values, 'POST');
  138. if(!empty($result->ok) && $result->ok == 1) {
  139. return $result->id;
  140. } else {
  141. throw new CouchDBResultException($result->reason);
  142. }
  143. }
  144. /**
  145. * Update a document in the database
  146. *
  147. * @param string $id the document ID
  148. * @param array $values array of values to set/update in the document
  149. * @throws CouchDBResultException when the update fails
  150. * @throws CouchDBNotFoundException when the document does not exist
  151. * @return string the new revision of the document
  152. */
  153. public function update($id, $values) {
  154. $rev = $this->send(array($id));
  155. if(!empty($rev->_rev)) {
  156. $values['_id'] = $id;
  157. $values['_rev'] = $rev->_rev;
  158. $result = $this->send($values, 'PUT');
  159. if(!empty($result->ok) && $result->ok === true) {
  160. return $result->rev;
  161. } else {
  162. throw new CouchDBResultException("Failed to update document");
  163. }
  164. } elseif(!empty($rev->error) && $rev->error == 'not_found') {
  165. throw new CouchDBNotFoundException('Document not found');
  166. }
  167. }
  168. /**
  169. * Delete a document in the database
  170. *
  171. * @param string $id
  172. * @throws CouchDBResultException when deletion fails
  173. * @throws CouchDBNotFoundException when document does not exist
  174. * @return boolean true when deletion was successful
  175. */
  176. public function delete($id) {
  177. $rev = $this->send(array($id));
  178. if(!empty($rev->_rev)) {
  179. $result = $this->send(array('id' => $id . '?rev='. $rev->_rev), 'DELETE');
  180. if(!empty($result->ok) && $result->ok === true) {
  181. return true;
  182. } else {
  183. throw new CouchDBResultException($result->reason);
  184. }
  185. } elseif(!empty($rev->error) && $rev->error == 'not_found') {
  186. throw new CouchDBNotFoundException('Document not found');
  187. }
  188. }
  189. /**
  190. * Get a document in the database
  191. *
  192. * @param string $id
  193. * @throws CouchDBNotFoundException when document does not exist
  194. * @return object
  195. */
  196. public function get($id) {
  197. $doc = $this->send(array($id));
  198. if(!empty($doc->_id)) {
  199. return $doc;
  200. } elseif(!empty($doc->error) && $doc->error == 'not_found') {
  201. throw new CouchDBNotFoundException('Document not found');
  202. }
  203. }
  204. /**
  205. * Get a view from the database
  206. *
  207. * @param string $category
  208. * @param string $name
  209. * @param bool $descending
  210. * @param bool $grouping
  211. * @param array $params Parameters like 'key', 'startkey', 'endkey', ...
  212. * @throws CouchDBNotFoundException when view does not exist
  213. * @return object
  214. */
  215. public function get_view($category, $name, $descending = false, $grouping = false, $params = array()) {
  216. $suffix = '';
  217. if($descending === true) {
  218. $params['descending'] = 'true';
  219. }
  220. if($grouping === true) {
  221. $params['group'] = 'true';
  222. }
  223. if(count($params) > 0) {
  224. $suffix = '?_=1';
  225. foreach($params as $key => $value) {
  226. $suffix .= '&' . $key . '=' . $value;
  227. }
  228. }
  229. $doc = $this->send(array('_design', $category, '_view', $name . $suffix));
  230. if(isset($doc->total_rows)) {
  231. return $doc;
  232. } elseif(!empty($doc->error) && $doc->error == 'not_found') {
  233. throw new CouchDBNotFoundException('Document not found');
  234. }
  235. }
  236. }
  237. class CouchDBResultException extends Exception {}
  238. class CouchDBAlreadyExistException extends Exception {}
  239. class CouchDBNotFoundException extends Exception {}
  240. class CouchDBConfigurationError extends Exception {}
  241. class CouchDBAuthenticationError extends Exception {}