PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/Db/Document/Adapter/Couch.php

https://bitbucket.org/luismayta/zrt
PHP | 505 lines | 289 code | 120 blank | 96 comment | 17 complexity | c66bc66898a24018992f1f4dd615f4f7 MD5 | raw file
  1. <?php
  2. class Zrt_Db_Document_Adapter_Couch
  3. {
  4. /**
  5. * @var Zend_Http_Client HTTP client used for accessing server
  6. */
  7. protected $_client;
  8. protected $_config = array(
  9. 'db' => null ,
  10. 'host' => '127.0.0.1' ,
  11. 'port' => 5984 ,
  12. 'ssl' => false ,
  13. 'username' => null ,
  14. 'password' => null
  15. );
  16. // The base URI for this connection.
  17. protected $_baseUri = null;
  18. // Whether the connection information has changed.
  19. protected $_dirty = false;
  20. public function __construct( $config = array( ) )
  21. {
  22. if ( null !== $config )
  23. {
  24. if ( is_array( $config ) )
  25. {
  26. $this->setFromArray( $config );
  27. }
  28. elseif ( $config instanceof Zend_Config )
  29. {
  30. $this->setFromConfig( $config );
  31. }
  32. elseif ( is_string( $config ) )
  33. {
  34. $this->setDb( $config );
  35. }
  36. }
  37. }
  38. public function setFromArray( array $options )
  39. {
  40. foreach ( $options as $key => $value )
  41. {
  42. $method = 'set' . ucfirst( $key );
  43. if ( method_exists( $this , $method ) )
  44. {
  45. $this->$method( $value );
  46. }
  47. }
  48. return $this;
  49. }
  50. public function setFromConfig( Zend_Config $config )
  51. {
  52. return $this->setFromArray( $config->toArray() );
  53. }
  54. /**
  55. * Set Database on which to perform operations
  56. *
  57. * @param string $db
  58. * @return Zrt_Db_Document_Adapter_Couch
  59. */
  60. public function setDb( $db )
  61. {
  62. if ( !preg_match( '/^[a-z][a-z0-9_$()+-\/]+$/' , $db ) )
  63. {
  64. throw new Zrt_Exception( sprintf( 'Invalid database specified: "%s"' ,
  65. htmlentities( $db ) ) );
  66. }
  67. $this->_config['db'] = $db;
  68. $this->_dirty = true;
  69. return $this;
  70. }
  71. /**
  72. * Retrieve current database name
  73. *
  74. * @return string|null
  75. */
  76. public function getDb()
  77. {
  78. return $this->_config['db'];
  79. }
  80. /**
  81. * Sets whether to use SSL.
  82. *
  83. * @param bool $ssl
  84. */
  85. public function setSsl( $ssl )
  86. {
  87. $this->_config['ssl'] = !!$ssl;
  88. $this->_dirty = true;
  89. return $this;
  90. }
  91. /**
  92. * Retrieve SSL settings.
  93. *
  94. * @return bool
  95. */
  96. public function getSsl()
  97. {
  98. return $this->_config['ssl'];
  99. }
  100. /**
  101. * Sets username to use.
  102. *
  103. * @param string $username
  104. */
  105. public function setUsername( $username )
  106. {
  107. $this->_config['username'] = $username;
  108. $this->_dirty = true;
  109. return $this;
  110. }
  111. /**
  112. * Retrieve username.
  113. *
  114. * @return string
  115. */
  116. public function getUsername()
  117. {
  118. return $this->_config['username'];
  119. }
  120. /**
  121. * Sets whether to use a password.
  122. *
  123. * @param string $password
  124. */
  125. public function setPassword( $password )
  126. {
  127. $this->_config['password'] = $password;
  128. $this->_dirty = true;
  129. return $this;
  130. }
  131. /**
  132. * Retrieve password.
  133. *
  134. * @return string
  135. */
  136. public function getPassword()
  137. {
  138. return $this->_config['password'];
  139. }
  140. /**
  141. * Set database host
  142. *
  143. * @param string $host
  144. * @return Zrt_Db_Document_Adapter_Couch
  145. */
  146. public function setHost( $host )
  147. {
  148. $this->_config['host'] = $host;
  149. $this->_dirty = true;
  150. return $this;
  151. }
  152. /**
  153. * Retrieve database host
  154. *
  155. * @return string
  156. */
  157. public function getHost()
  158. {
  159. return $this->_config['host'];
  160. }
  161. /**
  162. * Set database host port
  163. *
  164. * @param int $port
  165. * @return Zrt_Db_Document_Adapter_Couch
  166. */
  167. public function setPort( $port )
  168. {
  169. $this->_config['port'] = ( int ) $port;
  170. $this->_dirty = true;
  171. return $this;
  172. }
  173. /**
  174. * Retrieve database host port
  175. *
  176. * @return int
  177. */
  178. public function getPort()
  179. {
  180. return $this->_config['port'];
  181. }
  182. /**
  183. * Queries a view.
  184. *
  185. * @param Zrt_Db_Document_View
  186. * @return Zrt_Db_Document_CouchSet
  187. */
  188. public function view( Zrt_Db_Document_View $view )
  189. {
  190. $db = $this->getDb();
  191. $designDocument = $view->getDesignDocument();
  192. $name = $view->getName();
  193. $parameters = $view->getParameters();
  194. $response = $this->_prepare( "/$db/_design/$designDocument/_view/$name" ,
  195. $parameters )->_execute();
  196. $status = $response->getStatus();
  197. switch ( $status )
  198. {
  199. case Zrt_Http::OK:
  200. return new Zrt_Db_Document_CouchSet( array(
  201. 'adapter' => $this ,
  202. 'data' => $response->getBody()
  203. ) );
  204. break;
  205. case Zrt_Http::NOT_FOUND:
  206. throw new Zrt_Exception( "$name is not a valid view in the $designDocument document." );
  207. break;
  208. case Zrt_Http::BAD_REQUEST:
  209. // Usually a JSON formatting error.
  210. throw new Zrt_Exception( "Request was poorly formatted" );
  211. break;
  212. default:
  213. throw new Zrt_Exception( "Response code $status not handled." );
  214. break;
  215. }
  216. }
  217. /**
  218. * Retrieves documents from the database based on IDs, or null if nothing is found.
  219. *
  220. * @param string|array $id
  221. * @throws Zrt_Exception
  222. * @return Zrt_Db_Document_CouchSet
  223. */
  224. public function find( $identifiers )
  225. {
  226. $db = $this->getDb();
  227. if ( !is_array( $identifiers ) )
  228. {
  229. $identifiers = array( $identifiers );
  230. }
  231. $postData = new stdClass();
  232. $postData->keys = $identifiers;
  233. $parameters = array(
  234. 'include_docs' => true
  235. );
  236. $this->getHttpClient()->setRawData( Zend_Json::encode( $postData ) );
  237. $response = $this->_prepare( "/$db/_all_docs" , $parameters )->_execute( Zend_Http_Client::POST );
  238. $status = $response->getStatus();
  239. switch ( $status )
  240. {
  241. case Zrt_Http::OK:
  242. return new Zrt_Db_Document_CouchSet( array(
  243. 'adapter' => $this ,
  244. 'data' => $response->getBody()
  245. ) );
  246. break;
  247. case Zrt_Http::NOT_FOUND:
  248. return null;
  249. break;
  250. case Zrt_Http::UNAUTHORISED:
  251. throw new Zrt_Exception( "Database username and password were incorrect" );
  252. break;
  253. case Zrt_Http::BAD_REQUEST:
  254. throw new Zrt_Exception( "Error in call: " . $response->getBody() );
  255. break;
  256. case Zrt_Http::UNSUPPORTED_MEDIA_TYPE:
  257. throw new Zrt_Exception( "Content type must be application/json" );
  258. break;
  259. default:
  260. throw new Zrt_Exception( "Response code $status not handled." );
  261. break;
  262. }
  263. }
  264. /**
  265. * Saves a document, either by creating or updating it.
  266. *
  267. * @param Zrt_Db_Document_Couch $document
  268. * @param string $method
  269. * @throws Zrt_Exception
  270. */
  271. public function save( Zrt_Db_Document_Couch $document ,
  272. $method = Zend_Http_Client::PUT )
  273. {
  274. $db = $this->getDb();
  275. $id = $document->getId();
  276. // Set the raw data to be saved.
  277. $this->getHttpClient()->setRawData( $document->toJson() );
  278. $response = $this->_prepare( "/$db/$id" )->_execute( $method );
  279. $status = $response->getStatus();
  280. switch ( $status )
  281. {
  282. case Zrt_Http::OK:
  283. case Zrt_Http::CREATED:
  284. $decodedResponse = Zend_Json::decode( $response->getBody() );
  285. $revision = $decodedResponse['rev'];
  286. break;
  287. case Zrt_Http::PRECONDITION_FAILED:
  288. throw new Zrt_Exception( "A document with the ID $id already exists" );
  289. break;
  290. case Zrt_Http::CONFLICT:
  291. throw new Zrt_Exception( "There was a conflict updating the database" );
  292. break;
  293. case Zrt_Http::UNAUTHORISED:
  294. throw new Zrt_Exception( "Database username and password were incorrect" );
  295. break;
  296. case Zrt_Http::BAD_REQUEST:
  297. throw new Zrt_Exception( "Error in call: " . $response->getBody() );
  298. break;
  299. case Zrt_Http::UNSUPPORTED_MEDIA_TYPE:
  300. throw new Zrt_Exception( "Content type must be application/json" );
  301. break;
  302. default:
  303. throw new Zrt_Exception( "Response code $status not handled." . $response->getBody() );
  304. break;
  305. }
  306. return $revision;
  307. }
  308. protected function _getBaseUri()
  309. {
  310. if ( !$this->_dirty )
  311. {
  312. return $this->_baseUri;
  313. }
  314. $portNumber = $this->getPort();
  315. if ( $this->_config['ssl'] )
  316. {
  317. $s = 's';
  318. $port = (443 != $portNumber) ? ":$portNumber" : '';
  319. }
  320. else
  321. {
  322. $s = '';
  323. $port = (80 != $portNumber) ? ":$portNumber" : '';
  324. }
  325. $username = $this->getUsername();
  326. if ( $username )
  327. {
  328. $password = $this->getPassword();
  329. if ( !$password )
  330. {
  331. throw new Zrt_Exception( "Must specify both a username and password for authentication." );
  332. }
  333. $credentials = "$username:$password@";
  334. }
  335. else
  336. {
  337. $credentials = '';
  338. }
  339. // Save the constructed base URI and mark it clean so we don't regenerate next time.
  340. $this->_baseUri = "http$s://" . $credentials . $this->getHost() . $port;
  341. $this->_dirty = false;
  342. return $this->_baseUri;
  343. }
  344. public function ping()
  345. {
  346. return $this->_prepare( '/' )->_execute()->getBody();
  347. }
  348. public function _prepare( $path , $parameters = null )
  349. {
  350. $client = $this->getHttpClient();
  351. $client->setHeaders( 'Content-Type' , 'application/json' );
  352. $base = $this->_getBaseUri();
  353. $path = ltrim( $path , '/' );
  354. $client->setUri( "$base/$path" );
  355. if ( null !== $parameters )
  356. {
  357. foreach ( $parameters as $key => $value )
  358. {
  359. $parameters[$key] = Zend_Json::encode( $value );
  360. }
  361. $client->setParameterGet( $parameters );
  362. }
  363. return $this;
  364. }
  365. protected function _execute( $method = Zend_Http_Client::GET )
  366. {
  367. $client = $this->getHttpClient();
  368. $response = $client->request( $method );
  369. $client->resetParameters();
  370. return $response;
  371. }
  372. /**
  373. * Get current HTTP client
  374. *
  375. * @return Zend_Http_Client
  376. */
  377. public function getHttpClient()
  378. {
  379. if ( null === $this->_client )
  380. {
  381. $this->_client = new Zend_Http_Client();
  382. }
  383. return $this->_client;
  384. }
  385. }