PageRenderTime 29ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/main/Utils/AMQP/Pecl/AMQPPeclChannel.class.php

http://github.com/onPHP/onphp-framework
PHP | 585 lines | 406 code | 85 blank | 94 comment | 19 complexity | dc0e5179c5b5419c6c2c900e2837ae79 MD5 | raw file
Possible License(s): LGPL-3.0
  1. <?php
  2. /***************************************************************************
  3. * Copyright (C) 2011 by Sergey S. Sergeev *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; either version 2 of the License, or *
  8. * (at your option) any later version. *
  9. * *
  10. ***************************************************************************/
  11. final class AMQPPeclChannel extends AMQPBaseChannel
  12. {
  13. const NIL = 'nil';
  14. const AMQP_NONE = 0;
  15. protected $exchangeList = array();
  16. protected $queueList = array();
  17. protected $opened = false;
  18. /**
  19. * @var AMQPConsumer
  20. **/
  21. protected $consumer = null;
  22. public function isOpen()
  23. {
  24. return $this->opened === true;
  25. }
  26. /**
  27. * @return AMQPChannelInterface
  28. **/
  29. public function open()
  30. {
  31. $this->opened = true;
  32. return $this;
  33. }
  34. /**
  35. * @return AMQPChannelInterface
  36. **/
  37. public function close()
  38. {
  39. $this->opened = false;
  40. return $this;
  41. }
  42. /**
  43. * @throws AMQPServerException|AMQPServerConnectionException
  44. * @return AMQPChannelInterface
  45. **/
  46. public function basicAck($deliveryTag, $multiple = false)
  47. {
  48. try {
  49. $obj = $this->lookupQueue(self::NIL);
  50. $result = $obj->ack(
  51. $deliveryTag,
  52. $multiple === true
  53. ? AMQP_MULTIPLE
  54. : self::AMQP_NONE
  55. );
  56. } catch (AMQPQueueException $e) {
  57. throw new AMQPServerException(
  58. $e->getMessage(),
  59. $e->getCode(),
  60. $e
  61. );
  62. }
  63. $this->checkCommandResult(
  64. $result,
  65. "Could not ack message"
  66. );
  67. return $this;
  68. }
  69. /**
  70. * @throws AMQPServerQueueException|AMQPServerConnectionException|WrongStateException
  71. * @return AMQPChannelInterface
  72. **/
  73. public function basicCancel($consumerTag)
  74. {
  75. if (!$this->consumer instanceof AMQPConsumer)
  76. throw new WrongStateException();
  77. try {
  78. $obj = $this->lookupQueue($this->consumer->getQueueName());
  79. $result = $obj->cancel($consumerTag);
  80. $this->consumer->handleCancelOk($consumerTag);
  81. } catch (AMQPQueueException $e) {
  82. throw new AMQPServerException(
  83. $e->getMessage(),
  84. $e->getCode(),
  85. $e
  86. );
  87. }
  88. $this->checkCommandResult(
  89. $result,
  90. "Could not cancel queue"
  91. );
  92. return $this;
  93. }
  94. /**
  95. * PECL AMQP does not implement basicConsume logic, we'll emulate it.
  96. *
  97. * @return AMQPChannelInterface
  98. **/
  99. public function basicConsume($queue, $autoAck, AMQPConsumer $callback)
  100. {
  101. $this->consumer =
  102. $callback->
  103. setQueueName($queue)->
  104. setAutoAcknowledge($autoAck === true);
  105. return $this;
  106. }
  107. /**
  108. * @throws AMQPServerException|AMQPServerConnectionException|ObjectNotFoundException
  109. * @return AMQPIncomingMessage
  110. **/
  111. public function basicGet($queue, $noAck = true)
  112. {
  113. try {
  114. $obj = $this->lookupQueue($queue);
  115. $message = $obj->get(
  116. ($noAck === true)
  117. ? AMQP_NOACK
  118. : self::AMQP_NONE
  119. );
  120. } catch (AMQPQueueException $e) {
  121. throw new AMQPServerException(
  122. $e->getMessage(),
  123. $e->getCode(),
  124. $e
  125. );
  126. }
  127. $this->checkCommandResult(
  128. is_array($message),
  129. "Could not get from queue"
  130. );
  131. if (
  132. isset($message[AMQPIncomingMessage::COUNT])
  133. && $message[AMQPIncomingMessage::COUNT] == -1
  134. )
  135. throw new ObjectNotFoundException(
  136. "AMQP queue with name '{$queue}' is empty"
  137. );
  138. return AMQPIncomingMessage::spawn($message);
  139. }
  140. /**
  141. * @throws AMQPServerExchangeException|AMQPServerConnectionException
  142. * @return AMQPChannelInterface
  143. **/
  144. public function basicPublish(
  145. $exchange, $routingKey, AMQPOutgoingMessage $msg
  146. ) {
  147. try {
  148. $obj = $this->lookupExchange($exchange);
  149. $result = $obj->publish(
  150. $msg->getBody(),
  151. $routingKey,
  152. $msg->getBitmask(new AMQPPeclOutgoingMessageBitmask()),
  153. $msg->getProperties()
  154. );
  155. } catch (AMQPExchangeException $e) {
  156. throw new AMQPServerException(
  157. $e->getMessage(),
  158. $e->getCode(),
  159. $e
  160. );
  161. }
  162. $this->checkCommandResult(
  163. $result,
  164. "Could not publish to exchange"
  165. );
  166. return $this;
  167. }
  168. public function basicQos($prefetchSize, $prefetchCount)
  169. {
  170. throw new UnimplementedFeatureException();
  171. }
  172. public function exchangeToExchangeBind(
  173. $destination, $source, $routingKey
  174. )
  175. {
  176. throw new UnimplementedFeatureException(
  177. 'Exchange to exchange bindings is not yet implemented'
  178. );
  179. }
  180. public function exchangeToExchangeUnbind(
  181. $destination, $source, $routingKey
  182. )
  183. {
  184. throw new UnimplementedFeatureException(
  185. 'Exchange to exchange unbindings is not yet implemented'
  186. );
  187. }
  188. /**
  189. * @throws AMQPServerException|AMQPServerConnectionException
  190. * @return AMQPChannelInterface
  191. **/
  192. public function exchangeBind($name, $queue, $routingKey)
  193. {
  194. try {
  195. $obj = $this->lookupExchange($name);
  196. $result = $obj->bind($queue, $routingKey);
  197. } catch (AMQPExchangeException $e) {
  198. throw new AMQPServerException(
  199. $e->getMessage(),
  200. $e->getCode(),
  201. $e
  202. );
  203. }
  204. $this->checkCommandResult(
  205. $result,
  206. "Could not bind exchange"
  207. );
  208. return $this;
  209. }
  210. public function exchangeUnbind($name, $queue, $routingKey)
  211. {
  212. throw new UnimplementedFeatureException();
  213. }
  214. /**
  215. * @throws AMQPServerException|AMQPServerConnectionException
  216. * @return AMQPChannelInterface
  217. **/
  218. public function exchangeDeclare($name, AMQPExchangeConfig $conf)
  219. {
  220. $this->checkConnection();
  221. if (!$conf->getType() instanceof AMQPExchangeType)
  222. throw new WrongArgumentException(
  223. "AMQP exchange type is not set"
  224. );
  225. try {
  226. $this->exchangeList[$name] =
  227. new AMQPExchange($this->transport->getLink());
  228. $obj = $this->exchangeList[$name];
  229. $result = $obj->declare(
  230. $name,
  231. $conf->getType()->getName(),
  232. $conf->getBitmask(new AMQPPeclExchangeBitmask())
  233. );
  234. } catch (AMQPExchangeException $e) {
  235. throw new AMQPServerException(
  236. $e->getMessage(),
  237. $e->getCode(),
  238. $e
  239. );
  240. }
  241. $this->checkCommandResult(
  242. $result,
  243. "Could not declare exchange"
  244. );
  245. return $this;
  246. }
  247. /**
  248. * @throws AMQPServerException|AMQPServerConnectionException
  249. * @return AMQPChannelInterface
  250. **/
  251. public function exchangeDelete(
  252. $name, $ifUnused = false, $ifEmpty = false
  253. ) {
  254. $bitmask = self::AMQP_NONE;
  255. if ($ifUnused)
  256. $bitmask = $bitmask | AMQP_IFUNUSED;
  257. if ($ifEmpty)
  258. $bitmask = $bitmask | AMQP_IFEMPTY;
  259. try {
  260. $obj = $this->lookupExchange($name);
  261. $result = $obj->delete($name, $bitmask);
  262. } catch (AMQPExchangeException $e) {
  263. throw new AMQPServerException(
  264. $e->getMessage(),
  265. $e->getCode(),
  266. $e
  267. );
  268. }
  269. $this->checkCommandResult(
  270. $result,
  271. "Could not delete exchange"
  272. );
  273. $this->unsetExchange($name);
  274. return $this;
  275. }
  276. /**
  277. * @throws AMQPServerException|AMQPServerConnectionException
  278. * @return AMQPChannelInterface
  279. **/
  280. public function queueBind($name, $exchange, $routingKey)
  281. {
  282. try {
  283. $obj = $this->lookupQueue($name);
  284. $result = $obj->bind($exchange, $routingKey);
  285. } catch (AMQPQueueException $e) {
  286. throw new AMQPServerException(
  287. $e->getMessage(),
  288. $e->getCode(),
  289. $e
  290. );
  291. }
  292. $this->checkCommandResult(
  293. $result,
  294. "Could not bind queue"
  295. );
  296. return $this;
  297. }
  298. /**
  299. * @throws AMQPServerException|AMQPServerConnectionException
  300. * @return integer - the message count in queue
  301. **/
  302. public function queueDeclare($name, AMQPQueueConfig $conf)
  303. {
  304. $this->checkConnection();
  305. try {
  306. $this->queueList[$name] =
  307. new AMQPQueue($this->transport->getLink());
  308. $obj = $this->queueList[$name];
  309. $result = $obj->declare(
  310. $name,
  311. $conf->getBitmask(new AMQPPeclQueueBitmask())
  312. );
  313. } catch (AMQPQueueException $e) {
  314. throw new AMQPServerException(
  315. $e->getMessage(),
  316. $e->getCode(),
  317. $e
  318. );
  319. }
  320. $this->checkCommandResult(
  321. is_int($result),
  322. "Could not declare queue"
  323. );
  324. return $result;
  325. }
  326. /**
  327. * @throws AMQPServerException|AMQPServerConnectionException
  328. * @return AMQPChannelInterface
  329. **/
  330. public function queueDelete($name)
  331. {
  332. try {
  333. $obj = $this->lookupQueue($name);
  334. $result = $obj->delete($name);
  335. } catch (AMQPQueueException $e) {
  336. throw new AMQPServerException(
  337. $e->getMessage(),
  338. $e->getCode(),
  339. $e
  340. );
  341. }
  342. $this->checkCommandResult(
  343. $result,
  344. "Could not delete queue"
  345. );
  346. $this->unsetQueue($name);
  347. return $this;
  348. }
  349. /**
  350. * @throws AMQPServerException|AMQPServerConnectionException
  351. * @return AMQPChannelInterface
  352. **/
  353. public function queuePurge($name)
  354. {
  355. try {
  356. $obj = $this->lookupQueue($name);
  357. $result = $obj->purge($name);
  358. } catch (AMQPQueueException $e) {
  359. throw new AMQPServerException(
  360. $e->getMessage(),
  361. $e->getCode(),
  362. $e
  363. );
  364. }
  365. $this->checkCommandResult(
  366. $result,
  367. "Could not purge queue"
  368. );
  369. return $this;
  370. }
  371. /**
  372. * @throws AMQPServerException|AMQPServerConnectionException
  373. * @return AMQPChannelInterface
  374. **/
  375. public function queueUnbind($name, $exchange, $routingKey)
  376. {
  377. try {
  378. $obj = $this->lookupQueue($name);
  379. $result = $obj->unbind($exchange, $routingKey);
  380. } catch (AMQPQueueException $e) {
  381. throw new AMQPServerException(
  382. $e->getMessage(),
  383. $e->getCode(),
  384. $e
  385. );
  386. }
  387. $this->checkCommandResult(
  388. $result,
  389. "Could not unbind queue"
  390. );
  391. return $this;
  392. }
  393. /**
  394. * @throws AMQPServerException|WrongStateException
  395. * @return AMQPIncomingMessage
  396. **/
  397. public function getNextDelivery()
  398. {
  399. if (!$this->consumer instanceof AMQPConsumer)
  400. throw new WrongStateException();
  401. try {
  402. $obj = $this->lookupQueue(
  403. $this->consumer->getQueueName()
  404. );
  405. $messages = $obj->consume(
  406. array(
  407. 'min' => 1,
  408. 'max' => 1,
  409. 'ack' => (bool) $this->consumer->isAutoAcknowledge(),
  410. )
  411. );
  412. } catch (AMQPQueueException $e) {
  413. throw new AMQPServerException(
  414. $e->getMessage(),
  415. $e->getCode(),
  416. $e
  417. );
  418. }
  419. $this->checkCommandResult(
  420. is_array($messages) && !empty($messages),
  421. "Could not consume from queue"
  422. );
  423. $message = array_shift($messages);
  424. $incoming = AMQPIncomingMessage::spawn($message);
  425. if ($this->consumer->getConsumerTag() === null) {
  426. $this->consumer->setConsumerTag($incoming->getConsumerTag());
  427. $this->consumer->handleConsumeOk($incoming->getConsumerTag());
  428. } else if (
  429. $this->consumer->getConsumerTag()
  430. != $incoming->getConsumerTag()
  431. ) {
  432. $this->consumer->handleChangeConsumerTag(
  433. $this->consumer->getConsumerTag(),
  434. $incoming->getConsumerTag()
  435. );
  436. }
  437. $this->consumer->handleDelivery($incoming);
  438. return $incoming;
  439. }
  440. /**
  441. * @throws AMQPServerConnectionException
  442. * @return AMQPExchange
  443. **/
  444. protected function lookupExchange($name)
  445. {
  446. $this->checkConnection();
  447. if (!isset($this->exchangeList[$name])) {
  448. $this->exchangeList[$name] =
  449. new AMQPExchange($this->transport->getLink(), $name);
  450. }
  451. return $this->exchangeList[$name];
  452. }
  453. /**
  454. * @return AMQPPeclChannel
  455. **/
  456. protected function unsetExchange($name)
  457. {
  458. if (isset($this->exchangeList[$name]))
  459. unset($this->exchangeList[$name]);
  460. return $this;
  461. }
  462. /**
  463. * @throws AMQPServerConnectionException
  464. * @return AMQPQueue
  465. **/
  466. protected function lookupQueue($name)
  467. {
  468. $this->checkConnection();
  469. if (!isset($this->queueList[$name])) {
  470. $this->queueList[$name] =
  471. ($name == self::NIL)
  472. ? new AMQPQueue($this->transport->getLink())
  473. : new AMQPQueue($this->transport->getLink(), $name);
  474. }
  475. return $this->queueList[$name];
  476. }
  477. /**
  478. * @return AMQPPeclChannel
  479. **/
  480. protected function unsetQueue($name)
  481. {
  482. if (isset($this->queueList[$name]))
  483. unset($this->queueList[$name]);
  484. return $this;
  485. }
  486. /**
  487. * @throws AMQPServerConnectionException
  488. * @return AMQPPeclChannel
  489. **/
  490. protected function checkCommandResult($boolean, $message)
  491. {
  492. if ($boolean !== true) {
  493. //link is not alive!!!
  494. $this->transport->getLink()->disconnect();
  495. throw new AMQPServerConnectionException($message);
  496. }
  497. return $this;
  498. }
  499. }
  500. ?>