PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/www/system/library/Zend/Queue/Adapter/Db.php

https://bitbucket.org/vmihailenco/zf-blog
PHP | 536 lines | 257 code | 95 blank | 184 comment | 42 complexity | 333ac68d59b6ace51e4bf9d26064a056 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Queue
  17. * @subpackage Adapter
  18. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: Db.php 20096 2010-01-06 02:05:09Z bkarwin $
  21. */
  22. /**
  23. * @see Zend_Queue_Adapter_AdapterAbstract
  24. */
  25. /**
  26. * @see Zend_Db_Select
  27. */
  28. /**
  29. * @see Zend_Db
  30. */
  31. /**
  32. * @see Zend_Queue_Adapter_Db_Queue
  33. */
  34. /**
  35. * @see Zend_Queue_Adapter_Db_Message
  36. */
  37. /**
  38. * Class for using connecting to a Zend_Db-based queuing system
  39. *
  40. * @category Zend
  41. * @package Zend_Queue
  42. * @subpackage Adapter
  43. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  44. * @license http://framework.zend.com/license/new-bsd New BSD License
  45. */
  46. class Zend_Queue_Adapter_Db extends Zend_Queue_Adapter_AdapterAbstract
  47. {
  48. /**
  49. * @var Zend_Queue_Adapter_Db_Queue
  50. */
  51. protected $_queueTable = null;
  52. /**
  53. * @var Zend_Queue_Adapter_Db_Message
  54. */
  55. protected $_messageTable = null;
  56. /**
  57. * @var Zend_Db_Table_Row_Abstract
  58. */
  59. protected $_messageRow = null;
  60. /**
  61. * Constructor
  62. *
  63. * @param array|Zend_Config $options
  64. * @param Zend_Queue|null $queue
  65. * @return void
  66. */
  67. public function __construct($options, Zend_Queue $queue = null)
  68. {
  69. parent::__construct($options, $queue);
  70. if (!isset($this->_options['options'][Zend_Db_Select::FOR_UPDATE])) {
  71. // turn off auto update by default
  72. $this->_options['options'][Zend_Db_Select::FOR_UPDATE] = false;
  73. }
  74. if (!is_bool($this->_options['options'][Zend_Db_Select::FOR_UPDATE])) {
  75. throw new Zend_Queue_Exception('Options array item: Zend_Db_Select::FOR_UPDATE must be boolean');
  76. }
  77. if (isset($this->_options['dbAdapter'])
  78. && $this->_options['dbAdapter'] instanceof Zend_Db_Adapter_Abstract) {
  79. $db = $this->_options['dbAdapter'];
  80. } else {
  81. $db = $this->_initDbAdapter();
  82. }
  83. $this->_queueTable = new Zend_Queue_Adapter_Db_Queue(array(
  84. 'db' => $db,
  85. ));
  86. $this->_messageTable = new Zend_Queue_Adapter_Db_Message(array(
  87. 'db' => $db,
  88. ));
  89. }
  90. /**
  91. * Initialize Db adapter using 'driverOptions' section of the _options array
  92. *
  93. * Throws an exception if the adapter cannot connect to DB.
  94. *
  95. * @return Zend_Db_Adapter_Abstract
  96. * @throws Zend_Queue_Exception
  97. */
  98. protected function _initDbAdapter()
  99. {
  100. $options = &$this->_options['driverOptions'];
  101. if (!array_key_exists('type', $options)) {
  102. throw new Zend_Queue_Exception("Configuration array must have a key for 'type' for the database type to use");
  103. }
  104. if (!array_key_exists('host', $options)) {
  105. throw new Zend_Queue_Exception("Configuration array must have a key for 'host' for the host to use");
  106. }
  107. if (!array_key_exists('username', $options)) {
  108. throw new Zend_Queue_Exception("Configuration array must have a key for 'username' for the username to use");
  109. }
  110. if (!array_key_exists('password', $options)) {
  111. throw new Zend_Queue_Exception("Configuration array must have a key for 'password' for the password to use");
  112. }
  113. if (!array_key_exists('dbname', $options)) {
  114. throw new Zend_Queue_Exception("Configuration array must have a key for 'dbname' for the database to use");
  115. }
  116. $type = $options['type'];
  117. unset($options['type']);
  118. try {
  119. $db = Zend_Db::factory($type, $options);
  120. } catch (Zend_Db_Exception $e) {
  121. throw new Zend_Queue_Exception('Error connecting to database: ' . $e->getMessage(), $e->getCode(), $e);
  122. }
  123. return $db;
  124. }
  125. /********************************************************************
  126. * Queue management functions
  127. *********************************************************************/
  128. /**
  129. * Does a queue already exist?
  130. *
  131. * Throws an exception if the adapter cannot determine if a queue exists.
  132. * use isSupported('isExists') to determine if an adapter can test for
  133. * queue existance.
  134. *
  135. * @param string $name
  136. * @return boolean
  137. * @throws Zend_Queue_Exception
  138. */
  139. public function isExists($name)
  140. {
  141. $id = 0;
  142. try {
  143. $id = $this->getQueueId($name);
  144. } catch (Zend_Queue_Exception $e) {
  145. return false;
  146. }
  147. return ($id > 0);
  148. }
  149. /**
  150. * Create a new queue
  151. *
  152. * Visibility timeout is how long a message is left in the queue "invisible"
  153. * to other readers. If the message is acknowleged (deleted) before the
  154. * timeout, then the message is deleted. However, if the timeout expires
  155. * then the message will be made available to other queue readers.
  156. *
  157. * @param string $name queue name
  158. * @param integer $timeout default visibility timeout
  159. * @return boolean
  160. * @throws Zend_Queue_Exception - database error
  161. */
  162. public function create($name, $timeout = null)
  163. {
  164. if ($this->isExists($name)) {
  165. return false;
  166. }
  167. $queue = $this->_queueTable->createRow();
  168. $queue->queue_name = $name;
  169. $queue->timeout = ($timeout === null) ? self::CREATE_TIMEOUT_DEFAULT : (int)$timeout;
  170. try {
  171. if ($queue->save()) {
  172. return true;
  173. }
  174. } catch (Exception $e) {
  175. throw new Zend_Queue_Exception($e->getMessage(), $e->getCode(), $e);
  176. }
  177. return false;
  178. }
  179. /**
  180. * Delete a queue and all of it's messages
  181. *
  182. * Returns false if the queue is not found, true if the queue exists
  183. *
  184. * @param string $name queue name
  185. * @return boolean
  186. * @throws Zend_Queue_Exception - database error
  187. */
  188. public function delete($name)
  189. {
  190. $id = $this->getQueueId($name); // get primary key
  191. // if the queue does not exist then it must already be deleted.
  192. $list = $this->_queueTable->find($id);
  193. if (count($list) === 0) {
  194. return false;
  195. }
  196. $queue = $list->current();
  197. if ($queue instanceof Zend_Db_Table_Row_Abstract) {
  198. try {
  199. $queue->delete();
  200. } catch (Exception $e) {
  201. throw new Zend_Queue_Exception($e->getMessage(), $e->getCode(), $e);
  202. }
  203. }
  204. if (array_key_exists($name, $this->_queues)) {
  205. unset($this->_queues[$name]);
  206. }
  207. return true;
  208. }
  209. /*
  210. * Get an array of all available queues
  211. *
  212. * Not all adapters support getQueues(), use isSupported('getQueues')
  213. * to determine if the adapter supports this feature.
  214. *
  215. * @return array
  216. * @throws Zend_Queue_Exception - database error
  217. */
  218. public function getQueues()
  219. {
  220. $query = $this->_queueTable->select();
  221. $query->from($this->_queueTable, array('queue_id', 'queue_name'));
  222. $this->_queues = array();
  223. foreach ($this->_queueTable->fetchAll($query) as $queue) {
  224. $this->_queues[$queue->queue_name] = (int)$queue->queue_id;
  225. }
  226. $list = array_keys($this->_queues);
  227. return $list;
  228. }
  229. /**
  230. * Return the approximate number of messages in the queue
  231. *
  232. * @param Zend_Queue $queue
  233. * @return integer
  234. * @throws Zend_Queue_Exception
  235. */
  236. public function count(Zend_Queue $queue = null)
  237. {
  238. if ($queue === null) {
  239. $queue = $this->_queue;
  240. }
  241. $info = $this->_messageTable->info();
  242. $db = $this->_messageTable->getAdapter();
  243. $query = $db->select();
  244. $query->from($info['name'], array(new Zend_Db_Expr('COUNT(1)')))
  245. ->where('queue_id=?', $this->getQueueId($queue->getName()));
  246. // return count results
  247. return (int) $db->fetchOne($query);
  248. }
  249. /********************************************************************
  250. * Messsage management functions
  251. *********************************************************************/
  252. /**
  253. * Send a message to the queue
  254. *
  255. * @param string $message Message to send to the active queue
  256. * @param Zend_Queue $queue
  257. * @return Zend_Queue_Message
  258. * @throws Zend_Queue_Exception - database error
  259. */
  260. public function send($message, Zend_Queue $queue = null)
  261. {
  262. if ($this->_messageRow === null) {
  263. $this->_messageRow = $this->_messageTable->createRow();
  264. }
  265. if ($queue === null) {
  266. $queue = $this->_queue;
  267. }
  268. if (is_scalar($message)) {
  269. $message = (string) $message;
  270. }
  271. if (is_string($message)) {
  272. $message = trim($message);
  273. }
  274. if (!$this->isExists($queue->getName())) {
  275. throw new Zend_Queue_Exception('Queue does not exist:' . $queue->getName());
  276. }
  277. $msg = clone $this->_messageRow;
  278. $msg->queue_id = $this->getQueueId($queue->getName());
  279. $msg->created = time();
  280. $msg->body = $message;
  281. $msg->md5 = md5($message);
  282. // $msg->timeout = ??? @TODO
  283. try {
  284. $msg->save();
  285. } catch (Exception $e) {
  286. throw new Zend_Queue_Exception($e->getMessage(), $e->getCode(), $e);
  287. }
  288. $options = array(
  289. 'queue' => $queue,
  290. 'data' => $msg->toArray(),
  291. );
  292. $classname = $queue->getMessageClass();
  293. if (!class_exists($classname)) {
  294. Zend_Loader::loadClass($classname);
  295. }
  296. return new $classname($options);
  297. }
  298. /**
  299. * Get messages in the queue
  300. *
  301. * @param integer $maxMessages Maximum number of messages to return
  302. * @param integer $timeout Visibility timeout for these messages
  303. * @param Zend_Queue $queue
  304. * @return Zend_Queue_Message_Iterator
  305. * @throws Zend_Queue_Exception - database error
  306. */
  307. public function receive($maxMessages = null, $timeout = null, Zend_Queue $queue = null)
  308. {
  309. if ($maxMessages === null) {
  310. $maxMessages = 1;
  311. }
  312. if ($timeout === null) {
  313. $timeout = self::RECEIVE_TIMEOUT_DEFAULT;
  314. }
  315. if ($queue === null) {
  316. $queue = $this->_queue;
  317. }
  318. $msgs = array();
  319. $info = $this->_messageTable->info();
  320. $microtime = microtime(true); // cache microtime
  321. $db = $this->_messageTable->getAdapter();
  322. // start transaction handling
  323. try {
  324. if ( $maxMessages > 0 ) { // ZF-7666 LIMIT 0 clause not included.
  325. $db->beginTransaction();
  326. $query = $db->select();
  327. if ($this->_options['options'][Zend_Db_Select::FOR_UPDATE]) {
  328. // turn on forUpdate
  329. $query->forUpdate();
  330. }
  331. $query->from($info['name'], array('*'))
  332. ->where('queue_id=?', $this->getQueueId($queue->getName()))
  333. ->where('handle IS NULL OR timeout+' . (int)$timeout . ' < ' . (int)$microtime)
  334. ->limit($maxMessages);
  335. foreach ($db->fetchAll($query) as $data) {
  336. // setup our changes to the message
  337. $data['handle'] = md5(uniqid(rand(), true));
  338. $update = array(
  339. 'handle' => $data['handle'],
  340. 'timeout' => $microtime,
  341. );
  342. // update the database
  343. $where = array();
  344. $where[] = $db->quoteInto('message_id=?', $data['message_id']);
  345. $where[] = 'handle IS NULL OR timeout+' . (int)$timeout . ' < ' . (int)$microtime;
  346. $count = $db->update($info['name'], $update, $where);
  347. // we check count to make sure no other thread has gotten
  348. // the rows after our select, but before our update.
  349. if ($count > 0) {
  350. $msgs[] = $data;
  351. }
  352. }
  353. $db->commit();
  354. }
  355. } catch (Exception $e) {
  356. $db->rollBack();
  357. throw new Zend_Queue_Exception($e->getMessage(), $e->getCode(), $e);
  358. }
  359. $options = array(
  360. 'queue' => $queue,
  361. 'data' => $msgs,
  362. 'messageClass' => $queue->getMessageClass(),
  363. );
  364. $classname = $queue->getMessageSetClass();
  365. if (!class_exists($classname)) {
  366. Zend_Loader::loadClass($classname);
  367. }
  368. return new $classname($options);
  369. }
  370. /**
  371. * Delete a message from the queue
  372. *
  373. * Returns true if the message is deleted, false if the deletion is
  374. * unsuccessful.
  375. *
  376. * @param Zend_Queue_Message $message
  377. * @return boolean
  378. * @throws Zend_Queue_Exception - database error
  379. */
  380. public function deleteMessage(Zend_Queue_Message $message)
  381. {
  382. $db = $this->_messageTable->getAdapter();
  383. $where = $db->quoteInto('handle=?', $message->handle);
  384. if ($this->_messageTable->delete($where)) {
  385. return true;
  386. }
  387. return false;
  388. }
  389. /********************************************************************
  390. * Supporting functions
  391. *********************************************************************/
  392. /**
  393. * Return a list of queue capabilities functions
  394. *
  395. * $array['function name'] = true or false
  396. * true is supported, false is not supported.
  397. *
  398. * @param string $name
  399. * @return array
  400. */
  401. public function getCapabilities()
  402. {
  403. return array(
  404. 'create' => true,
  405. 'delete' => true,
  406. 'send' => true,
  407. 'receive' => true,
  408. 'deleteMessage' => true,
  409. 'getQueues' => true,
  410. 'count' => true,
  411. 'isExists' => true,
  412. );
  413. }
  414. /********************************************************************
  415. * Functions that are not part of the Zend_Queue_Adapter_Abstract
  416. *********************************************************************/
  417. /**
  418. * Get the queue ID
  419. *
  420. * Returns the queue's row identifier.
  421. *
  422. * @param string $name
  423. * @return integer|null
  424. * @throws Zend_Queue_Exception
  425. */
  426. protected function getQueueId($name)
  427. {
  428. if (array_key_exists($name, $this->_queues)) {
  429. return $this->_queues[$name];
  430. }
  431. $query = $this->_queueTable->select();
  432. $query->from($this->_queueTable, array('queue_id'))
  433. ->where('queue_name=?', $name);
  434. $queue = $this->_queueTable->fetchRow($query);
  435. if ($queue === null) {
  436. throw new Zend_Queue_Exception('Queue does not exist: ' . $name);
  437. }
  438. $this->_queues[$name] = (int)$queue->queue_id;
  439. return $this->_queues[$name];
  440. }
  441. }