Routing.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. <?php declare(strict_types=1);
  2. namespace mikemadisonweb\rabbitmq\components;
  3. use mikemadisonweb\rabbitmq\exceptions\RuntimeException;
  4. use PhpAmqpLib\Channel\AMQPChannel;
  5. use PhpAmqpLib\Connection\AbstractConnection;
  6. use PhpAmqpLib\Exception\AMQPProtocolChannelException;
  7. use yii\helpers\ArrayHelper;
  8. class Routing
  9. {
  10. protected $queues = [];
  11. protected $exchanges = [];
  12. protected $bindings = [];
  13. private $exchangesDeclared = [];
  14. private $queuesDeclared = [];
  15. private $isDeclared = false;
  16. /**
  17. * @var $conn AbstractConnection
  18. */
  19. private $conn;
  20. /**
  21. * @var $conn AMQPChannel
  22. */
  23. private $ch;
  24. /**
  25. * @param AbstractConnection $conn
  26. */
  27. public function __construct(AbstractConnection $conn)
  28. {
  29. $this->conn = $conn;
  30. }
  31. /**
  32. * @param array $queues
  33. */
  34. public function setQueues(array $queues)
  35. {
  36. $this->queues = $this->arrangeByName($queues);
  37. }
  38. /**
  39. * @param array $exchanges
  40. */
  41. public function setExchanges(array $exchanges)
  42. {
  43. $this->exchanges = $this->arrangeByName($exchanges);
  44. }
  45. /**
  46. * @param array $bindings
  47. */
  48. public function setBindings(array $bindings)
  49. {
  50. $this->bindings = $bindings;
  51. }
  52. /**
  53. * Declare all routing entries defined by configuration
  54. * @return bool
  55. * @throws RuntimeException
  56. */
  57. public function declareAll() : bool
  58. {
  59. if (!$this->isDeclared) {
  60. foreach (array_keys($this->exchanges) as $name) {
  61. $this->declareExchange($name);
  62. }
  63. foreach (array_keys($this->queues) as $name) {
  64. $this->declareQueue($name);
  65. }
  66. $this->declareBindings();
  67. $this->isDeclared = true;
  68. return true;
  69. }
  70. return false;
  71. }
  72. /**
  73. * @param $queueName
  74. * @throws RuntimeException
  75. */
  76. public function declareQueue(string $queueName)
  77. {
  78. if(!isset($this->queues[$queueName])) {
  79. throw new RuntimeException("Queue `{$queueName}` is not configured.");
  80. }
  81. $queue = $this->queues[$queueName];
  82. if (!isset($this->queuesDeclared[$queueName])) {
  83. if (ArrayHelper::isAssociative($queue)) {
  84. $this->getChannel()->queue_declare(
  85. $queue['name'],
  86. $queue['passive'],
  87. $queue['durable'],
  88. $queue['exclusive'],
  89. $queue['auto_delete'],
  90. $queue['nowait'],
  91. $queue['arguments'],
  92. $queue['ticket']
  93. );
  94. } else {
  95. foreach ($queue as $q) {
  96. $this->getChannel()->queue_declare(
  97. $q['name'],
  98. $q['passive'],
  99. $q['durable'],
  100. $q['exclusive'],
  101. $q['auto_delete'],
  102. $q['nowait'],
  103. $q['arguments'],
  104. $q['ticket']
  105. );
  106. }
  107. }
  108. $this->queuesDeclared[$queueName] = true;
  109. }
  110. }
  111. /**
  112. * Create bindings
  113. */
  114. public function declareBindings()
  115. {
  116. foreach ($this->bindings as $binding) {
  117. if (isset($binding['queue'])) {
  118. $this->bindExchangeToQueue($binding);
  119. } else {
  120. $this->bindExchangeToExchange($binding);
  121. }
  122. }
  123. }
  124. /**
  125. * Create exchange-to-queue binding
  126. * @param array $binding
  127. */
  128. public function bindExchangeToQueue(array $binding)
  129. {
  130. if (isset($binding['routing_keys']) && count($binding['routing_keys']) > 0) {
  131. foreach ($binding['routing_keys'] as $routingKey) {
  132. // queue binding is not permitted on the default exchange
  133. if ('' !== $binding['exchange']) {
  134. $this->getChannel()->queue_bind($binding['queue'], $binding['exchange'], $routingKey);
  135. }
  136. }
  137. } else {
  138. // queue binding is not permitted on the default exchange
  139. if ('' !== $binding['exchange']) {
  140. $this->getChannel()->queue_bind($binding['queue'], $binding['exchange']);
  141. }
  142. }
  143. }
  144. /**
  145. * Create exchange-to-exchange binding
  146. * @param array $binding
  147. */
  148. public function bindExchangeToExchange(array $binding)
  149. {
  150. if (isset($binding['routing_keys']) && count($binding['routing_keys']) > 0) {
  151. foreach ($binding['routing_keys'] as $routingKey) {
  152. // queue binding is not permitted on the default exchange
  153. if ('' !== $binding['exchange']) {
  154. $this->getChannel()->exchange_bind($binding['to_exchange'], $binding['exchange'], $routingKey);
  155. }
  156. }
  157. } else {
  158. // queue binding is not permitted on the default exchange
  159. if ('' !== $binding['exchange']) {
  160. $this->getChannel()->exchange_bind($binding['to_exchange'], $binding['exchange']);
  161. }
  162. }
  163. }
  164. /**
  165. * @param $exchangeName
  166. * @throws RuntimeException
  167. */
  168. public function declareExchange(string $exchangeName)
  169. {
  170. if(!isset($this->exchanges[$exchangeName])) {
  171. throw new RuntimeException("Exchange `{$exchangeName}` is not configured.");
  172. }
  173. $exchange = $this->exchanges[$exchangeName];
  174. if (!isset($this->exchangesDeclared[$exchangeName])) {
  175. $this->getChannel()->exchange_declare(
  176. $exchange['name'],
  177. $exchange['type'],
  178. $exchange['passive'],
  179. $exchange['durable'],
  180. $exchange['auto_delete'],
  181. $exchange['internal'],
  182. $exchange['nowait'],
  183. $exchange['arguments'],
  184. $exchange['ticket']
  185. );
  186. $this->exchangesDeclared[$exchangeName] = true;
  187. }
  188. }
  189. /**
  190. * Purge the queue
  191. * @param string $queueName
  192. * @throws RuntimeException
  193. */
  194. public function purgeQueue(string $queueName)
  195. {
  196. if (!isset($this->queues[$queueName])) {
  197. throw new RuntimeException("Queue {$queueName} is not configured. Purge is aborted.");
  198. }
  199. $this->getChannel()->queue_purge($queueName, true);
  200. }
  201. /**
  202. * Delete all configured queues and exchanges
  203. * @throws RuntimeException
  204. */
  205. public function deleteAll()
  206. {
  207. foreach (array_keys($this->queues) as $name) {
  208. $this->deleteQueue($name);
  209. }
  210. foreach (array_keys($this->exchanges) as $name) {
  211. $this->deleteExchange($name);
  212. }
  213. }
  214. /**
  215. * Delete the queue
  216. * @param string $queueName
  217. * @throws RuntimeException
  218. */
  219. public function deleteQueue(string $queueName)
  220. {
  221. if (!isset($this->queues[$queueName])) {
  222. throw new RuntimeException("Queue {$queueName} is not configured. Delete is aborted.");
  223. }
  224. $this->getChannel()->queue_delete($queueName);
  225. }
  226. /**
  227. * Delete the queue
  228. * @param string $exchangeName
  229. * @throws RuntimeException
  230. */
  231. public function deleteExchange(string $exchangeName)
  232. {
  233. if (!isset($this->exchanges[$exchangeName])) {
  234. throw new RuntimeException("Exchange {$exchangeName} is not configured. Delete is aborted.");
  235. }
  236. $this->getChannel()->exchange_delete($exchangeName);
  237. }
  238. /**
  239. * Checks whether exchange is already declared in broker
  240. * @param string $exchangeName
  241. * @return bool
  242. */
  243. public function isExchangeExists(string $exchangeName) : bool
  244. {
  245. try {
  246. $this->getChannel()->exchange_declare($exchangeName, null, true);
  247. } catch (AMQPProtocolChannelException $e) {
  248. return false;
  249. }
  250. return true;
  251. }
  252. /**
  253. * Checks whether queue is already declared in broker
  254. * @param string $queueName
  255. * @return bool
  256. */
  257. public function isQueueExists(string $queueName) : bool
  258. {
  259. try {
  260. $this->getChannel()->queue_declare($queueName, true);
  261. } catch (AMQPProtocolChannelException $e) {
  262. return false;
  263. }
  264. return true;
  265. }
  266. /**
  267. * @param array $unnamedArr
  268. * @return array
  269. */
  270. private function arrangeByName(array $unnamedArr) : array
  271. {
  272. $namedArr = [];
  273. foreach ($unnamedArr as $elem) {
  274. if('' === $elem['name']) {
  275. $namedArr[$elem['name']][] = $elem;
  276. } else {
  277. $namedArr[$elem['name']] = $elem;
  278. }
  279. }
  280. return $namedArr;
  281. }
  282. /**
  283. * @return AMQPChannel
  284. */
  285. private function getChannel()
  286. {
  287. if (empty($this->ch) || null === $this->ch->getChannelId()) {
  288. $this->ch = $this->conn->channel();
  289. }
  290. return $this->ch;
  291. }
  292. }