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

/bluebox/libraries/AMQP/Core.php

https://github.com/robertleeplummerjr/bluebox
PHP | 316 lines | 233 code | 39 blank | 44 comment | 32 complexity | 222255582bf123e5cb59331edfc18f81 MD5 | raw file
  1. <?php
  2. /**
  3. * Description of Core
  4. *
  5. * Retrieved from http://code.google.com/p/php-amqplib/
  6. *
  7. * @author Vadim Zaliva <lord@crocodile.org>
  8. */
  9. class AMQP_Core {
  10. public static $METHOD_NAME_MAP = array(
  11. "10,10" => "Connection.start",
  12. "10,11" => "Connection.start_ok",
  13. "10,20" => "Connection.secure",
  14. "10,21" => "Connection.secure_ok",
  15. "10,30" => "Connection.tune",
  16. "10,31" => "Connection.tune_ok",
  17. "10,40" => "Connection.open",
  18. "10,41" => "Connection.open_ok",
  19. "10,50" => "Connection.redirect",
  20. "10,60" => "Connection.close",
  21. "10,61" => "Connection.close_ok",
  22. "20,10" => "Channel.open",
  23. "20,11" => "Channel.open_ok",
  24. "20,20" => "Channel.flow",
  25. "20,21" => "Channel.flow_ok",
  26. "20,30" => "Channel.alert",
  27. "20,40" => "Channel.close",
  28. "20,41" => "Channel.close_ok",
  29. "30,10" => "Channel.access_request",
  30. "30,11" => "Channel.access_request_ok",
  31. "40,10" => "Channel.exchange_declare",
  32. "40,11" => "Channel.exchange_declare_ok",
  33. "40,20" => "Channel.exchange_delete",
  34. "40,21" => "Channel.exchange_delete_ok",
  35. "50,10" => "Channel.queue_declare",
  36. "50,11" => "Channel.queue_declare_ok",
  37. "50,20" => "Channel.queue_bind",
  38. "50,21" => "Channel.queue_bind_ok",
  39. "50,30" => "Channel.queue_purge",
  40. "50,31" => "Channel.queue_purge_ok",
  41. "50,40" => "Channel.queue_delete",
  42. "50,41" => "Channel.queue_delete_ok",
  43. "60,10" => "Channel.basic_qos",
  44. "60,11" => "Channel.basic_qos_ok",
  45. "60,20" => "Channel.basic_consume",
  46. "60,21" => "Channel.basic_consume_ok",
  47. "60,30" => "Channel.basic_cancel",
  48. "60,31" => "Channel.basic_cancel_ok",
  49. "60,40" => "Channel.basic_publish",
  50. "60,50" => "Channel.basic_return",
  51. "60,60" => "Channel.basic_deliver",
  52. "60,70" => "Channel.basic_get",
  53. "60,71" => "Channel.basic_get_ok",
  54. "60,72" => "Channel.basic_get_empty",
  55. "60,80" => "Channel.basic_ack",
  56. "60,90" => "Channel.basic_reject",
  57. "60,100" => "Channel.basic_recover",
  58. "90,10" => "Channel.tx_select",
  59. "90,11" => "Channel.tx_select_ok",
  60. "90,20" => "Channel.tx_commit",
  61. "90,21" => "Channel.tx_commit_ok",
  62. "90,30" => "Channel.tx_rollback",
  63. "90,31" => "Channel.tx_rollback_ok"
  64. );
  65. private static $CONTENT_METHODS = array(
  66. "60,60", // Basic.deliver
  67. "60,71", // Basic.get_ok
  68. );
  69. private static $CLOSE_METHODS = array(
  70. "10,60", // Connection.close
  71. "20,40", // Channel.close
  72. );
  73. public function __construct($connection, $channel_id)
  74. {
  75. $this->connection = $connection;
  76. $this->channel_id = $channel_id;
  77. $connection->channels[$channel_id] = $this;
  78. $this->frame_queue = array(); // Lower level queue for frames
  79. $this->method_queue = array(); // Higher level queue for methods
  80. $this->auto_decode = false;
  81. }
  82. function dispatch($method_sig, $args, $content)
  83. {
  84. if(!array_key_exists($method_sig, $this->METHOD_MAP))
  85. throw new Exception("Unknown AMQP method $method_sig");
  86. $amqp_method = $this->METHOD_MAP[$method_sig];
  87. if($content == NULL)
  88. return call_user_func(array($this,$amqp_method), $args);
  89. else
  90. return call_user_func(array($this,$amqp_method), $args, $content);
  91. }
  92. function next_frame()
  93. {
  94. self::debug_msg("waiting for a new frame");
  95. if($this->frame_queue != NULL)
  96. return array_pop($this->frame_queue);
  97. return $this->connection->wait_channel($this->channel_id);
  98. }
  99. protected function send_method_frame($method_sig, $args="")
  100. {
  101. $this->connection->send_channel_method_frame($this->channel_id, $method_sig, $args);
  102. }
  103. function wait_content()
  104. {
  105. $frm = $this->next_frame();
  106. $frame_type = $frm[0];
  107. $payload = $frm[1];
  108. if($frame_type != 2)
  109. throw new Exception("Expecting Content header");
  110. $payload_reader = new AMQP_Reader(substr($payload,0,12));
  111. $class_id = $payload_reader->read_short();
  112. $weight = $payload_reader->read_short();
  113. $body_size = $payload_reader->read_longlong();
  114. $msg = new AMQP_Message();
  115. $msg->load_properties(substr($payload,12));
  116. $body_parts = array();
  117. $body_received = 0;
  118. while(bccomp($body_size,$body_received)==1)
  119. {
  120. $frm = $this->next_frame();
  121. $frame_type = $frm[0];
  122. $payload = $frm[1];
  123. if($frame_type != 3)
  124. throw new Exception("Expecting Content body, received frame type $frame_type");
  125. $body_parts[] = $payload;
  126. $body_received = bcadd($body_received, strlen($payload));
  127. }
  128. $msg->body = implode("",$body_parts);
  129. if($this->auto_decode and isset($msg->content_encoding))
  130. {
  131. try
  132. {
  133. $msg->body = $msg->body->decode($msg->content_encoding);
  134. } catch (Exception $e) {
  135. self::debug_msg("Ignoring body decoding exception: " . $e->getMessage());
  136. }
  137. }
  138. return $msg;
  139. }
  140. /**
  141. * Wait for some expected AMQP methods and dispatch to them.
  142. * Unexpected methods are queued up for later calls to this
  143. * method.
  144. */
  145. public function wait($allowed_methods=NULL)
  146. {
  147. if($allowed_methods)
  148. self::debug_msg("waiting for " . implode(", ", $allowed_methods));
  149. else
  150. self::debug_msg("waiting for any method");
  151. //Process deferred methods
  152. foreach($this->method_queue as $qk=>$queued_method)
  153. {
  154. self::debug_msg("checking queue method " . $qk);
  155. $method_sig = $queued_method[0];
  156. if($allowed_methods==NULL || in_array($method_sig, $allowed_methods))
  157. {
  158. unset($this->method_queue[$qk]);
  159. self::debug_msg("Executing queued method: $method_sig: " .
  160. AMQP_Core::$METHOD_NAME_MAP[self::methodSig($method_sig)]);
  161. return $this->dispatch($queued_method[0],
  162. $queued_method[1],
  163. $queued_method[2]);
  164. }
  165. }
  166. // No deferred methods? wait for new ones
  167. while(true)
  168. {
  169. $frm = $this->next_frame();
  170. $frame_type = $frm[0];
  171. $payload = $frm[1];
  172. if($frame_type != 1)
  173. throw new Exception("Expecting AMQP method, received frame type: $frame_type");
  174. if(strlen($payload) < 4)
  175. throw new Exception("Method frame too short");
  176. $method_sig_array = unpack("n2", substr($payload,0,4));
  177. $method_sig = "" . $method_sig_array[1] . "," . $method_sig_array[2];
  178. $args = new AMQP_Reader(substr($payload,4));
  179. self::debug_msg("> $method_sig: " . AMQP_Core::$METHOD_NAME_MAP[self::methodSig($method_sig)]);
  180. if(in_array($method_sig, AMQP_Core::$CONTENT_METHODS))
  181. $content = $this->wait_content();
  182. else
  183. $content = NULL;
  184. if($allowed_methods==NULL ||
  185. in_array($method_sig,$allowed_methods) ||
  186. in_array($method_sig,AMQP_Core::$CLOSE_METHODS))
  187. {
  188. return $this->dispatch($method_sig, $args, $content);
  189. }
  190. // Wasn't what we were looking for? save it for later
  191. self::debug_msg("Queueing for later: $method_sig: " . AMQP_Core::$METHOD_NAME_MAP[self::methodSig($method_sig)]);
  192. array_push($this->method_queue,array($method_sig, $args, $content));
  193. }
  194. }
  195. public static function debug_msg($s)
  196. {
  197. kohana::log('debug', $s);
  198. //error_log($s);
  199. }
  200. public static function methodSig($a)
  201. {
  202. if(is_string($a))
  203. return $a;
  204. else
  205. return sprintf("%d,%d",$a[0] ,$a[1]);
  206. }
  207. /**
  208. * View any string as a hexdump.
  209. *
  210. * This is most commonly used to view binary data from streams
  211. * or sockets while debugging, but can be used to view any string
  212. * with non-viewable characters.
  213. *
  214. * @version 1.3.2
  215. * @author Aidan Lister <aidan@php.net>
  216. * @author Peter Waller <iridum@php.net>
  217. * @link http://aidanlister.com/repos/v/function.hexdump.php
  218. * @param string $data The string to be dumped
  219. * @param bool $htmloutput Set to false for non-HTML output
  220. * @param bool $uppercase Set to true for uppercase hex
  221. * @param bool $return Set to true to return the dump
  222. */
  223. public static function hexdump ($data, $htmloutput = true, $uppercase = false, $return = false)
  224. {
  225. // Init
  226. $hexi = '';
  227. $ascii = '';
  228. $dump = ($htmloutput === true) ? '<pre>' : '';
  229. $offset = 0;
  230. $len = strlen($data);
  231. // Upper or lower case hexidecimal
  232. $x = ($uppercase === false) ? 'x' : 'X';
  233. // Iterate string
  234. for ($i = $j = 0; $i < $len; $i++)
  235. {
  236. // Convert to hexidecimal
  237. $hexi .= sprintf("%02$x ", ord($data[$i]));
  238. // Replace non-viewable bytes with '.'
  239. if (ord($data[$i]) >= 32) {
  240. $ascii .= ($htmloutput === true) ?
  241. htmlentities($data[$i]) :
  242. $data[$i];
  243. } else {
  244. $ascii .= '.';
  245. }
  246. // Add extra column spacing
  247. if ($j === 7) {
  248. $hexi .= ' ';
  249. $ascii .= ' ';
  250. }
  251. // Add row
  252. if (++$j === 16 || $i === $len - 1) {
  253. // Join the hexi / ascii output
  254. $dump .= sprintf("%04$x %-49s %s", $offset, $hexi, $ascii);
  255. // Reset vars
  256. $hexi = $ascii = '';
  257. $offset += 16;
  258. $j = 0;
  259. // Add newline
  260. if ($i !== $len - 1) {
  261. $dump .= "\n";
  262. }
  263. }
  264. }
  265. // Finish dump
  266. $dump .= $htmloutput === true ?
  267. '</pre>' :
  268. '';
  269. $dump .= "\n";
  270. // Output method
  271. if ($return === false) {
  272. echo $dump;
  273. } else {
  274. return $dump;
  275. }
  276. }
  277. }