PageRenderTime 45ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Queue/Adapter/Memcacheq.php

https://github.com/Exercise/zf2
PHP | 418 lines | 188 code | 52 blank | 178 comment | 26 complexity | 8957b37d0cee9f2fe7d4eeb851682ce0 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$
  21. */
  22. /**
  23. * @namespace
  24. */
  25. namespace Zend\Queue\Adapter;
  26. use Zend\Queue;
  27. use Zend\Queue\Message;
  28. /**
  29. * Class for using connecting to a Memcache-based queuing system
  30. *
  31. * @uses \Memcache
  32. * @uses \Zend\Queue\Adapter\AdapterAbstract
  33. * @uses \Zend\Queue\Queue
  34. * @uses \Zend\Queue\Exception
  35. * @uses \Zend\Queue\Message
  36. * @category Zend
  37. * @package Zend_Queue
  38. * @subpackage Adapter
  39. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  40. * @license http://framework.zend.com/license/new-bsd New BSD License
  41. */
  42. class Memcacheq extends AbstractAdapter
  43. {
  44. const DEFAULT_HOST = '127.0.0.1';
  45. const DEFAULT_PORT = 22201;
  46. const EOL = "\r\n";
  47. /**
  48. * @var \Memcache
  49. */
  50. protected $_cache = null;
  51. /**
  52. * @var string
  53. */
  54. protected $_host = null;
  55. /**
  56. * @var integer
  57. */
  58. protected $_port = null;
  59. /**
  60. * @var resource
  61. */
  62. protected $_socket = null;
  63. /********************************************************************
  64. * Constructor / Destructor
  65. *********************************************************************/
  66. /**
  67. * Constructor
  68. *
  69. * @param array|\Zend\Config\Config $options
  70. * @param null|\Zend\Queue\Queue $queue
  71. * @return void
  72. */
  73. public function __construct($options, Queue\Queue $queue = null)
  74. {
  75. if (!extension_loaded('memcache')) {
  76. throw new Queue\Exception('Memcache extension does not appear to be loaded');
  77. }
  78. parent::__construct($options, $queue);
  79. $options = &$this->_options['driverOptions'];
  80. if (!array_key_exists('host', $options)) {
  81. $options['host'] = self::DEFAULT_HOST;
  82. }
  83. if (!array_key_exists('port', $options)) {
  84. $options['port'] = self::DEFAULT_PORT;
  85. }
  86. $this->_cache = new \Memcache();
  87. $result = $this->_cache->connect($options['host'], $options['port']);
  88. if ($result === false) {
  89. throw new Queue\Exception('Could not connect to MemcacheQ');
  90. }
  91. $this->_host = $options['host'];
  92. $this->_port = (int)$options['port'];
  93. }
  94. /**
  95. * Destructor
  96. *
  97. * @return void
  98. */
  99. public function __destruct()
  100. {
  101. if ($this->_cache instanceof \Memcache) {
  102. $this->_cache->close();
  103. }
  104. if (is_resource($this->_socket)) {
  105. $cmd = 'quit' . self::EOL;
  106. fwrite($this->_socket, $cmd);
  107. fclose($this->_socket);
  108. }
  109. }
  110. /********************************************************************
  111. * Queue management functions
  112. *********************************************************************/
  113. /**
  114. * Does a queue already exist?
  115. *
  116. * Throws an exception if the adapter cannot determine if a queue exists.
  117. * use isSupported('isExists') to determine if an adapter can test for
  118. * queue existance.
  119. *
  120. * @param string $name
  121. * @return boolean
  122. * @throws \Zend\Queue\Exception
  123. */
  124. public function isExists($name)
  125. {
  126. if (empty($this->_queues)) {
  127. $this->getQueues();
  128. }
  129. return in_array($name, $this->_queues);
  130. }
  131. /**
  132. * Create a new queue
  133. *
  134. * Visibility timeout is how long a message is left in the queue "invisible"
  135. * to other readers. If the message is acknowleged (deleted) before the
  136. * timeout, then the message is deleted. However, if the timeout expires
  137. * then the message will be made available to other queue readers.
  138. *
  139. * @param string $name queue name
  140. * @param integer $timeout default visibility timeout
  141. * @return boolean
  142. * @throws \Zend\Queue\Exception
  143. */
  144. public function create($name, $timeout=null)
  145. {
  146. if ($this->isExists($name)) {
  147. return false;
  148. }
  149. if ($timeout === null) {
  150. $timeout = self::CREATE_TIMEOUT_DEFAULT;
  151. }
  152. // MemcacheQ does not have a method to "create" a queue
  153. // queues are created upon sending a packet.
  154. // We cannot use the send() and receive() functions because those
  155. // depend on the current name.
  156. $result = $this->_cache->set($name, 'creating queue', 0, 15);
  157. $result = $this->_cache->get($name);
  158. $this->_queues[] = $name;
  159. return true;
  160. }
  161. /**
  162. * Delete a queue and all of it's messages
  163. *
  164. * Returns false if the queue is not found, true if the queue exists
  165. *
  166. * @param string $name queue name
  167. * @return boolean
  168. * @throws \Zend\Queue\Exception
  169. */
  170. public function delete($name)
  171. {
  172. $response = $this->_sendCommand('delete ' . $name, array('DELETED', 'NOT_FOUND'), true);
  173. if (in_array('DELETED', $response)) {
  174. $key = array_search($name, $this->_queues);
  175. if ($key !== false) {
  176. unset($this->_queues[$key]);
  177. }
  178. return true;
  179. }
  180. return false;
  181. }
  182. /**
  183. * Get an array of all available queues
  184. *
  185. * Not all adapters support getQueues(), use isSupported('getQueues')
  186. * to determine if the adapter supports this feature.
  187. *
  188. * @return array
  189. * @throws \Zend\Queue\Exception
  190. */
  191. public function getQueues()
  192. {
  193. $this->_queues = array();
  194. $response = $this->_sendCommand('stats queue', array('END'));
  195. foreach ($response as $i => $line) {
  196. $this->_queues[] = str_replace('STAT ', '', $line);
  197. }
  198. return $this->_queues;
  199. }
  200. /**
  201. * Return the approximate number of messages in the queue
  202. *
  203. * @param \Zend\Queue\Queue $queue
  204. * @return integer
  205. * @throws \Zend\Queue\Exception (not supported)
  206. */
  207. public function count(Queue\Queue $queue=null)
  208. {
  209. throw new Queue\Exception('count() is not supported in this adapter');
  210. }
  211. /********************************************************************
  212. * Messsage management functions
  213. *********************************************************************/
  214. /**
  215. * Send a message to the queue
  216. *
  217. * @param string $message Message to send to the active queue
  218. * @param \Zend\Queue\Queue $queue
  219. * @return \Zend\Queue\Message
  220. * @throws \Zend\Queue\Exception
  221. */
  222. public function send($message, Queue\Queue $queue=null)
  223. {
  224. if ($queue === null) {
  225. $queue = $this->_queue;
  226. }
  227. if (!$this->isExists($queue->getName())) {
  228. throw new Queue\Exception('Queue does not exist:' . $queue->getName());
  229. }
  230. $message = (string) $message;
  231. $data = array(
  232. 'message_id' => md5(uniqid(rand(), true)),
  233. 'handle' => null,
  234. 'body' => $message,
  235. 'md5' => md5($message),
  236. );
  237. $result = $this->_cache->set($queue->getName(), $message, 0, 0);
  238. if ($result === false) {
  239. throw new Queue\Exception('failed to insert message into queue:' . $queue->getName());
  240. }
  241. $options = array(
  242. 'queue' => $queue,
  243. 'data' => $data,
  244. );
  245. $classname = $queue->getMessageClass();
  246. return new $classname($options);
  247. }
  248. /**
  249. * Get messages in the queue
  250. *
  251. * @param integer $maxMessages Maximum number of messages to return
  252. * @param integer $timeout Visibility timeout for these messages
  253. * @param \Zend\Queue\Queue $queue
  254. * @return \Zend\Queue\Message\MessageIterator
  255. * @throws \Zend\Queue\Exception
  256. */
  257. public function receive($maxMessages=null, $timeout=null, Queue\Queue $queue=null)
  258. {
  259. if ($maxMessages === null) {
  260. $maxMessages = 1;
  261. }
  262. if ($timeout === null) {
  263. $timeout = self::RECEIVE_TIMEOUT_DEFAULT;
  264. }
  265. if ($queue === null) {
  266. $queue = $this->_queue;
  267. }
  268. $msgs = array();
  269. if ($maxMessages > 0 ) {
  270. for ($i = 0; $i < $maxMessages; $i++) {
  271. $data = array(
  272. 'handle' => md5(uniqid(rand(), true)),
  273. 'body' => $this->_cache->get($queue->getName()),
  274. );
  275. $msgs[] = $data;
  276. }
  277. }
  278. $options = array(
  279. 'queue' => $queue,
  280. 'data' => $msgs,
  281. 'messageClass' => $queue->getMessageClass(),
  282. );
  283. $classname = $queue->getMessageSetClass();
  284. return new $classname($options);
  285. }
  286. /**
  287. * Delete a message from the queue
  288. *
  289. * Returns true if the message is deleted, false if the deletion is
  290. * unsuccessful.
  291. *
  292. * @param \Zend\Queue\Message $message
  293. * @return boolean
  294. * @throws \Zend\Queue\Exception (unsupported)
  295. */
  296. public function deleteMessage(Message $message)
  297. {
  298. throw new Queue\Exception('deleteMessage() is not supported in ' . get_class($this));
  299. }
  300. /********************************************************************
  301. * Supporting functions
  302. *********************************************************************/
  303. /**
  304. * Return a list of queue capabilities functions
  305. *
  306. * $array['function name'] = true or false
  307. * true is supported, false is not supported.
  308. *
  309. * @param string $name
  310. * @return array
  311. */
  312. public function getCapabilities()
  313. {
  314. return array(
  315. 'create' => true,
  316. 'delete' => true,
  317. 'send' => true,
  318. 'receive' => true,
  319. 'deleteMessage' => false,
  320. 'getQueues' => true,
  321. 'count' => false,
  322. 'isExists' => true,
  323. );
  324. }
  325. /********************************************************************
  326. * Functions that are not part of the \Zend\Queue\Adapter\AdapterAbstract
  327. *********************************************************************/
  328. /**
  329. * sends a command to MemcacheQ
  330. *
  331. * The memcache functions by php cannot handle all types of requests
  332. * supported by MemcacheQ
  333. * Non-standard requests are handled by this function.
  334. *
  335. * @param string $command - command to send to memcacheQ
  336. * @param array $terminator - strings to indicate end of memcacheQ response
  337. * @param boolean $include_term - include terminator in response
  338. * @return array
  339. * @throws \Zend\Queue\Exception if connection cannot be opened
  340. */
  341. protected function _sendCommand($command, array $terminator, $include_term=false)
  342. {
  343. if (!is_resource($this->_socket)) {
  344. $this->_socket = fsockopen($this->_host, $this->_port, $errno, $errstr, 10);
  345. }
  346. if ($this->_socket === false) {
  347. throw new Queue\Exception("Could not open a connection to $this->_host:$this->_port errno=$errno : $errstr");
  348. }
  349. $response = array();
  350. $cmd = $command . self::EOL;
  351. fwrite($this->_socket, $cmd);
  352. $continue_reading = true;
  353. while (!feof($this->_socket) && $continue_reading) {
  354. $resp = trim(fgets($this->_socket, 1024));
  355. if (in_array($resp, $terminator)) {
  356. if ($include_term) {
  357. $response[] = $resp;
  358. }
  359. $continue_reading = false;
  360. } else {
  361. $response[] = $resp;
  362. }
  363. }
  364. return $response;
  365. }
  366. }