Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Changes to the publish confirms feature:

 1. Channel  will now  save a  copy of each  outgoing message  when in
 confirm mode,  this could have  RAM usage implications  for processes
 that publish large numbers of messages

 2. When confirm (n)acks are received, the Channel delivers the actual
 message   that  is  being   confirmed  to   the  ChannelEventHandler.
 Previously, the  Channel would deliver  the incoming ack to  the CEH,
 which is pretty useless because  the only information held in the ack
 is the message sequence number (which is internal to the Channel) and
 because the  broker often sends acks with  multiple=true, which means
 the channel delivers the same incoming ack message many times.
  • Loading branch information...
commit 51a8cfed135d3616046ddc6aba1252f016d65aa5 1 parent 4052be7
Robin Harvey authored
Showing with 69 additions and 111 deletions.
  1. +1 −4 TODO.dev
  2. +0 −2  build/cpf/amqphp/CallbackExitStrategy.php
  3. +1 −0  build/cpf/amqphp/CallbackExitStrategy.php
  4. +0 −2  build/cpf/amqphp/Channel.php
  5. +1 −0  build/cpf/amqphp/Channel.php
  6. +0 −2  build/cpf/amqphp/ChannelEventHandler.php
  7. +1 −0  build/cpf/amqphp/ChannelEventHandler.php
  8. +0 −2  build/cpf/amqphp/ConditionalExitStrategy.php
  9. +1 −0  build/cpf/amqphp/ConditionalExitStrategy.php
  10. +0 −2  build/cpf/amqphp/Connection.php
  11. +1 −0  build/cpf/amqphp/Connection.php
  12. +0 −2  build/cpf/amqphp/Consumer.php
  13. +1 −0  build/cpf/amqphp/Consumer.php
  14. +0 −2  build/cpf/amqphp/EventLoop.php
  15. +1 −0  build/cpf/amqphp/EventLoop.php
  16. +0 −2  build/cpf/amqphp/ExitStrategy.php
  17. +1 −0  build/cpf/amqphp/ExitStrategy.php
  18. +0 −2  build/cpf/amqphp/Factory.php
  19. +1 −0  build/cpf/amqphp/Factory.php
  20. +0 −2  build/cpf/amqphp/MaxloopExitStrategy.php
  21. +1 −0  build/cpf/amqphp/MaxloopExitStrategy.php
  22. +0 −2  build/cpf/amqphp/SimpleConsumer.php
  23. +1 −0  build/cpf/amqphp/SimpleConsumer.php
  24. +0 −2  build/cpf/amqphp/Socket.php
  25. +1 −0  build/cpf/amqphp/Socket.php
  26. +0 −2  build/cpf/amqphp/StreamSocket.php
  27. +1 −0  build/cpf/amqphp/StreamSocket.php
  28. +0 −2  build/cpf/amqphp/TimeoutExitStrategy.php
  29. +1 −0  build/cpf/amqphp/TimeoutExitStrategy.php
  30. +0 −2  build/cpf/amqphp/persistent/APCPersistenceHelper.php
  31. +1 −0  build/cpf/amqphp/persistent/APCPersistenceHelper.php
  32. +0 −2  build/cpf/amqphp/persistent/FilePersistenceHelper.php
  33. +1 −0  build/cpf/amqphp/persistent/FilePersistenceHelper.php
  34. +0 −2  build/cpf/amqphp/persistent/PChannel.php
  35. +1 −0  build/cpf/amqphp/persistent/PChannel.php
  36. +0 −2  build/cpf/amqphp/persistent/PConnection.php
  37. +1 −0  build/cpf/amqphp/persistent/PConnection.php
  38. +0 −2  build/cpf/amqphp/persistent/PersistenceHelper.php
  39. +1 −0  build/cpf/amqphp/persistent/PersistenceHelper.php
  40. +0 −2  build/cpf/amqphp/protocol/abstrakt/ClassFactory.php
  41. +1 −0  build/cpf/amqphp/protocol/abstrakt/ClassFactory.php
  42. +0 −2  build/cpf/amqphp/protocol/abstrakt/DomainFactory.php
  43. +1 −0  build/cpf/amqphp/protocol/abstrakt/DomainFactory.php
  44. +0 −2  build/cpf/amqphp/protocol/abstrakt/FieldFactory.php
  45. +1 −0  build/cpf/amqphp/protocol/abstrakt/FieldFactory.php
  46. +0 −2  build/cpf/amqphp/protocol/abstrakt/MethodFactory.php
  47. +1 −0  build/cpf/amqphp/protocol/abstrakt/MethodFactory.php
  48. +0 −2  build/cpf/amqphp/protocol/abstrakt/XmlSpecClass.php
  49. +1 −0  build/cpf/amqphp/protocol/abstrakt/XmlSpecClass.php
  50. +0 −2  build/cpf/amqphp/protocol/abstrakt/XmlSpecDomain.php
  51. +1 −0  build/cpf/amqphp/protocol/abstrakt/XmlSpecDomain.php
  52. +0 −2  build/cpf/amqphp/protocol/abstrakt/XmlSpecField.php
  53. +1 −0  build/cpf/amqphp/protocol/abstrakt/XmlSpecField.php
  54. +0 −2  build/cpf/amqphp/protocol/abstrakt/XmlSpecMethod.php
  55. +1 −0  build/cpf/amqphp/protocol/abstrakt/XmlSpecMethod.php
  56. +0 −2  build/cpf/amqphp/wire/Decimal.php
  57. +1 −0  build/cpf/amqphp/wire/Decimal.php
  58. +0 −2  build/cpf/amqphp/wire/Hexdump.php
  59. +1 −0  build/cpf/amqphp/wire/Hexdump.php
  60. +0 −2  build/cpf/amqphp/wire/Method.php
  61. +1 −0  build/cpf/amqphp/wire/Method.php
  62. +0 −2  build/cpf/amqphp/wire/Protocol.php
  63. +1 −0  build/cpf/amqphp/wire/Protocol.php
  64. +0 −2  build/cpf/amqphp/wire/Reader.php
  65. +1 −0  build/cpf/amqphp/wire/Reader.php
  66. +0 −2  build/cpf/amqphp/wire/Table.php
  67. +1 −0  build/cpf/amqphp/wire/Table.php
  68. +0 −2  build/cpf/amqphp/wire/TableField.php
  69. +1 −0  build/cpf/amqphp/wire/TableField.php
  70. +0 −2  build/cpf/amqphp/wire/Writer.php
  71. +1 −0  build/cpf/amqphp/wire/Writer.php
  72. +17 −10 demos/producer.php
  73. +16 −27 src/amqphp/Channel.php
5 TODO.dev
View
@@ -22,7 +22,4 @@
http://www.rabbitmq.com/ha.html
In particular: "As a result of the requeuing, clients that
re-consume from the queue must be aware that they are likely to
- subsequently receive messages that they have seen previously"
-
- * Add support for configurable multiple acks, i.e. automatically
- buffer up to N acks and send as a single nack.
+ subsequently receive messages that they have seen previously"
2  build/cpf/amqphp/CallbackExitStrategy.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; class CallbackExitStrategy implements ExitStrategy { private $cb; private $args; function configure ($sMode, $cb=null, $args=null) { if (! is_callable($cb)) { trigger_error("Select mode - invalid callback params", E_USER_WARNING); return false; } else { $this->cb = $cb; $this->args = $args; return true; } } function init (Connection $conn) {} function preSelect ($prev=null) { if ($prev === false) { return false; } if (true !== call_user_func_array($this->cb, $this->args)) { return false; } else { return $prev; } } function complete () {} }
1  build/cpf/amqphp/CallbackExitStrategy.php
View
2  build/cpf/amqphp/Channel.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; class Channel { protected $myConn; protected $chanId; protected $flow = true; private $destroyed = false; protected $frameMax; protected $isOpen = false; protected $consumers = array(); protected $callbackHandler; protected $confirmSeqs = array(); protected $confirmSeq = 0; protected $confirmMode = false; public $ackBuffer = 5; protected $pendingAcks = array(); protected $numPendAcks = 0; protected $ackFlag; function setEventHandler (ChannelEventHandler $evh) { $this->callbackHandler = $evh; } function hasOutstandingConfirms () { return (bool) $this->confirmSeqs; } function setConfirmMode () { if ($this->confirmMode) { return; } $confSelect = $this->confirm('select'); $confSelectOk = $this->invoke($confSelect); if (! ($confSelectOk instanceof wire\Method) || $confSelectOk->amqpClass != 'confirm.select-ok') { throw new \Exception("Failed to set confirm mode", 8674); } $this->confirmMode = true; } function setConnection (Connection $rConn) { $this->myConn = $rConn; } function setChanId ($chanId) { $this->chanId = $chanId; } function getChanId () { return $this->chanId; } function setFrameMax ($frameMax) { $this->frameMax = $frameMax; } function initChannel () { $pl = $this->myConn->getProtocolLoader(); $meth = new wire\Method($pl('ClassFactory', 'GetMethod', array('channel', 'open')), $this->chanId); $meth->setField('reserved-1', ''); $resp = $this->myConn->invoke($meth); } function __call ($class, $args) { if ($this->destroyed) { throw new \Exception("Attempting to use a destroyed channel", 8766); } $m = $this->myConn->constructMethod($class, $args); $m->setWireChannel($this->chanId); $m->setMaxFrameSize($this->frameMax); return $m; } function invoke (wire\Method $m) { if ($this->destroyed) { throw new \Exception("Attempting to use a destroyed channel", 8767); } else if (! $this->flow) { trigger_error("Channel is closed", E_USER_WARNING); return; } else if (is_null($tmp = $m->getWireChannel())) { $m->setWireChannel($this->chanId); } else if ($tmp != $this->chanId) { throw new \Exception("Method is invoked through the wrong channel", 7645); } if ($this->confirmMode && $m->amqpClass == 'basic.publish') { $this->confirmSeq++; $this->confirmSeqs[] = $this->confirmSeq; } return $this->myConn->invoke($m); } function handleChannelMessage (wire\Method $meth) { switch ($meth->amqpClass) { case 'channel.flow': $this->flow = ! $this->flow; if ($r = $meth->getMethodProto()->getResponses()) { $meth = new wire\Method($r[0], $this->chanId); $this->invoke($meth); } return false; case 'channel.close': $pl = $this->myConn->getProtocolLoader(); if ($culprit = $pl('ClassFactory', 'GetMethod', array($meth->getField('class-id'), $meth->getField('method-id')))) { $culprit = $culprit->getSpecName(); } else { $culprit = '(Unknown or unspecified)'; } $errCode = $pl('ProtoConsts', 'Konstant', array($meth->getField('reply-code'))); $eb = ''; foreach ($meth->getFields() as $k => $v) { $eb .= sprintf("(%s=%s) ", $k, $v); } $tmp = $meth->getMethodProto()->getResponses(); $closeOk = new wire\Method($tmp[0], $this->chanId); $em = "[channel.close] reply-code={$errCode['name']} triggered by $culprit: $eb"; try { $this->myConn->invoke($closeOk); $em .= " Channel closed OK"; $n = 3687; } catch (\Exception $e) { $em .= " Additionally, channel closure ack send failed"; $n = 2435; } throw new \Exception($em, $n); case 'channel.close-ok': case 'channel.open-ok': case 'channel.flow-ok': return true; default: throw new \Exception("Received unexpected channel message: {$meth->amqpClass}", 8795); } } function handleChannelDelivery (wire\Method $meth) { switch ($meth->amqpClass) { case 'basic.deliver': return $this->deliverConsumerMessage($meth); case 'basic.return': if ($this->callbackHandler) { $this->callbackHandler->publishReturn($meth); } return false; case 'basic.ack': $this->removeConfirmSeqs($meth, 'publishConfirm'); return false; case 'basic.nack': $this->removeConfirmSeqs($meth, 'publishNack'); return false; case 'basic.cancel': $this->handleConsumerCancel($meth); break; default: throw new \Exception("Received unexpected channel delivery:\n{$meth->amqpClass}", 87998); } } private function handleConsumerCancel ($meth) { $ctag = $meth->getField('consumer-tag'); list($cons, $status,) = $this->getConsumerAndStatus($ctag); if ($cons && $status == 'READY') { $cons->handleCancel($meth, $this); $this->setConsumerStatus($ctag, 'CLOSED') OR trigger_error("Failed to set consumer status flag (2)", E_USER_WARNING); if (! $meth->getField('no-wait')) { $this->invoke($this->basic('cancel-ok', array('consumer-tag', $ctag))); } } else if ($cons) { $m = sprintf("Cancellation message delivered to closed consumer %s", $ctag); trigger_error($m, E_USER_WARNING); } else { $m = sprintf("Unable to load consumer for consumer cancellation %s", $ctag); trigger_error($m, E_USER_WARNING); } } private function deliverConsumerMessage ($meth) { $ctag = $meth->getField('consumer-tag'); $response = false; list($cons, $status, $consParams) = $this->getConsumerAndStatus($ctag); if ($cons && $status == 'READY') { $response = $cons->handleDelivery($meth, $this); } else if ($cons) { $m = sprintf("Message delivered to closed consumer %s in non-ready state %s -- reject %s", $ctag, $status, $meth->getField('delivery-tag')); trigger_error($m, E_USER_WARNING); $response = CONSUMER_REJECT; } else { $m = sprintf("Unable to load consumer for delivery %s -- reject %s", $ctag, $meth->getField('delivery-tag')); trigger_error($m, E_USER_WARNING); $response = CONSUMER_REJECT; } if (! $response) { return false; } if (! is_array($response)) { $response = array($response); } foreach ($response as $resp) { switch ($resp) { case CONSUMER_ACK: if (! array_key_exists('no-ack', $consParams) || ! $consParams['no-ack']) { $this->ack($meth, CONSUMER_ACK); } break; case CONSUMER_DROP: case CONSUMER_REJECT: if (! array_key_exists('no-ack', $consParams) || ! $consParams['no-ack']) { $this->ack($meth, $resp); } break; case CONSUMER_CANCEL: $this->removeConsumerByTag($cons, $ctag); break; } } return false; } private function ack ($meth, $action) { if (is_null($this->ackFlag)) { $this->ackFlag = $action; } else if ($action != $this->ackFlag) { $this->flushAcks(); $this->ackFlag = $action; } $this->pendingAcks[] = $meth->getField('delivery-tag'); $this->numPendAcks++; if ($this->numPendAcks >= $this->ackBuffer) { $this->flushAcks(); } } private function flushAcks () { if (is_null($this->ackFlag)) { return; } switch ($this->ackFlag) { case CONSUMER_ACK: $ack = $this->basic('ack', array('delivery-tag' => array_pop($this->pendingAcks), 'multiple' => true)); $this->invoke($ack); break; case CONSUMER_REJECT: case CONSUMER_DROP: $rej = $this->basic('nack', array('delivery-tag' => array_pop($this->pendingAcks), 'multiple' => true, 'requeue' => ($this->ackFlag == CONSUMER_REJECT))); $this->invoke($rej); break; default: throw new \Exception("Internal (n)ack tracking state error", 2956); } $this->ackFlag = null; $this->numPendAcks = 0; $this->pendingAcks = array(); } private function removeConfirmSeqs (wire\Method $meth, $event) { if ($meth->getField('multiple')) { $dtag = $meth->getField('delivery-tag'); $evh = $this->callbackHandler; $this->confirmSeqs = array_filter($this->confirmSeqs, function ($id) use ($dtag, $evh, $event, $meth) { if ($id <= $dtag) { if ($evh) { $evh->$event($meth); } return false; } else { return true; } }); } else { $dt = $meth->getField('delivery-tag'); if (isset($this->confirmSeqs)) { if ($this->callbackHandler) { $this->callbackHandler->$event($meth); } unset($this->confirmSeqs[array_search($dt, $this->confirmSeqs)]); } } } function shutdown () { if (! $this->invoke($this->channel('close', array('reply-code' => '', 'reply-text' => '')))) { trigger_error("Unclean channel shutdown", E_USER_WARNING); } $this->myConn->removeChannel($this); $this->destroyed = true; $this->myConn = $this->chanId = $this->ticket = null; } function addConsumer (Consumer $cons, array $cParams=null) { $this->consumers[] = array($cons, false, 'READY_WAIT', $cParams); } function canListen () { return $this->hasListeningConsumers() || $this->hasOutstandingConfirms(); } function removeConsumer (Consumer $cons) { foreach ($this->consumers as $c) { if ($c[0] === $cons) { if ($c[2] == 'READY') { $this->removeConsumerByTag($c[0], $c[1]); } return; } } trigger_error("Consumer does not belong to this Channel", E_USER_WARNING); } function removeAllConsumers () { foreach ($this->consumers as $c) { if ($c[2] == 'READY') { $this->removeConsumerByTag($c[0], $c[1]); } } } private function removeConsumerByTag (Consumer $cons, $ctag) { list(, $cstate,) = $this->getConsumerAndStatus($ctag); if ($cstate == 'CLOSED') { trigger_error("Consumer is already removed", E_USER_WARNING); return; } $cnl = $this->basic('cancel', array('consumer-tag' => $ctag, 'no-wait' => false)); $cOk = $this->invoke($cnl); if ($cOk->amqpClass == 'basic.cancel-ok') { $this->setConsumerStatus($ctag, 'CLOSED') OR trigger_error("Failed to set consumer status flag", E_USER_WARNING); } else { throw new \Exception("Failed to cancel consumer - bad broker response", 9768); } $cons->handleCancelOk($cOk, $this); } private function setConsumerStatus ($tag, $status) { foreach ($this->consumers as $k => $c) { if ($c[1] === $tag) { $this->consumers[$k][2] = $status; return true; } } return false; } private function getConsumerAndStatus ($tag) { foreach ($this->consumers as $c) { if ($c[1] == $tag) { return array($c[0], $c[2], $c[3]); } } return array(null, 'INVALID', null); } function hasListeningConsumers () { foreach ($this->consumers as $c) { if ($c[2] === 'READY') { return true; } } return false; } function getConsumerByTag ($t) { foreach ($this->consumers as $c) { if ($c[2] == 'READY' && $c[1] === $t) { return $c[0]; } } } function getConsumerTags () { $tags = array(); foreach ($this->consumers as $c) { if ($c[2] == 'READY') { $tags[] = $c[1]; } } return $tags; } function startAllConsumers () { if (! $this->consumers) { return false; } $r = false; foreach (array_keys($this->consumers) as $cnum) { if (false === $this->consumers[$cnum][1]) { $this->_startConsumer($cnum); $r = true; } } return $r; } private function _startConsumer ($cnum) { $consume = false; if (($consume = $this->consumers[$cnum][0]->getConsumeMethod($this)) && ! ($consume instanceof wire\Method)) { trigger_error("Consumer returned invalid consume method", E_USER_WARNING); $consume = false; } if (! $consume && is_array($this->consumers[$cnum][3])) { $consume = $this->basic('consume', $this->consumers[$cnum][3]); } if (! $consume) { throw new \Exception("Couldn't find any consume paramters while starting consumer", 9265); } $cOk = $this->invoke($consume); $this->consumers[$cnum][0]->handleConsumeOk($cOk, $this); $this->consumers[$cnum][2] = 'READY'; $this->consumers[$cnum][1] = $cOk->getField('consumer-tag'); $this->consumers[$cnum][3] = $consume->getFields(); } function startConsumer (Consumer $cons) { foreach ($this->consumers as $i => $c) { if ($c[0] === $cons && $c[1] === false) { $this->_startConsumer($i); return true; } } return false; } function onSelectEnd () { $this->flushAcks(); $this->consuming = false; } function isSuspended () { return ! $this->flow; } function toggleFlow () { $flow = ! $this->flow; $this->flow = true; $meth = $this->channel('flow', array('active' => $flow)); $fr = $this->invoke($meth); $newFlow = $fr->getField('active'); if ($newFlow != $flow) { trigger_error(sprintf("Flow Unexpected channel flow response, expected %d, got %d", ! $this->flow, $this->flow), E_USER_WARNING); } $this->flow = $newFlow; } }
1  build/cpf/amqphp/Channel.php
View
2  build/cpf/amqphp/ChannelEventHandler.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; interface ChannelEventHandler { function publishConfirm (wire\Method $meth); function publishReturn (wire\Method $meth); function publishNack(wire\Method $meth); }
1  build/cpf/amqphp/ChannelEventHandler.php
View
2  build/cpf/amqphp/ConditionalExitStrategy.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; class ConditionalExitStrategy implements ExitStrategy { private $conn; function configure ($sMode) {} function init (Connection $conn) { $this->conn = $conn; } function preSelect ($prev=null) { if ($prev === false) { return false; } $hasConsumers = false; foreach ($this->conn->getChannels() as $chan) { if ($chan->canListen()) { $hasConsumers = true; break; } } if (! $hasConsumers) { return false; } else { return $prev; } } function complete () { $this->conn = null; } }
1  build/cpf/amqphp/ConditionalExitStrategy.php
View
2  build/cpf/amqphp/Connection.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; const DEBUG = false; const PROTOCOL_HEADER = "AMQP\x00\x00\x09\x01"; const STRAT_TIMEOUT_ABS = 1; const STRAT_TIMEOUT_REL = 2; const STRAT_MAXLOOPS = 3; const STRAT_CALLBACK = 4; const STRAT_COND = 5; const CONSUMER_ACK = 1; const CONSUMER_REJECT = 2; const CONSUMER_DROP = 3; const CONSUMER_CANCEL = 4; class Connection { public static $ClientProperties = array( 'product' => ' BraveSirRobin/amqphp', 'version' => '0.9.3', 'platform' => 'PHP 5.3 +', 'copyright' => 'Copyright (c) 2010,2011,2012 Robin Harvey (harvey.robin@gmail.com)', 'information' => 'This software is released under the terms of the GNU LGPL: http://www.gnu.org/licenses/lgpl-3.0.txt', 'capabilities' => array('exchange_exchange_bindings' => true, 'consumer_cancel_notify' => true, 'basic.nack' => true, 'publisher_confirms' => true)); public $capabilities; private static $CProps = array( 'socketImpl', 'socketParams', 'username', 'userpass', 'vhost', 'frameMax', 'chanMax', 'signalDispatch', 'heartbeat', 'socketFlags'); protected $sock; protected $socketImpl = '\amqphp\Socket'; protected $protoImpl = 'v0_9_1'; private $protoLoader; protected $socketParams = array('host' => 'localhost', 'port' => 5672); private $socketFlags; private $username; private $userpass; protected $vhost; protected $frameMax = 65536; protected $chanMax = 50; private $heartbeat = 0; protected $signalDispatch = true; protected $chans = array(); protected $nextChan = 1; private $blocking = false; protected $unDelivered = array(); protected $unDeliverable = array(); protected $incompleteMethods = array(); protected $readSrc = null; protected $connected = false; private $exStrats = array(); function __construct (array $params = array()) { $this->setConnectionParams($params); } function setConnectionParams (array $params) { foreach (self::$CProps as $pn) { if (isset($params[$pn])) { $this->$pn = $params[$pn]; } } } function getProtocolLoader () { if (is_null($this->protoLoader)) { $protoImpl = $this->protoImpl; $this->protoLoader = function ($class, $method, $args) use ($protoImpl) { $fqClass = '\\amqphp\\protocol\\' . $protoImpl . '\\' . $class; return call_user_func_array(array($fqClass, $method), $args); }; } return $this->protoLoader; } function shutdown () { if (! $this->connected) { trigger_error("Cannot shut a closed connection", E_USER_WARNING); return; } foreach (array_keys($this->chans) as $cName) { $this->chans[$cName]->shutdown(); } $pl = $this->getProtocolLoader(); $meth = new wire\Method($pl('ClassFactory', 'GetMethod', array('connection', 'close'))); $meth->setField('reply-code', ''); $meth->setField('reply-text', ''); $meth->setField('class-id', ''); $meth->setField('method-id', ''); if (! $this->write($meth->toBin($pl))) { trigger_error("Unclean connection shutdown (1)", E_USER_WARNING); return; } if (! ($raw = $this->read())) { trigger_error("Unclean connection shutdown (2)", E_USER_WARNING); return; } $meth = new wire\Method(); $meth->readConstruct(new wire\Reader($raw), $pl); if ($meth->amqpClass != 'connection.close-ok') { trigger_error("Channel protocol shudown fault", E_USER_WARNING); } $this->sock->close(); $this->connected = false; } protected function initSocket () { if (! isset($this->socketImpl)) { throw new \Exception("No socket implementation specified", 7545); } $this->sock = new $this->socketImpl($this->socketParams, $this->socketFlags, $this->vhost); } function connect () { if ($this->connected) { trigger_error("Connection is connected already", E_USER_WARNING); return; } $this->initSocket(); $this->sock->connect(); $this->doConnectionStartup(); } protected function doConnectionStartup () { if (! $this->write(PROTOCOL_HEADER)) { throw new \Exception("Connection initialisation failed (1)", 9873); } if (! ($raw = $this->read())) { throw new \Exception("Connection initialisation failed (2)", 9874); } if (substr($raw, 0, 4) == 'AMQP' && $raw !== PROTOCOL_HEADER) { throw new \Exception("Connection initialisation failed (3)", 9875); } $meth = new wire\Method(); $pl = $this->getProtocolLoader(); $meth->readConstruct(new wire\Reader($raw), $pl); if (($startF = $meth->getField('server-properties')) && isset($startF['capabilities']) && ($startF['capabilities']->getType() == 'F')) { $this->capabilities = $startF['capabilities']->getValue()->getArrayCopy(); } if ($meth->amqpClass == 'connection.start') { $resp = $meth->getMethodProto()->getResponses(); $meth = new wire\Method($resp[0]); } else { throw new \Exception("Connection initialisation failed (5)", 9877); } $meth->setField('client-properties', new wire\Table(self::$ClientProperties)); $meth->setField('mechanism', 'AMQPLAIN'); $meth->setField('response', $this->getSaslResponse()); $meth->setField('locale', 'en_US'); if (! ($this->write($meth->toBin($pl)))) { throw new \Exception("Connection initialisation failed (6)", 9878); } if (! ($raw = $this->read())) { throw new \Exception("Connection initialisation failed (7)", 9879); } $meth = new wire\Method(); $meth->readConstruct(new wire\Reader($raw), $pl); $chanMax = $meth->getField('channel-max'); $frameMax = $meth->getField('frame-max'); $this->chanMax = ($chanMax < $this->chanMax) ? $chanMax : $this->chanMax; $this->frameMax = ($this->frameMax == 0 || $frameMax < $this->frameMax) ? $frameMax : $this->frameMax; if ($meth->amqpClass == 'connection.tune') { $resp = $meth->getMethodProto()->getResponses(); $meth = new wire\Method($resp[0]); } else { throw new \Exception("Connection initialisation failed (9)", 9881); } $meth->setField('channel-max', $this->chanMax); $meth->setField('frame-max', $this->frameMax); $meth->setField('heartbeat', $this->heartbeat); if (! ($this->write($meth->toBin($pl)))) { throw new \Exception("Connection initialisation failed (10)", 9882); } $meth = $this->constructMethod('connection', array('open', array('virtual-host' => $this->vhost))); $meth = $this->invoke($meth); if ($meth->amqpClass != 'connection.open-ok') { throw new \Exception("Connection initialisation failed (13)", 9885); } $this->connected = true; } private function getSaslResponse () { $t = new wire\Table(array('LOGIN' => $this->username, 'PASSWORD' => $this->userpass)); $w = new wire\Writer(); $w->write($t, 'table'); return substr($w->getBuffer(), 4); } function getChannel ($num) { return $this->chans[$num]; } function openChannel () { return $this->initNewChannel(__NAMESPACE__ . '\\Channel'); } function getChannels () { return $this->chans; } function setSignalDispatch ($val) { $this->signalDispatch = (boolean) $val; } function getSignalDispatch () { return $this->signalDispatch; } function removeChannel (Channel $chan) { if (false !== ($k = array_search($chan, $this->chans))) { unset($this->chans[$k]); } else { trigger_error("Channel not found", E_USER_WARNING); } } function getSocketId () { return $this->sock->getId(); } function getSocketCK () { return $this->sock->getCK(); } protected function initNewChannel ($impl=null) { if (! $this->connected) { trigger_error("Connection is not connected - cannot create Channel", E_USER_WARNING); return null; } $newChan = $this->nextChan++; if ($this->chanMax > 0 && $newChan > $this->chanMax) { throw new \Exception("Channels are exhausted!", 23756); } $this->chans[$newChan] = is_null($impl) ? new Channel : new $impl; $this->chans[$newChan]->setConnection($this); $this->chans[$newChan]->setChanId($newChan); $this->chans[$newChan]->setFrameMax($this->frameMax); $this->chans[$newChan]->initChannel(); return $this->chans[$newChan]; } function getVHost () { return $this->vhost; } function getSocketImplClass () { return $this->socketImpl; } function isConnected () { return $this->connected; } private function read () { $ret = $this->sock->read(); if ($ret === false) { $errNo = $this->sock->lastError(); if ($this->signalDispatch && $this->sock->selectInterrupted()) { pcntl_signal_dispatch(); } $errStr = $this->sock->strError(); throw new \Exception ("[1] Read block select produced an error: [$errNo] $errStr", 9963); } return $ret; } private function write ($buffs) { $bw = 0; foreach ((array) $buffs as $buff) { $bw += $this->sock->write($buff); } return $bw; } private function handleConnectionMessage (wire\Method $meth) { if ($meth->isHeartbeat()) { $resp = "\x08\x00\x00\x00\x00\x00\x00\xce"; $this->write($resp); return; } switch ($meth->amqpClass) { case 'connection.close': $pl = $this->getProtocolLoader(); if ($culprit = $pl('ClassFactory', 'GetMethod', array($meth->getField('class-id'), $meth->getField('method-id')))) { $culprit = $culprit->getSpecName(); } else { $culprit = '(Unknown or unspecified)'; } $errCode = $pl('ProtoConsts', 'Konstant', array($meth->getField('reply-code'))); $eb = ''; foreach ($meth->getFields() as $k => $v) { $eb .= sprintf("(%s=%s) ", $k, $v); } $tmp = $meth->getMethodProto()->getResponses(); $closeOk = new wire\Method($tmp[0]); $em = "[connection.close] reply-code={$errCode['name']} triggered by $culprit: $eb"; if ($this->write($closeOk->toBin($pl))) { $em .= " Connection closed OK"; $n = 7565; } else { $em .= " Additionally, connection closure ack send failed"; $n = 7566; } $this->sock->close(); throw new \Exception($em, $n); default: $this->sock->close(); throw new \Exception(sprintf("Unexpected channel message (%s), connection closed", $meth->amqpClass), 96356); } } function isBlocking () { return $this->blocking; } function setBlocking ($b) { $this->blocking = (boolean) $b; } function select () { $evl = new EventLoop; $evl->addConnection($this); $evl->select(); } function pushExitStrategy () { if ($this->blocking) { trigger_error("Push exit strategy - cannot switch mode whilst blocking", E_USER_WARNING); return false; } $_args = func_get_args(); if (! $_args) { trigger_error("Push exit strategy - no select parameters supplied", E_USER_WARNING); return false; } switch ($mode = array_shift($_args)) { case STRAT_TIMEOUT_ABS: case STRAT_TIMEOUT_REL: @list($epoch, $usecs) = $_args; $this->exStrats[] = $tmp = new TimeoutExitStrategy; return $tmp->configure($mode, $epoch, $usecs); case STRAT_MAXLOOPS: $this->exStrats[] = $tmp = new MaxloopExitStrategy; return $tmp->configure(STRAT_MAXLOOPS, array_shift($_args)); case STRAT_CALLBACK: $cb = array_shift($_args); $this->exStrats[] = $tmp = new CallbackExitStrategy; return $tmp->configure(STRAT_CALLBACK, $cb, $_args); case STRAT_COND: $this->exStrats[] = $tmp = new ConditionalExitStrategy; return $tmp->configure(STRAT_COND, $this); default: trigger_error("Select mode - mode not found", E_USER_WARNING); return false; } } function clearExitStrategies () { $this->exStrats = array(); } function notifyPreSelect () { $r = true; foreach ($this->exStrats as $strat) { $r = $strat->preSelect($r); } return $r; } function notifySelectInit () { foreach ($this->exStrats as $strat) { $strat->init($this); } foreach ($this->chans as $chan) { $chan->startAllConsumers(); } } function notifyComplete () { foreach($this->exStrats as $strat) { $strat->complete(); } foreach ($this->chans as $chan) { $chan->onSelectEnd(); } } function doSelectRead () { $buff = $this->sock->readAll(); if ($buff && ($meths = $this->readMessages($buff))) { $this->unDelivered = array_merge($this->unDelivered, $meths); } else if ($buff === '') { $this->blocking = false; throw new \Exception("Empty read in blocking select loop, socket error:\n" . $this->sock->strError(), 9864); } } function invoke (wire\Method $inMeth, $noWait=false) { if (! ($this->write($inMeth->toBin($this->getProtocolLoader())))) { throw new \Exception("Send message failed (1)", 5623); } if (! $noWait && $inMeth->getMethodProto()->getSpecResponseMethods()) { if ($inMeth->getMethodProto()->hasNoWaitField()) { foreach ($inMeth->getMethodProto()->getFields() as $f) { if ($f->getSpecDomainName() == 'no-wait' && $inMeth->getField($f->getSpecFieldName())) { return; } } } while (true) { if (! ($buff = $this->read())) { throw new \Exception(sprintf("(2) Send message failed for %s:\n", $inMeth->amqpClass), 5624); } $meths = $this->readMessages($buff); foreach (array_keys($meths) as $k) { $meth = $meths[$k]; unset($meths[$k]); if ($inMeth->isResponse($meth)) { if ($meths) { $this->unDelivered = array_merge($this->unDelivered, $meths); } return $meth; } else { $this->unDelivered[] = $meth; } } } } } private function readMessages ($buff) { if (is_null($this->readSrc)) { $src = new wire\Reader($buff); } else { $src = $this->readSrc; $src->append($buff); $this->readSrc = null; } $allMeths = array(); $pl = $this->getProtocolLoader(); while (true) { $meth = null; if ($this->incompleteMethods) { foreach ($this->incompleteMethods as $im) { if ($im->canReadFrom($src)) { $meth = $im; $rcr = $meth->readConstruct($src, $pl); break; } } } if (! $meth) { $meth = new wire\Method; $this->incompleteMethods[] = $meth; $rcr = $meth->readConstruct($src, $pl); } if ($meth->readConstructComplete()) { if (false !== ($p = array_search($meth, $this->incompleteMethods, true))) { unset($this->incompleteMethods[$p]); } if ($this->connected && $meth->getWireChannel() == 0) { $this->handleConnectionMessage($meth); } else if ($meth->getWireClassId() == 20 && ($chan = $this->chans[$meth->getWireChannel()])) { $chanR = $chan->handleChannelMessage($meth); if ($chanR === true) { $allMeths[] = $meth; } } else { $allMeths[] = $meth; } } if ($rcr === wire\Method::PARTIAL_FRAME) { $this->readSrc = $src; break; } else if ($src->isSpent()) { break; } } return $allMeths; } function getUndeliveredMessages () { return $this->unDelivered; } function deliverAll () { while ($this->unDelivered) { $meth = array_shift($this->unDelivered); if (isset($this->chans[$meth->getWireChannel()])) { $this->chans[$meth->getWireChannel()]->handleChannelDelivery($meth); } else { trigger_error("Message delivered on unknown channel", E_USER_WARNING); $this->unDeliverable[] = $meth; } } } function getUndeliverableMessages ($chan) { $r = array(); foreach (array_keys($this->unDeliverable) as $k) { if ($this->unDeliverable[$k]->getWireChannel() == $chan) { $r[] = $this->unDeliverable[$k]; } } return $r; } function removeUndeliverableMessages ($chan) { foreach (array_keys($this->unDeliverable) as $k) { if ($this->unDeliverable[$k]->getWireChannel() == $chan) { unset($this->unDeliverable[$k]); } } } function constructMethod ($class, $_args) { $method = (isset($_args[0])) ? $_args[0] : null; $args = (isset($_args[1])) ? $_args[1] : array(); $content = (isset($_args[2])) ? $_args[2] : null; $pl = $this->getProtocolLoader(); if (! ($cls = $pl('ClassFactory', 'GetClassByName', array($class)))) { throw new \Exception("Invalid Amqp class or php method", 8691); } else if (! ($meth = $cls->getMethodByName($method))) { throw new \Exception("Invalid Amqp method", 5435); } $m = new wire\Method($meth); foreach ($args as $k => $v) { $m->setField($k, $v); } $m->setContent($content); return $m; } }
1  build/cpf/amqphp/Connection.php
View
2  build/cpf/amqphp/Consumer.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\wire; interface Consumer { function handleCancelOk (wire\Method $meth, Channel $chan); function handleCancel (wire\Method $meth, Channel $chan); function handleConsumeOk (wire\Method $meth, Channel $chan); function handleDelivery (wire\Method $meth, Channel $chan); function handleRecoveryOk (wire\Method $meth, Channel $chan); function getConsumeMethod (Channel $chan); }
1  build/cpf/amqphp/Consumer.php
View
2  build/cpf/amqphp/EventLoop.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; class EventLoop { private $cons = array(); private static $In = false; function addConnection (Connection $conn) { $this->cons[$conn->getSocketId()] = $conn; } function removeConnection (Connection $conn) { if (array_key_exists($conn->getSocketId(), $this->cons)) { unset($this->cons[$conn->getSocketId()]); } } function select () { $sockImpl = false; foreach ($this->cons as $c) { if ($c->isBlocking()) { throw new \Exception("Event loop cannot start - connection is already blocking", 3267); } if ($sockImpl === false) { $sockImpl = $c->getSocketImplClass(); } else if ($sockImpl != $c->getSocketImplClass()) { throw new \Exception("Event loop doesn't support mixed socket implementations", 2678); } if (! $c->isConnected()) { throw new \Exception("Connection is not connected", 2174); } } foreach ($this->cons as $c) { $c->setBlocking(true); $c->notifySelectInit(); } $started = false; while (true) { $tv = array(); foreach ($this->cons as $cid => $c) { $c->deliverAll(); $tv[] = array($cid, $c->notifyPreSelect()); } $psr = $this->processPreSelects($tv); if (is_array($psr)) { list($tvSecs, $tvUsecs) = $psr; } else if ($psr === true) { $tvSecs = null; $tvUsecs = 0; } else if (is_null($psr) && empty($this->cons)) { if (! $started) { trigger_error("Select loop not entered - no connections are listening", E_USER_WARNING); } break; } else { throw new \Exception("Unexpected PSR response", 2758); } $this->signal(); $started = true; if (is_null($tvSecs)) { list($ret, $read, $ex) = call_user_func(array($sockImpl, 'Zelekt'), array_keys($this->cons), null, 0); } else { list($ret, $read, $ex) = call_user_func(array($sockImpl, 'Zelekt'), array_keys($this->cons), $tvSecs, $tvUsecs); } if ($ret === false) { $this->signal(); $errNo = $errStr = array('(No specific socket exceptions found)'); if ($ex) { $errNo = $errStr = array(); foreach ($ex as $sock) { $errNo[] = $sock->lastError(); $errStr[] = $sock->strError(); } } $eMsg = sprintf("[2] Read block select produced an error: [%s] (%s)", implode(",", $errNo), implode("),(", $errStr)); throw new \Exception ($eMsg, 9963); } else if ($ret > 0) { foreach ($read as $sock) { $c = $this->cons[$sock->getId()]; $c->doSelectRead(); $c->deliverAll(); } } } foreach ($this->cons as $id => $conn) { $conn->notifyComplete(); $conn->setBlocking(false); $this->removeConnection($conn); } } private function processPreSelects (array $tvs) { $wins = null; foreach ($tvs as $tv) { $sid = $tv[0]; $tv = $tv[1]; if ($tv === false) { $this->cons[$sid]->notifyComplete(); $this->cons[$sid]->setBlocking(false); $this->removeConnection($this->cons[$sid]); } else if (is_null($wins)) { $wins = $tv; } else if ($tv === true && ! is_array($wins)) { $wins = true; } else if (is_array($tv)) { if ($wins === true) { $wins = $tv; } else { switch (bccomp((string) $wins[0], (string) $tv[0])) { case 0: if (1 === bccomp((string) $wins[1], (string) $tv[1])) { $wins = $tv; } break; case 1; $wins = $tv; break; } } } } return $wins; } private function signal () { foreach ($this->cons as $c) { if ($c->getSignalDispatch()) { pcntl_signal_dispatch(); return; } } } }
1  build/cpf/amqphp/EventLoop.php
View
2  build/cpf/amqphp/ExitStrategy.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; interface ExitStrategy { function configure ($sMode); function init (Connection $conn); function preSelect ($prev=null); function complete (); }
1  build/cpf/amqphp/ExitStrategy.php
View
2  build/cpf/amqphp/Factory.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; use \amqphp\persistent as pers; class Factory { const XML_FILE = 1; const XML_STRING = 2; private static $RC_CACHE = array(); private $simp; private $rootEl; function __construct ($xml, $documentURI=false, $flag=self::XML_FILE) { $d = new \DOMDocument; switch ($flag) { case self::XML_FILE: if (! $d->load($xml)) { throw new \Exception("Failed to load factory XML", 92656); } break; case self::XML_STRING: if (! $d->loadXML($xml)) { throw new \Exception("Failed to load factory XML", 92656); } if ($documentURI) { $d->documentURI = $documentURI; } break; default: throw new \Exception("Invalid construct flag", 95637); } if (-1 === $d->xinclude()) { throw new \Exception("Failed to load factory XML", 92657); } else if (! ($this->simp = simplexml_import_dom($d))) { throw new \Exception("Failed to load factory XML", 92658); } switch ($tmp = strtolower((string) $this->simp->getName())) { case 'setup': case 'methods': $this->rootEl = $tmp; break; default: throw new \Exception("Unexpected Factory configuration data root element", 17893); } } function run (Channel $chan=null) { switch ($this->rootEl) { case 'setup': return $this->runSetupSequence(); case 'methods': if (is_null($chan)) { throw new \Exception("Invalid factory configuration - expected a target channel", 15758); } return $this->runMethodSequence($chan, $this->simp->xpath('/methods/method')); } } function getConnections () { $r = array(); foreach ($this->run() as $res) { if ($res instanceof Connection) { $r[] = $res; } } return $r; } private function callProperties ($subj, $conf) { foreach ($conf->xpath('./set_properties/*') as $prop) { $pname = (string) $prop->getName(); $pval = $this->kast($prop, $prop['k']); $subj->$pname = $pval; } } private function runSetupSequence () { $ret = array(); foreach ($this->simp->connection as $conn) { $_chans = array(); $refl = $this->getRc((string) $conn->impl); $_conn = $refl->newInstanceArgs($this->xmlToArray($conn->constr_args->children())); $this->callProperties($_conn, $conn); $_conn->connect(); $ret[] = $_conn; if (count($conn->exit_strats) > 0) { foreach ($conn->exit_strats->strat as $strat) { call_user_func_array(array($_conn, 'pushExitStrategy'), $this->xmlToArray($strat->children())); } } if ($_conn instanceof pers\PConnection && $_conn->getPersistenceStatus() == pers\PConnection::SOCK_REUSED) { continue; } foreach ($conn->channel as $chan) { $_chan = $_conn->openChannel(); $this->callProperties($_chan, $chan); if (isset($chan->event_handler)) { $impl = (string) $chan->event_handler->impl; if (count($chan->event_handler->constr_args)) { $refl = $this->getRc($impl); $_evh = $refl->newInstanceArgs($this->xmlToArray($chan->event_handler->constr_args->children())); } else { $_evh = new $impl; } $_chan->setEventHandler($_evh); } $_chans[] = $_chan; $rMeths = $chan->xpath('.//method'); if (count($rMeths) > 0) { $ret[] = $this->runMethodSequence($_chan, $rMeths); } if (count($chan->confirm_mode) > 0 && $this->kast($chan->confirm_mode, 'boolean')) { $_chan->setConfirmMode(); } } $i = 0; foreach ($conn->channel as $chan) { $_chan = $_chans[$i++]; foreach ($chan->consumer as $cons) { $impl = (string) $cons->impl; if (count($cons->constr_args)) { $refl = $this->getRc($impl); $_cons = $refl->newInstanceArgs($this->xmlToArray($cons->constr_args->children())); } else { $_cons = new $impl; } $this->callProperties($_cons, $cons); $_chan->addConsumer($_cons); if (isset($cons->autostart) && $this->kast($cons->autostart, 'boolean')) { $_chan->startConsumer($_cons); } } } } return $ret; } private function runMethodSequence (Channel $chan, array $meths) { $r = array(); foreach ($meths as $iMeth) { $a = $this->xmlToArray($iMeth); $c = $a['a_class']; $r[] = $chan->invoke($chan->$c($a['a_method'], $a['a_args'])); } return $r; } private function kast ($val, $cast) { switch ($cast) { case 'string': return (string) $val; case 'bool': case 'boolean': $val = trim((string) $val); if ($val === '0' || strtolower($val) === 'false') { return false; } else if ($val === '1' || strtolower($val) === 'true') { return true; } else { trigger_error("Bad boolean cast $val - use 0/1 true/false", E_USER_WARNING); return true; } case 'int': case 'integer': return (int) $val; case 'const': return constant((string) $val); case 'eval': return eval((string) $val); default: trigger_error("Unknown Kast $cast", E_USER_WARNING); return (string) $val; } } private function xmlToArray (\SimpleXmlElement $e) { $ret = array(); foreach ($e as $c) { $ret[(string) $c->getName()] = (count($c) == 0) ? $this->kast($c, (string) $c['k']) : $this->xmlToArray($c); } return $ret; } private function getRc ($class) { return array_key_exists($class, self::$RC_CACHE) ? self::$RC_CACHE[$class] : (self::$RC_CACHE[$class] = new \ReflectionClass($class)); } }
1  build/cpf/amqphp/Factory.php
View
2  build/cpf/amqphp/MaxloopExitStrategy.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; class MaxloopExitStrategy implements ExitStrategy { private $maxLoops; private $nLoops; function configure ($sMode, $ml=null) { if (! (is_int($ml) || is_numeric($ml)) || $ml == 0) { trigger_error("Select mode - invalid maxloops params : '$ml'", E_USER_WARNING); return false; } else { $this->maxLoops = (int) $ml; return true; } } function init (Connection $conn) { $this->nLoops = 0; } function preSelect ($prev=null) { if ($prev === false) { return false; } if (++$this->nLoops > $this->maxLoops) { return false; } else { return $prev; } } function complete () {} }
1  build/cpf/amqphp/MaxloopExitStrategy.php
View
2  build/cpf/amqphp/SimpleConsumer.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; class SimpleConsumer implements Consumer { protected $consumeParams; protected $consuming = false; function __construct (array $consumeParams) { $this->consumeParams = $consumeParams; } function handleCancelOk (wire\Method $meth, Channel $chan) { $this->consuming = false; } function handleCancel (wire\Method $meth, Channel $chan) { $this->consuming = false; } function handleConsumeOk (wire\Method $meth, Channel $chan) { $this->consuming = true; } function handleDelivery (wire\Method $meth, Channel $chan) {} function handleRecoveryOk (wire\Method $meth, Channel $chan) {} function getConsumeMethod (Channel $chan) { return $chan->basic('consume', $this->consumeParams); } }
1  build/cpf/amqphp/SimpleConsumer.php
View
2  build/cpf/amqphp/Socket.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; class Socket { const READ_SELECT = 1; const WRITE_SELECT = 2; const READ_LENGTH = 4096; private static $All = array(); private static $Counter = 0; private $host; private $port; private $sock; private $id; private $vhost; private $connected = false; private static $interrupt = false; function __construct ($params, $flags, $vhost) { $this->host = $params['host']; $this->port = $params['port']; $this->id = ++self::$Counter; $this->vhost = $vhost; } function getVHost () { return $this->vhost; } function getCK () { return sprintf("%s:%s:%s", $this->host, $this->port, md5($this->vhost)); } function connect () { if (! ($this->sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP))) { throw new \Exception("Failed to create inet socket", 7895); } else if (! socket_connect($this->sock, $this->host, $this->port)) { throw new \Exception("Failed to connect inet socket ({$this->host}, {$this->port})", 7564); } $this->connected = true; self::$All[] = $this; } function isReusedPSock () { return false; } function select ($tvSec, $tvUsec = 0, $rw = self::READ_SELECT) { $read = $write = $ex = null; if ($rw & self::READ_SELECT) { $read = $ex = array($this->sock); } if ($rw & self::WRITE_SELECT) { $write = $ex = array($this->sock); } if (! $read && ! $write) { throw new \Exception("Select must read and/or write", 9864); } self::$interrupt = false; $ret = socket_select($read, $write, $ex, $tvSec, $tvUsec); if ($ret === false && $this->lastError() == SOCKET_EINTR) { self::$interrupt = true; } return $ret; } static function Zelekt (array $incSet, $tvSec, $tvUsec) { $write = null; $read = $all = array(); foreach (self::$All as $i => $o) { if (in_array($o->id, $incSet)) { $read[$i] = $all[$i] = $o->sock; } } $ex = $read; $ret = false; if ($read) { $ret = socket_select($read, $write, $ex, $tvSec, $tvUsec); } if ($ret === false && socket_last_error() == SOCKET_EINTR) { self::$interrupt = true; return false; } $_read = $_ex = array(); foreach ($read as $sock) { if (false !== ($key = array_search($sock, $all, true))) { $_read[] = self::$All[$key]; } } foreach ($ex as $k => $sock) { if (false !== ($key = array_search($sock, $all, true))) { $_ex[] = self::$All[$key]; } } return array($ret, $_read, $_ex); } function selectInterrupted () { return self::$interrupt; } function read () { $buff = ''; $select = $this->select(5); if ($select === false) { return false; } else if ($select > 0) { $buff = $this->readAll(); } return $buff; } function lastError () { return socket_last_error(); } function strError () { return socket_strerror($this->lastError()); } function readAll ($readLen = self::READ_LENGTH) { $buff = ''; while (@socket_recv($this->sock, $tmp, $readLen, MSG_DONTWAIT)) { $buff .= $tmp; } if (DEBUG) { echo "\n<read>\n"; echo wire\Hexdump::hexdump($buff); } return $buff; } function write ($buff) { $bw = 0; $contentLength = strlen($buff); while (true) { if (DEBUG) { echo "\n<write>\n"; echo wire\Hexdump::hexdump($buff); } if (($tmp = socket_write($this->sock, $buff)) === false) { throw new \Exception(sprintf("\nSocket write failed: %s\n", $this->strError()), 7854); } $bw += $tmp; if ($bw < $contentLength) { $buff = substr($buff, $bw); } else { break; } } return $bw; } function close () { $this->connected = false; socket_close($this->sock); $this->detach(); } private function detach () { if (false !== ($k = array_search($this, self::$All))) { unset(self::$All[$k]); } } function getId () { return $this->id; } }
1  build/cpf/amqphp/Socket.php
View
2  build/cpf/amqphp/StreamSocket.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; class StreamSocket { const READ_SELECT = 1; const WRITE_SELECT = 2; const READ_LENGTH = 4096; private static $All = array(); private static $Counter = 0; private $host; private $id; private $port; private $connected; private $interrupt = false; private $flags; private $vhost; private $stfp; function __construct ($params, $flags, $vhost) { $this->url = $params['url']; $this->context = isset($params['context']) ? $params['context'] : array(); $this->flags = $flags ? $flags : array(); $this->id = ++self::$Counter; $this->vhost = $vhost; } function getVHost () { return $this->vhost; } function getCK () { return md5(sprintf("%s:%s:%s", $this->url, $this->getFlags(), $this->vhost)); } private function getFlags () { $flags = STREAM_CLIENT_CONNECT; foreach ($this->flags as $f) { $flags |= constant($f); } return $flags; } function connect () { $context = stream_context_create($this->context); $flags = $this->getFlags(); $this->sock = stream_socket_client($this->url, $errno, $errstr, ini_get("default_socket_timeout"), $flags, $context); $this->stfp = ftell($this->sock); if (! $this->sock) { throw new \Exception("Failed to connect stream socket {$this->url}, ($errno, $errstr): flags $flags", 7568); } else if (($flags & STREAM_CLIENT_PERSISTENT) && $this->stfp > 0) { foreach (self::$All as $sock) { if ($sock !== $this && $sock->getCK() == $this->getCK()) { $this->sock = null; throw new \Exception(sprintf("Stream socket connection created a new wrapper object for " . "an existing persistent connection on URL %s", $this->url), 8164); } } } $this->connected = true; self::$All[] = $this; } function isReusedPSock () { return ($this->stfp > 0); } function getConnectionStartFP () { return $this->stfp; } function tell () { return ftell($this->sock); } function select ($tvSec, $tvUsec = 0, $rw = self::READ_SELECT) { $read = $write = $ex = null; if ($rw & self::READ_SELECT) { $read = $ex = array($this->sock); } if ($rw & self::WRITE_SELECT) { $write = array($this->sock); } if (! $read && ! $write) { throw new \Exception("Select must read and/or write", 9864); } $this->interrupt = false; $ret = stream_select($read, $write, $ex, $tvSec, $tvUsec); if ($ret === false) { $this->interrupt = true; } return $ret; } static function Zelekt (array $incSet, $tvSec, $tvUsec) { $write = null; $read = $all = array(); foreach (self::$All as $i => $o) { if (in_array($o->id, $incSet)) { $read[$i] = $all[$i] = $o->sock; } } $ex = $read; $ret = false; if ($read) { $ret = stream_select($read, $write, $ex, $tvSec, $tvUsec); } if ($ret === false) { return false; } $_read = $_ex = array(); foreach ($read as $sock) { if (false !== ($key = array_search($sock, $all, true))) { $_read[] = self::$All[$key]; } } foreach ($ex as $k => $sock) { if (false !== ($key = array_search($sock, $all, true))) { $_ex[] = self::$All[$key]; } } return array($ret, $_read, $_ex); } function selectInterrupted () { return $this->interrupt; } function lastError () { return 0; } function strError () { return '(\amqphp\StreamSocket->strError -- not implemented)'; } function readAll ($readLen = self::READ_LENGTH) { $buff = ''; do { $buff .= fread($this->sock, $readLen); $smd = stream_get_meta_data($this->sock); $readLen = min($smd['unread_bytes'], $readLen); } while ($smd['unread_bytes'] > 0); if (DEBUG) { echo "\n<read>\n"; echo wire\Hexdump::hexdump($buff); } return $buff; } function read () { return $this->readAll(); } function getUnreadBytes () { return ($smd = stream_get_meta_data($this->sock)) ? $smd['unread_bytes'] : false; } function eof () { return feof($this->sock); } function write ($buff) { $bw = 0; $contentLength = strlen($buff); if ($contentLength == 0) { return 0; } while (true) { if (DEBUG) { echo "\n<write>\n"; echo wire\Hexdump::hexdump($buff); } if (($tmp = fwrite($this->sock, $buff)) === false) { throw new \Exception(sprintf("\nStream write failed (error): %s\n", $this->strError()), 7854); } else if ($tmp === 0) { throw new \Exception(sprintf("\nStream write failed (zero bytes written): %s\n", $this->strError()), 7855); } $bw += $tmp; if ($bw < $contentLength) { $buff = substr($buff, $bw); } else { break; } } fflush($this->sock); return $bw; } function close () { $this->connected = false; fclose($this->sock); $this->detach(); } private function detach () { if (false !== ($k = array_search($this, self::$All))) { unset(self::$All[$k]); } } function getId () { return $this->id; } }
1  build/cpf/amqphp/StreamSocket.php
View
2  build/cpf/amqphp/TimeoutExitStrategy.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; class TimeoutExitStrategy implements ExitStrategy { private $toStyle; private $secs; private $usecs; private $epoch; function configure ($sMode, $secs=null, $usecs=null) { $this->toStyle = $sMode; $this->secs = (string) $secs; $this->usecs = (string) $usecs; return true; } function init (Connection $conn) { if ($this->toStyle == STRAT_TIMEOUT_REL) { list($uSecs, $epoch) = explode(' ', microtime()); $uSecs = bcmul($uSecs, '1000000'); $this->usecs = bcadd($this->usecs, $uSecs); $this->epoch = bcadd($this->secs, $epoch); if (! (bccomp($this->usecs, '1000000') < 0)) { $this->epoch = bcadd('1', $this->epoch); $this->usecs = bcsub($this->usecs, '1000000'); } } else { $this->epoch = $this->secs; } } function preSelect ($prev=null) { if ($prev === false) { return false; } list($uSecs, $epoch) = explode(' ', microtime()); $epDiff = bccomp($epoch, $this->epoch); if ($epDiff == 1) { return false; } $uSecs = bcmul($uSecs, '1000000'); if ($epDiff == 0 && bccomp($uSecs, $this->usecs) >= 0) { return false; } $udiff = bcsub($this->usecs, $uSecs); if (substr($udiff, 0, 1) == '-') { $blockTmSecs = (int) bcsub($this->epoch, $epoch) - 1; $udiff = bcadd($udiff, '1000000'); } else { $blockTmSecs = (int) bcsub($this->epoch, $epoch); } if (is_array($prev) && ($prev[0] < $blockTmSecs || ($prev[0] == $blockTmSecs && $prev[1] < $udiff))) { return $prev; } else { return array($blockTmSecs, $udiff); } } function complete () {} }
1  build/cpf/amqphp/TimeoutExitStrategy.php
View
2  build/cpf/amqphp/persistent/APCPersistenceHelper.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\persistent; class APCPersistenceHelper implements PersistenceHelper { private $data; private $uk; function setUrlKey ($k) { if (is_null($k)) { throw new \Exception("Url key cannot be null", 8260); } $this->uk = $k; } function getData () { return $this->data; } function setData ($data) { $this->data = $data; } private function getKey () { if (is_null($this->uk)) { throw new \Exception("Url key cannot be null", 8261); } return sprintf('apc.amqphp.%s.%s', getmypid(), $this->uk); } function save () { $k = $this->getKey(); return apc_store($k, $this->data); } function load () { $success = false; $this->data = apc_fetch($this->getKey(), $success); return $success; } function destroy () { return apc_delete($this->getKey()); } }
1  build/cpf/amqphp/persistent/APCPersistenceHelper.php
View
2  build/cpf/amqphp/persistent/FilePersistenceHelper.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\persistent; class FilePersistenceHelper implements PersistenceHelper { const TEMP_DIR = '/tmp'; private $data; private $uk; private $tmpDir = self::TEMP_DIR; function setUrlKey ($k) { if (is_null($k)) { throw new \Exception("Url key cannot be null", 8260); } $this->uk = $k; } function setTmpDir ($tmpDir) { $this->tmpDir = $tmpDir; } function getData () { return $this->data; } function setData ($data) { $this->data = $data; } function getTmpFile () { if (is_null($this->uk)) { throw new \Exception("Url key cannot be null", 8261); } return sprintf('%s%sapc.amqphp.%s.%s', $this->tmpDir, DIRECTORY_SEPARATOR, getmypid(), $this->uk); } function save () { return file_put_contents($this->getTmpFile(), (string) $this->data); } function load () { $this->data = file_get_contents($this->getTmpFile()); return ($this->data !== false); } function destroy () { return @unlink($this->getTmpFile()); } }
1  build/cpf/amqphp/persistent/FilePersistenceHelper.php
View
2  build/cpf/amqphp/persistent/PChannel.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\persistent; class PChannel extends \amqphp\Channel implements \Serializable { public $suspendOnSerialize = false; public $resumeOnHydrate = false; private static $PersProps = array('chanId', 'flow', 'frameMax', 'confirmSeqs', 'confirmSeq', 'confirmMode', 'isOpen', 'callbackHandler', 'suspendOnSerialize', 'resumeOnHydrate', 'ackBuffer', 'pendingAcks', 'numPendAcks', 'ackFlag'); function serialize () { $data = array(); foreach (self::$PersProps as $k) { $data[$k] = $this->$k; } $data['consumers'] = array(); foreach ($this->consumers as $cons) { if ($cons[0] instanceof \Serializable && $cons[2] == 'READY') { $data['consumers'][] = $cons; } } return serialize($data); } function unserialize ($data) { $data = unserialize($data); foreach (self::$PersProps as $p) { $this->$p = $data[$p]; } foreach ($data['consumers'] as $i => $c) { $this->consumers[$i] = array($c[0], $c[1], $c[2], $c[3]); } } }
1  build/cpf/amqphp/persistent/PChannel.php
View
2  build/cpf/amqphp/persistent/PConnection.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\persistent; use amqphp\protocol; use amqphp\wire; class PConnection extends \amqphp\Connection implements \Serializable { const SOCK_NEW = 1; const SOCK_REUSED = 2; private static $BasicProps = array('capabilities', 'socketImpl', 'protoImpl', 'socketParams', 'vhost', 'frameMax', 'chanMax', 'signalDispatch', 'nextChan', 'unDelivered', 'unDeliverable', 'incompleteMethods', 'readSrc'); private $pHelper; public $pHelperImpl; private $stateFlag = 0; const ST_CONSTR = 1; const ST_UNSER = 2; const ST_SER = 4; final function __construct (array $params = array()) { $this->stateFlag |= self::ST_CONSTR; if (isset($params['heartbeat']) && $params['heartbeat'] > 0) { throw new \Exception("Persistent connections cannot use a heatbeat", 24803); } if (! array_key_exists('socketImpl', $params)) { $params['socketImpl'] = '\\amqphp\\StreamSocket'; } else if ($params['socketImpl'] != '\\amqphp\\StreamSocket') { throw new \Exception("Persistent connections must use the StreamSocket socket implementation", 24804); } if (! array_key_exists('socketFlags', $params)) { $params['socketFlags'] = array('STREAM_CLIENT_PERSISTENT'); } else if ( ! in_array('STREAM_CLIENT_PERSISTENT', $params['socketFlags'])) { $params['socketFlags'][] = 'STREAM_CLIENT_PERSISTENT'; } parent::__construct($params); } function connect () { if ($this->connected) { trigger_error("PConnection is connected already", E_USER_WARNING); return; } $this->initSocket(); $this->sock->connect(); if ($this->sock->isReusedPSock()) { $this->wakeup(); } else { $this->doConnectionStartup(); if ($ph = $this->getPersistenceHelper()) { $ph->destroy(); } } } function shutdown () { $ph = $this->getPersistenceHelper(); parent::shutdown(); if ($ph) { $ph->destroy(); } } protected function initNewChannel ($impl=null) { $impl = __NAMESPACE__ . "\\PChannel"; return parent::initNewChannel($impl); } private function getPersistenceHelper () { if (! $this->connected) { throw new \Exception("PConnection persistence helper cannot be created before the connection is open", 3789); } else if (! $this->pHelperImpl) { return false; } if (is_null($this->pHelper)) { $c = $this->pHelperImpl; $this->pHelper = new $c; if (! ($this->pHelper instanceof PersistenceHelper)) { throw new \Exception("PConnection persistence helper implementation is invalid", 26934); } $this->pHelper->setUrlKey($this->sock->getCK()); } return $this->pHelper; } function getPersistenceStatus () { if (! $this->connected) { return 0; } else if ($this->sock->isReusedPSock()) { return self::SOCK_REUSED; } else { return self::SOCK_NEW; } } function sleep () { if (! ($ph = $this->getPersistenceHelper())) { throw new \Exception("Failed to load a persistence helper during sleep", 10785); } $ph->setData($this->serialize()); $ph->save(); } function serialize () { $z = $data = array(); foreach ($this->chans as $chan) { if ($chan->suspendOnSerialize && ! $chan->isSuspended()) { $chan->toggleFlow(); } } $z[0] = $this->chans; foreach (self::$BasicProps as $k) { if (in_array($k, array('readSrc', 'incompleteMethods', 'unDelivered', 'unDeliverable')) && $this->$k) { trigger_error("PConnection will persist application data ({$k})", E_USER_WARNING); } $data[$k] = $this->$k; } $z[1] = $data; $this->stateFlag |= self::ST_SER; return serialize($z); } function unserialize ($serialised) { $data = unserialize($serialised); $rewake = false; if ($this->stateFlag & self::ST_UNSER) { throw new \Exception("PConnection is already unserialized", 2886); } else if (! ($this->stateFlag & self::ST_CONSTR)) { $this->__construct(); $rewake = true; } foreach (self::$BasicProps as $k) { $this->$k = $data[1][$k]; } if ($rewake) { $this->initSocket(); $this->sock->connect(); if (! $this->sock->isReusedPSock()) { throw new \Exception("Persisted connection woken up with a fresh socket connection", 9249); } foreach (self::$BasicProps as $k) { if ($k == 'vhost' && $data[1][$k] != $this->sock->getVHost()) { throw new \Exception("Persisted connection woken up as different VHost", 9250); } } $this->connected = true; } if (isset($data[0])) { $this->chans = $data[0]; foreach ($this->chans as $chan) { $chan->setConnection($this); } } $this->stateFlag |= self::ST_UNSER; foreach ($this->chans as $chan) { if ($chan->resumeOnHydrate && $chan->isSuspended()) { $chan->toggleFlow(); } } } private function wakeup () { $this->connected = true; if (! ($ph = $this->getPersistenceHelper())) { throw new \Exception("Failed to load persistence helper during wakeup", 1798); } if (! $ph->load()) { try { $e = null; $this->shutdown(); } catch (\Exception $e) { } throw new \Exception('Failed to reload amqp connection cache during wakeup', 8543, $e); } $this->unserialize($ph->getData()); } }
1  build/cpf/amqphp/persistent/PConnection.php
View
2  build/cpf/amqphp/persistent/PersistenceHelper.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\persistent; interface PersistenceHelper { function setUrlKey ($k); function getData (); function setData ($data); function save (); function load (); function destroy (); }
1  build/cpf/amqphp/persistent/PersistenceHelper.php
View
2  build/cpf/amqphp/protocol/abstrakt/ClassFactory.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\protocol\abstrakt; abstract class ClassFactory { protected static $Cache; final static function GetClassByIndex ($index) { foreach (static::$Cache as $cNum => $c) { if ($c[0] === $index) { return is_string($c[2]) ? (static::$Cache[$cNum][2] = new $c[2]) : $c[2]; } } } final static function GetClassByName ($name) { foreach (static::$Cache as $cNum => $c) { if ($c[1] === $name) { return is_string($c[2]) ? (static::$Cache[$cNum][2] = new $c[2]) : $c[2]; } } } final static function GetMethod ($c, $m) { $c = is_int($c) || is_numeric($c) ? self::GetClassByIndex($c) : self::GetClassByName($c); if ($c) { $m = is_int($m) || is_numeric($m) ? $c->getMethodByIndex($m) : $c->getMethodByName($m); if ($m) { return $m; } } } }
1  build/cpf/amqphp/protocol/abstrakt/ClassFactory.php
View
2  build/cpf/amqphp/protocol/abstrakt/DomainFactory.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\protocol\abstrakt; abstract class DomainFactory { protected static $Cache; final static function IsDomain ($dName) { return isset(static::$Cache[$dName]); } final static function GetDomain ($dName) { if (isset(static::$Cache[$dName])) { return (is_string(static::$Cache[$dName])) ? (static::$Cache[$dName] = new static::$Cache[$dName]) : static::$Cache[$dName]; } } final static function Validate ($val, $dName) { return static::GetDomain($dName)->validate($val); } }
1  build/cpf/amqphp/protocol/abstrakt/DomainFactory.php
View
2  build/cpf/amqphp/protocol/abstrakt/FieldFactory.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\protocol\abstrakt; abstract class FieldFactory { protected static $Cache; private static function Lookup ($fName, $mName = '') { foreach (static::$Cache as $i => $f) { if ($f[0] === $fName && $f[1] === $mName) { return $i; } } return false; } final static function IsField ($fName, $mName = '') { return (static::Lookup($fName, $mName) !== false); } final static function GetField ($fName, $mName = '') { if (false !== ($f = static::Lookup($fName, $mName))) { return is_string(static::$Cache[$f][2]) ? (static::$Cache[$f][2] = new static::$Cache[$f][2]) : static::$Cache[$f][2]; } } final static function GetClassFields () { $r = array(); foreach (static::$Cache as $f) { if ($f[1] === '') { $r[] = static::GetField($f[0]); } } return $r; } final static function GetFieldsForMethod ($mName) { $r = array(); foreach (static::$Cache as $f) { if ($f[1] === $mName) { $r[] = static::GetField($f[0], $mName); } } return $r; } final static function Validate ($val, $fName, $mName = '') { return static::GetField($fName, $mName)->validate($val); } }
1  build/cpf/amqphp/protocol/abstrakt/FieldFactory.php
View
2  build/cpf/amqphp/protocol/abstrakt/MethodFactory.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\protocol\abstrakt; abstract class MethodFactory { protected static $Cache; private static function Lookup ($mName, $asName = true) { $j = ($asName) ? 1 : 0; foreach (static::$Cache as $i => $f) { if ($f[$j] === $mName) { return $i; } } return false; } final static function GetMethodByName ($mName) { if (false !== ($i = static::Lookup($mName))) { return (is_string(static::$Cache[$i][2])) ? (static::$Cache[$i][2] = new static::$Cache[$i][2]) : static::$Cache[$i][2]; } } final static function GetMethodsByName (array $restrict = array()) { $m = array(); foreach (static::$Cache as $c) { if (! $restrict || in_array($c[1], $restrict)) { $m[] = static::GetMethodByName($c[1]); } } return $m; } final static function GetMethodByIndex ($idx) { if (false !== ($i = static::Lookup($idx, false))) { return (is_string(static::$Cache[$i][2])) ? (static::$Cache[$i][2] = new static::$Cache[$i][2]) : static::$Cache[$i][2]; } } final static function GetMethodsByIndex (array $restrict = array()) { $m = array(); foreach (static::$Cache as $c) { if (! $restrict || in_array($c[0], $restrict)) { $m[] = static::GetMethodByIndex($c[0]); } } return $m; } }
1  build/cpf/amqphp/protocol/abstrakt/MethodFactory.php
View
2  build/cpf/amqphp/protocol/abstrakt/XmlSpecClass.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\protocol\abstrakt; abstract class XmlSpecClass { protected $name; protected $index; protected $fields; protected $methods; protected $methFact; protected $fieldFact; final function getSpecName () { return $this->name; } final function getSpecIndex () { return $this->index; } final function getSpecFields () { return $this->fields; } final function getSpecMethods () { return $this->methods; } final function getMethods () { return call_user_func(array($this->methFact, 'GetMethodsByName'), $this->methods); } final function getMethodByName ($mName) { if (in_array($mName, $this->methods)) { return call_user_func(array($this->methFact, 'GetMethodByName'), $mName); } } final function getMethodByIndex ($idx) { if (in_array($idx, array_keys($this->methods))) { return call_user_func(array($this->methFact, 'GetMethodByIndex'), $idx); } } final function getFields () { return call_user_func(array($this->fieldFact, 'GetClassFields'), $this->name); } final function getFieldByName ($fName) { if (in_array($fName, $this->fields)) { return call_user_func(array($this->fieldFact, 'GetField'), $fName); } } }
1  build/cpf/amqphp/protocol/abstrakt/XmlSpecClass.php
View
2  build/cpf/amqphp/protocol/abstrakt/XmlSpecDomain.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\protocol\abstrakt; abstract class XmlSpecDomain { protected $name; protected $protocolType; final function getSpecDomainName () { return $this->name; } final function getSpecDomainType () { return $this->protocolType; } function validate($subject) { return true; } }
1  build/cpf/amqphp/protocol/abstrakt/XmlSpecDomain.php
View
2  build/cpf/amqphp/protocol/abstrakt/XmlSpecField.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\protocol\abstrakt; interface XmlSpecField { function getSpecFieldName (); function getSpecFieldDomain (); }
1  build/cpf/amqphp/protocol/abstrakt/XmlSpecField.php
View
2  build/cpf/amqphp/protocol/abstrakt/XmlSpecMethod.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\protocol\abstrakt; abstract class XmlSpecMethod { protected $class; protected $name; protected $index; protected $synchronous; protected $responseMethods; protected $fields; protected $methFact; protected $fieldFact; protected $classFact; protected $content; final function getSpecClass () { return $this->class; } final function getSpecName () { return $this->name; } final function getSpecIndex () { return $this->index; } final function getSpecIsSynchronous () { return $this->synchronous; } final function getSpecResponseMethods () { return $this->responseMethods; } final function getSpecFields () { return $this->fields; } final function getSpecHasContent () { return $this->content; } final function getFields () { return call_user_func(array($this->fieldFact, 'GetFieldsForMethod'), $this->name); } final function getField ($fName) { if (in_array($fName, $this->fields)) { return call_user_func(array($this->fieldFact, 'GetField'), $fName, $this->name); } } final function getResponses () { return call_user_func(array($this->methFact, 'GetMethodsByName'), $this->responseMethods); } final function getClass () { return call_user_func(array($this->classFact, 'GetClassByName'), $this->class); } final function hasNoWaitField () { return $this->hasNoWait; } }
1  build/cpf/amqphp/protocol/abstrakt/XmlSpecMethod.php
View
2  build/cpf/amqphp/wire/Decimal.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\wire; use amqphp\protocol as proto; use amqphp\protocol\abstrakt; class Decimal { const BC_SCALE_DEFAULT = 8; private $unscaled; private $scale; private $bcScale = self::BC_SCALE_DEFAULT; function __construct($unscaled, $scale=false) { if ($scale !== false) { if ($scale < 0 || $scale > 255) { throw new \Exception("Scale out of range", 9876); } $this->unscaled = (string) $unscaled; $this->scale = (string) $scale; } else if (is_float($unscaled)) { list($whole, $frac) = explode('.', (string) $unscaled); $frac = rtrim($frac, '0'); $this->unscaled = $whole . $frac; $this->scale = strlen($frac); } else if (is_int($unscaled)) { $this->unscaled = $unscaled; $this->scale = 0; } else { throw new \Exception("Unable to construct a decimal", 48943); } if ($this->scale > 255) { throw new \Exception("Decimal scale is out of range", 7843); } } function getUnscaled() { return $this->unscaled; } function getScale() { return $this->scale; } function setBcScale($i) { $this->bcScale = (int) $i; } function toBcString() { return bcdiv($this->unscaled, bcpow('10', $this->scale, $this->bcScale), $this->bcScale); } function toFloat() { return (float) $this->toBcString(); } function __toString() { return $this->toBcString(); } }
1  build/cpf/amqphp/wire/Decimal.php
View
2  build/cpf/amqphp/wire/Hexdump.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\wire; final class Hexdump { final static function hexdump ($buff) { static $f, $f2; if ($buff === '') { return "00000000\n"; } if (is_null($f)) { $f = function ($char) { return sprintf('%02s', dechex(ord($char))); }; $f2 = function ($char) { $ord = ord($char); return ($ord > 31 && $ord < 127) ? chr($ord) : '.'; }; } $l = strlen($buff); $ret = ''; for ($i = 0; $i < $l; $i += 16) { $line = substr($buff, $i, 8); $ll = $offLen = strlen($line); $rem = (8 - $ll) * 3; $hexes = vsprintf(str_repeat('%3s', $ll), array_map($f, str_split($line, 1))); $chars = '|' . vsprintf(str_repeat('%s', $ll), array_map($f2, str_split($line, 1))); $lBuff = sprintf("%08s %s", dechex($i), $hexes); if ($line = substr($buff, $i + 8, 8)) { $ll = strlen($line); $offLen += $ll; $rem = (8 - $ll) * 3 + 1; $hexes = vsprintf(str_repeat('%3s', $ll), array_map($f, str_split($line, 1))); $chars .= ' '. vsprintf(str_repeat('%s', $ll), array_map($f2, str_split($line, 1))); $lBuff .= sprintf(" %s%{$rem}s %s|\n", $hexes, ' ', $chars); } else { $lBuff .= ' ' . str_repeat(" ", $rem + 26) . $chars . "|\n"; } $ret .= $lBuff; } return sprintf("%s%08s\n", $ret, dechex($l)); } }
1  build/cpf/amqphp/wire/Hexdump.php
View
2  build/cpf/amqphp/wire/Method.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\wire; use amqphp\protocol as proto; use amqphp\protocol\abstrakt; class Method implements \Serializable { const ST_METH_READ = 1; const ST_CHEAD_READ = 2; const ST_BODY_READ = 4; const PARTIAL_FRAME = 7; private static $PlainPFields = array('rcState', 'mode', 'fields', 'classFields', 'content', 'frameSize', 'wireChannel', 'isHb', 'wireMethodId', 'wireClassId', 'contentSize'); private $rcState = 0; private $methProto; private $classProto; private $mode; private $fields = array(); private $classFields = array(); private $content; private $frameSize; private $wireChannel = null; private $wireMethodId; private $wireClassId; private $contentSize; private $isHb = false; private $protoLoader; public $amqpClass; function serialize () { if ($this->mode != 'read') { trigger_error("Only read mode methods should be serialised", E_USER_WARNING); return null; } $ret = array(); $ret['plainFields'] = array(); foreach (self::$PlainPFields as $k) { $ret['plainFields'][$k] = $this->$k; } if ($this->methProto && $this->classProto) { $ret['protos'] = array(get_class($this->methProto), get_class($this->classProto)); } return serialize($ret); } function unserialize ($s) { $state = unserialize($s); foreach (self::$PlainPFields as $k) { $this->$k = $state['plainFields'][$k]; } if (array_key_exists('protos', $state)) { list($mc, $cc) = $state['protos']; $this->methProto = new $mc; $this->classProto = new $cc; $this->amqpClass = sprintf('%s.%s', $this->classProto->getSpecName(), $this->methProto->getSpecName()); } } function setProtocolLoader ($l) { $this->protoLoader = $l; } function __construct (abstrakt\XmlSpecMethod $src = null, $chan = 0) { if ($src instanceof abstrakt\XmlSpecMethod) { $this->methProto = $src; $this->classProto = $this->methProto->getClass(); $this->mode = 'write'; $this->wireChannel = $chan; $this->amqpClass = sprintf('%s.%s', $this->classProto->getSpecName(), $this->methProto->getSpecName()); } else { $this->mode = 'read'; } } function canReadFrom (Reader $src) { if (is_null($this->wireChannel)) { return true; } if (true === ($_fh = $this->extractFrameHeader($src))) { return false; } list($wireType, $wireChannel, $wireSize) = $_fh; $ret = ($wireChannel == $this->wireChannel); $src->rewind(7); return $ret; } function readConstruct (Reader $src, \Closure $protoLoader) { if ($this->mode == 'write') { trigger_error('Invalid read construct operation on a read mode method', E_USER_WARNING); return false; } $FRME = 206; $break = false; $ret = true; $this->protoLoader = $protoLoader; while (! $src->isSpent()) { if (true === ($_fh = $this->extractFrameHeader($src))) { $ret = self::PARTIAL_FRAME; break; } else { list($wireType, $wireChannel, $wireSize) = $_fh; } if (! $this->wireChannel) { $this->wireChannel = $wireChannel; } else if ($this->wireChannel != $wireChannel) { $src->rewind(7); return true; } if ($src->isSpent($wireSize + 1)) { $src->rewind(7); $ret = self::PARTIAL_FRAME; break; } switch ($wireType) { case 1: $this->readMethodContent($src, $wireSize); if (! $this->methProto->getSpecHasContent()) { $break = true; } break; case 2: $this->readContentHeaderContent($src, $wireSize); break; case 3: $this->readBodyContent($src, $wireSize); if ($this->readConstructComplete()) { $break = true; } break; case 8: $break = $ret = $this->isHb = true; break; default: throw new \Exception(sprintf("Unsupported frame type %d", $wireType), 8674); } if ($src->read('octet') != $FRME) { throw new \Exception(sprintf("Framing exception - missed frame end (%s) - (%d,%d,%d,%d) [%d, %d]", $this->amqpClass, $this->rcState, $break, $src->isSpent(), $this->readConstructComplete(), strlen($this->content), $this->contentSize ), 8763); } if ($break) { break; } } return $ret; } private function extractFrameHeader(Reader $src) { if ($src->isSpent(7)) { return true; } if (null === ($wireType = $src->read('octet'))) { throw new \Exception('Failed to read type from frame', 875); } else if (null === ($wireChannel = $src->read('short'))) { throw new \Exception('Failed to read channel from frame', 9874); } else if (null === ($wireSize = $src->read('long'))) { throw new \Exception('Failed to read size from frame', 8715); } return array($wireType, $wireChannel, $wireSize); } private function readMethodContent (Reader $src, $wireSize) { $st = $src->p; $this->wireClassId = $src->read('short'); $this->wireMethodId = $src->read('short'); $protoLoader = $this->protoLoader; if (! ($this->classProto = $protoLoader('ClassFactory', 'GetClassByIndex', array($this->wireClassId)))) { throw new \Exception(sprintf("Failed to construct class prototype for class ID %s", $this->wireClassId), 9875); } else if (! ($this->methProto = $this->classProto->getMethodByIndex($this->wireMethodId))) { throw new \Exception("Failed to construct method prototype", 5645); } $this->amqpClass = sprintf('%s.%s', $this->classProto->getSpecName(), $this->methProto->getSpecName()); foreach ($this->methProto->getFields() as $f) { $this->fields[$f->getSpecFieldName()] = $src->read($f->getSpecDomainType()); } $en = $src->p; if ($wireSize != ($en - $st)) { throw new \Exception("Invalid method frame size", 9845); } $this->rcState = $this->rcState | self::ST_METH_READ; } private function readContentHeaderContent (Reader $src, $wireSize) { $st = $src->p; $wireClassId = $src->read('short'); $src->read('short'); $this->contentSize = $src->read('longlong'); if ($wireClassId != $this->wireClassId) { throw new \Exception(sprintf("Unexpected class in content header (%d, %d) - read state %d", $wireClassId, $this->wireClassId, $this->rcState), 5434); } $binFlags = ''; while (true) { if (null === ($fBlock = $src->read('short'))) { throw new \Exception("Failed to read property flag block", 4548); } $binFlags .= str_pad(decbin($fBlock), 16, '0', STR_PAD_LEFT); if (0 !== (strlen($binFlags) % 16)) { throw new \Exception("Unexpected message property flags", 8740); } if (substr($binFlags, -1) == '1') { $binFlags = substr($binFlags, 0, -1); } else { break; } } foreach ($this->classProto->getFields() as $i => $f) { if ($f->getSpecFieldDomain() == 'bit') { $this->classFields[$f->getSpecFieldName()] = (boolean) substr($binFlags, $i, 1); } else if (substr($binFlags, $i, 1) == '1') { $this->classFields[$f->getSpecFieldName()] = $src->read($f->getSpecFieldDomain()); } else { $this->classFields[$f->getSpecFieldName()] = null; } } $en = $src->p; if ($wireSize != ($en - $st)) { throw new \Exception("Invalid content header frame size", 2546); } $this->rcState = $this->rcState | self::ST_CHEAD_READ; } private function readBodyContent (Reader $src, $wireSize) { $this->content .= $src->readN($wireSize); $this->rcState = $this->rcState | self::ST_BODY_READ; } function readConstructComplete () { if ($this->isHb) { return true; } else if (! $this->methProto) { return false; } else if (! $this->methProto->getSpecHasContent()) { return (boolean) $this->rcState & self::ST_METH_READ; } else { return ($this->rcState & self::ST_CHEAD_READ) && (strlen($this->content) >= $this->contentSize); } } function setField ($name, $val) { if ($this->mode == 'read') { trigger_error('Setting field value for read constructed method', E_USER_WARNING); } else if (in_array($name, $this->methProto->getSpecFields())) { $this->fields[$name] = $val; } else if (in_array($name, $this->classProto->getSpecFields())) { $this->classFields[$name] = $val; } else { $warns = sprintf("Field %s is invalid for Amqp message type %s", $name, $this->amqpClass); trigger_error($warns, E_USER_WARNING); } } function getField ($name) { if (array_key_exists($name, $this->fields)) { return $this->fields[$name]; } else if (array_key_exists($name, $this->classFields)) { return $this->classFields[$name]; } else if (! in_array($name, array_merge($this->classProto->getSpecFields(), $this->methProto->getSpecFields()))) { $warns = sprintf("Field %s is invalid for Amqp message type %s", $name, $this->amqpClass); trigger_error($warns, E_USER_WARNING); } } function getFields () { return array_merge($this->classFields, $this->fields); } function setContent ($content) { if ($this->mode == 'read') { trigger_error('Setting content value for read constructed method', E_USER_WARNING); } else if (strlen($content)) { if (! $this->methProto->getSpecHasContent()) { trigger_error('Setting content value for a method which doesn\'t take content', E_USER_WARNING); } $this->content = $content; } } function getContent () { if (! $this->methProto->getSpecHasContent()) { trigger_error('Invalid serialize operation on a method which doesn\'t take content', E_USER_WARNING); return ''; } return $this->content; } function getMethodProto () { return $this->methProto; } function getClassProto () { return $this->classProto; } function getWireChannel () { return $this->wireChannel; } function getWireSize () { return $this->wireSize; } function getWireClassId () { return $this->wireClassId; } function getWireMethodId () { return $this->wireMethodId; } function setMaxFrameSize ($max) { $this->frameSize = $max; } function setWireChannel ($chan) { $this->wireChannel = $chan; } function isHeartbeat () { return $this->isHb; } function toBin (\Closure $protoLoader) { if ($this->mode == 'read') { trigger_error('Invalid serialize operation on a read mode method', E_USER_WARNING); return ''; } $frme = $protoLoader('ProtoConsts', 'GetConstant', array('FRAME_END')); $w = new Writer; $tmp = $this->getMethodBin(); $w->write(1, 'octet'); $w->write($this->wireChannel, 'short'); $w->write(strlen($tmp), 'long'); $buff = $w->getBuffer() . $tmp . $frme; $ret = array($buff); if ($this->methProto->getSpecHasContent()) { $w = new Writer; $tmp = $this->getContentHeaderBin(); $w->write(2, 'octet'); $w->write($this->wireChannel, 'short'); $w->write(strlen($tmp), 'long'); $ret[] = $w->getBuffer() . $tmp . $frme; $tmp = (string) $this->content; $i = 0; $frameSize = $this->frameSize - 8; while (true) { $chunk = substr($tmp, ($i * $frameSize), $frameSize); if (strlen($chunk) == 0) { break; } $w = new Writer; $w->write(3, 'octet'); $w->write($this->wireChannel, 'short'); $w->write(strlen($chunk), 'long'); $ret[] = $w->getBuffer() . $chunk . $frme; $i++; } } return $ret; } private function getMethodBin () { if ($this->mode == 'read') { trigger_error('Invalid serialize operation on a read mode method', E_USER_WARNING); return ''; } $src = new Writer; $src->write($this->classProto->getSpecIndex(), 'short'); $src->write($this->methProto->getSpecIndex(), 'short'); foreach ($this->methProto->getFields() as $f) { $name = $f->getSpecFieldName(); $type = $f->getSpecDomainType(); $val = ''; if (array_key_exists($name, $this->fields)) { $val = $this->fields[$name]; if (! $f->validate($val)) { $warns = sprintf("Field %s of method %s failed validation by protocol binding class %s", $name, $this->amqpClass, get_class($f)); trigger_error($warns, E_USER_WARNING); } } $src->write($val, $type); } return $src->getBuffer(); } private function getContentHeaderBin () { if ($this->mode == 'read') { trigger_error('Invalid serialize operation on a read mode method', E_USER_WARNING); return ''; } else if (! $this->methProto->getSpecHasContent()) { trigger_error('Invalid serialize operation on a method which doesn\'t take content', E_USER_WARNING); return ''; } $src = new Writer; $src->write($this->classProto->getSpecIndex(), 'short'); $src->write(0, 'short'); $src->write(strlen($this->content), 'longlong'); $pFlags = ''; $pChunks = 0; $pList = ''; $src2 = new Writer; foreach ($this->classProto->getFields() as $i => $f) { if (($i % 15) == 0) { if ($i > 0) { $pFlags .= '1'; } $pChunks++; } $fName = $f->getSpecFieldName(); $dName = $f->getSpecFieldDomain(); if (array_key_exists($fName, $this->classFields) && ! ($dName == 'bit' && ! $this->classFields[$fName])) { $pFlags .= '1'; } else { $pFlags .= '0'; } if (array_key_exists($fName, $this->classFields) && $dName != 'bit') { if (! $f->validate($this->classFields[$fName])) { trigger_error("Field {$fName} of method {$this->amqpClass} is not valid", E_USER_WARNING); } $src2->write($this->classFields[$fName], $f->getSpecDomainType()); } } if ($pFlags && (strlen($pFlags) % 16) !== 0) { $pFlags .= str_repeat('0', 16 - (strlen($pFlags) % 16)); } $pBuff = ''; for ($i = 0; $i < $pChunks; $i++) { $pBuff .= pack('n', bindec(substr($pFlags, $i*16, 16))); } return $src->getBuffer() . $pBuff . $src2->getBuffer(); } function isResponse (Method $other) { if ($exp = $this->methProto->getSpecResponseMethods()) { if ($this->classProto->getSpecName() != $other->classProto->getSpecName() || $this->wireChannel != $other->wireChannel) { return false; } else { return in_array($other->methProto->getSpecName(), $exp); } } else { trigger_error("Method does not expect a response", E_USER_WARNING); return false; } } }
1  build/cpf/amqphp/wire/Method.php
View
2  build/cpf/amqphp/wire/Protocol.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\wire; use amqphp\protocol as proto; use amqphp\protocol\abstrakt; abstract class Protocol { private static $Versions = array('0.9.1'); private static $ImplTypes = array('Table', 'Boolean', 'ShortShortInt', 'ShortShortUInt', 'ShortInt', 'ShortUInt', 'LongInt', 'LongUInt', 'LongLongInt', 'LongLongUInt', 'Float', 'Double', 'DecimalValue', 'ShortString', 'LongString', 'FieldArray', 'Timestamp'); private static $XmlTypesMap = array('bit' => 'Boolean', 'octet' => 'ShortShortUInt', 'short' => 'ShortUInt', 'long' => 'LongUInt', 'longlong' => 'LongLongUInt', 'shortstr' => 'ShortString', 'longstr' => 'LongString', 'timestamp' => 'LongLongUInt', 'table' => 'Table'); private static $AmqpTableMap = array('t' => 'ShortShortUInt', 'b' => 'ShortShortInt', 'B' => 'ShortShortUInt', 'U' => 'ShortInt', 'u' => 'ShortUInt', 'I' => 'LongInt', 'i' => 'LongUInt', 'L' => 'LongLongInt', 'l' => 'LongLongUInt', 'f' => 'Float', 'd' => 'Double', 'D' => 'DecimalValue', 's' => 'ShortString', 'S' => 'LongString', 'A' => 'FieldArray', 'T' => 'LongLongUInt', 'F' => 'Table'); protected $bin; static function GetXmlTypes () { return self::$XmlTypesMap; } protected function getImplForXmlType($t) { return isset(self::$XmlTypesMap[$t]) ? self::$XmlTypesMap[$t] : null; } protected function getImplForTableType($t) { return isset(self::$AmqpTableMap[$t]) ? self::$AmqpTableMap[$t] : null; } protected function getTableTypeForValue($val) { if (is_bool($val)) { return 't'; } else if (is_int($val)) { if ($val > 0) { if ($val < 256) { return 'B'; } else if ($val < 65536) { return 'u'; } else if ($val < 4294967296) { return 'i'; } else { return 'l'; } } else if ($val < 0) { $val = abs($val); if ($val < 256) { return 'b'; } else if ($val < 65536) { return 'U'; } else if ($val < 4294967296) { return 'I'; } else { return 'L'; } } else { return 'B'; } } else if (is_float($val)) { return 'f'; } else if (is_string($val)) { return 'S'; } else if (is_array($val)) { $isArray = false; foreach (array_keys($val) as $k) { if (is_int($k)) { $isArray = true; break; } } return $isArray ? 'A' : 'F'; } else if ($val instanceof Decimal) { return 'D'; } return null; } protected function getXmlTypeForValue($val) { if (is_bool($val)) { return 'bit'; } else if (is_int($val)) { $val = abs($val); if ($val < 256) { return 'octet'; } else if ($val < 65536) { return 'short'; } else if ($val < 4294967296) { return 'long'; } else { return 'longlong'; } } else if (is_string($val)) { return (strlen($val) < 255) ? 'shortstr' : 'longstr'; } else if (is_array($val) || $val instanceof Table) { return 'table'; } return null; } function getBuffer() { return $this->bin; } }
1  build/cpf/amqphp/wire/Protocol.php
View
2  build/cpf/amqphp/wire/Reader.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\wire; use amqphp\protocol as proto; use amqphp\protocol\abstrakt; class Reader extends Protocol { public $p = 0; public $binPackOffset = 0; public $binBuffer; public $binLen = 0; function __construct ($bin) { $this->bin = $bin; $this->binLen = strlen($bin); } function isSpent ($n = false) { $n = ($n === false) ? 0 : $n - 1; return ($this->p + $n >= $this->binLen); } function bytesRemaining () { return $this->binLen - $this->p; } function getRemainingBuffer () { $r = substr($this->bin, $this->p); $this->p = $this->binLen - 1; return $r; } function append ($bin) { $this->bin .= $bin; $this->binLen += strlen($bin); } function rewind ($n) { $this->p -= $n; } function readN ($n) { $ret = substr($this->bin, $this->p, $n); $this->p += strlen($ret); return $ret; } function read ($type, $tableField=false) { $implType = ($tableField) ? $this->getImplForTableType($type) : $this->getImplForXmlType($type); if (! $implType) { trigger_error("Warning: no type mapping found for input type or value - nothing read", E_USER_WARNING); return; } $r = $this->{"read$implType"}(); if ($implType === 'Boolean') { if ($this->binPackOffset++ > 6) { $this->binPackOffset = 0; } } else { $this->binPackOffset = 0; } return $r; } private function readTable () { $tLen = $this->readLongUInt(); $tEnd = $this->p + $tLen; $t = new Table; while ($this->p < $tEnd) { $fName = $this->readShortString(); $fType = chr($this->readShortShortUInt()); $t[$fName] = new TableField($this->read($fType, true), $fType); } return $t; } private function readBoolean () { if ($this->binPackOffset == 0) { $tmp = unpack('C', substr($this->bin, $this->p++, 1)); $this->binBuffer = reset($tmp); } return ($this->binBuffer & (1 << $this->binPackOffset)) ? 1 : 0; } private function readShortShortInt () { $tmp = unpack('c', substr($this->bin, $this->p++, 1)); $i = reset($tmp); return $i; } private function readShortShortUInt () { $tmp = unpack('C', substr($this->bin, $this->p++, 1)); $i = reset($tmp); return $i; } private function readShortInt () { $tmp = unpack('s', substr($this->bin, $this->p, 2)); $i = reset($tmp); $this->p += 2; return $i; } private function readShortUInt () { $tmp = unpack('n', substr($this->bin, $this->p, 2)); $i = reset($tmp); $this->p += 2; return $i; } private function readLongInt () { $tmp = unpack('L', substr($this->bin, $this->p, 4)); $i = reset($tmp); $this->p += 4; return $i; } private function readLongUInt () { $tmp = unpack('N', substr($this->bin, $this->p, 4)); $i = reset($tmp); $this->p += 4; return $i; } private function readLongLongInt () { trigger_error("Unimplemented read method %s", __METHOD__); } private function readLongLongUInt () { $byte = substr($this->bin, $this->p++, 1); $ret = ord($byte); for ($i = 1; $i < 8; $i++) { $ret = ($ret << 8) + ord(substr($this->bin, $this->p++, 1)); } return $ret; } private function readFloat () { $tmp = unpack('f', substr($this->bin, $this->p, 4)); $i = reset($tmp); $this->p += 4; return $i; } private function readDouble () { $tmp = unpack('d', substr($this->bin, $this->p, 8)); $i = reset($tmp); $this->p += 8; return $i; } private function readDecimalValue () { $scale = $this->readShortShortUInt(); $unscaled = $this->readLongUInt(); return new Decimal($unscaled, $scale); } private function readShortString () { $l = $this->readShortShortUInt(); $s = substr($this->bin, $this->p, $l); $this->p += $l; return $s; } private function readLongString () { $l = $this->readLongUInt(); $s = substr($this->bin, $this->p, $l); $this->p += $l; return $s; } private function readFieldArray () { $aLen = $this->readLongUInt(); $aEnd = $this->p + $aLen; $a = array(); while ($this->p < $aEnd) { $t = chr($this->readShortShortUInt()); $a[] = $this->read($t, true); } return $a; } }
1  build/cpf/amqphp/wire/Reader.php
View
2  build/cpf/amqphp/wire/Table.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\wire; use amqphp\protocol as proto; use amqphp\protocol\abstrakt; class Table implements \ArrayAccess, \Iterator { const ITER_MODE_SIMPLE = 1; const ITER_MODE_TYPED = 2; private $data = array(); private $iterMode = self::ITER_MODE_SIMPLE; private $iterP = 0; private $iterK; static function IsValidKey($k) { return is_string($k) && $k && (strlen($k) < 129) && preg_match("/[a-zA-Z\$\#][a-zA-Z-_\$\#]*/", $k); } function __construct(array $data = array()) { foreach ($data as $name => $av) { $this->offsetSet($name, $av); } } function offsetExists($k) { return array_key_exists($k, $this->data); } function offsetGet($k) { if (! $this->offsetExists($k)) { trigger_error(sprintf("Offset not found [0]: %s", $k), E_USER_WARNING); return null; } return $this->data[$k]; } function offsetSet($k, $v) { if ( ! self::IsValidKey($k)) { throw new \Exception("Invalid table key", 7255); } else if (! ($v instanceof TableField)) { $v = new TableField($v); } $this->data[$k] = $v; } function offsetUnset($k) { if (! isset($this->data[$k])) { trigger_error(sprintf("Offset not found [1]: %s", $k), E_USER_WARNING); } else { unset($this->data[$n]); } } function getArrayCopy() { $ac = array(); foreach ($this->data as $k => $v) { $ac[$k] = $v->getValue(); } return $ac; } function rewind() { $this->iterP = 0; $this->iterK = array_keys($this->data); } function current() { return $this->data[$this->iterK[$this->iterP]]; } function key() { return $this->iterK[$this->iterP]; } function next() { $this->iterP++; } function valid() { return isset($this->iterK[$this->iterP]); } }
1  build/cpf/amqphp/wire/Table.php
View
2  build/cpf/amqphp/wire/TableField.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\wire; use amqphp\protocol as proto; use amqphp\protocol\abstrakt; class TableField extends Protocol { protected $val; protected $type; function __construct($val, $type=false) { $this->val = $val; $this->type = ($type === false) ? $this->getTableTypeForValue($val) : $type; } function getValue() { return $this->val; } function setValue($val) { $this->val = $val; } function getType() { return $this->type; } function __toString() { return (string) $this->val; } }
1  build/cpf/amqphp/wire/TableField.php
View
2  build/cpf/amqphp/wire/Writer.php
View
@@ -1,2 +0,0 @@
-<?php
- namespace amqphp\wire; use amqphp\protocol as proto; use amqphp\protocol\abstrakt; class Writer extends Protocol { private $binPackOffset = 0; function write ($value, $type, $tableField=false) { $implType = ($tableField) ? $this->getImplForTableType($type) : $this->getImplForXmlType($type); if (! $implType) { trigger_error(sprintf("Warning: Unknown Amqp type: %s", $type), E_USER_WARNING); $implType = ($tableField) ? $this->getTableTypeForValue($value) : $this->getXmlTypeForValue($value); if (! $implType) { trigger_error("Warning: no type mapping found for input type or value - nothing written", E_USER_WARNING); return; } } $r = $this->{"write$implType"}($value); if ($implType === 'Boolean') { $this->binPackOffset++; } else { $this->binPackOffset = 0; } } private function writeTable ($val) { if (is_array($val)) { $val = new Table($val); } else if (! ($val instanceof Table)) { $val = array(); } $w = new Writer; foreach ($val as $fName => $field) { $w->writeShortString($fName); $w->writeShortShortUInt(ord($field->getType())); $w->write($field->getValue(), $field->getType(), true); } $this->bin .= pack('N', strlen($w->bin)) . $w->bin; } private function writeFieldArray (array $arr) { $p = strlen($this->bin); foreach ($arr as $item) { if (! ($item instanceof TableField)) { $item = new TableField($item); } $this->writeShortShortUInt(ord($item->getType())); $this->write($item->getValue(), $item->getType(), true); } $p2 = strlen($this->bin); $binSav = $this->bin; $this->bin = ''; $this->writeLongUInt($p2 - $p); $binLen = $this->bin; $this->bin = substr($binSav, 0, $p) . $binLen . substr($binSav, $p); } private function writeBoolean ($val) { if ($this->binPackOffset == 0) { if ($val) { $this->bin .= pack('C', 1); } else { $this->bin .= pack('C', 0); } } else { $tmp = unpack('C', substr($this->bin, -1)); $b = reset($tmp); if ($val) { $b += pow(2, $this->binPackOffset); } if ($this->binPackOffset > 6) { $this->binPackOffset = -1; } $this->bin = substr($this->bin, 0, -1) . pack('C', $b); } } private function writeShortShortInt ($val) { $this->bin .= pack('c', (int) $val); } private function writeShortShortUInt ($val) { $this->bin .= pack('C', (int) $val); } private function writeShortInt ($val) { $this->bin .= pack('s', (int) $val); } private function writeShortUInt ($val) { $this->bin .= pack('n', (int) $val); } private function writeLongInt ($val) { $this->bin .= pack('L', (int) $val); } private function writeLongUInt ($val) { $this->bin .= pack('N', (int) $val); } private function writeLongLongInt ($val) { error("Unimplemented *write* method %s", __METHOD__); } private function writeLongLongUInt ($val) { $tmp = array(); for ($i = 0; $i < 8; $i++) { $tmp[] = $val & 255; $val = ($val >> 8); } foreach (array_reverse($tmp) as $octet) { $this->bin .= chr($octet); } } private function writeFloat ($val) { $this->bin .= pack('f', (float) $val); } private function writeDouble ($val) { $this->bin .= pack('d', (float) $val); } private function writeDecimalValue ($val) { if (! ($val instanceof Decimal)) { $val = new Decimal($val); } $this->writeShortShortUInt($val->getScale()); $this->writeLongUInt($val->getUnscaled()); } private function writeShortString ($val) { $this->writeShortShortUInt(strlen($val)); $this->bin .= $val; } private function writeLongString ($val) { $this->writeLongUInt(strlen($val)); $this->bin .= $val; } }
1  build/cpf/amqphp/wire/Writer.php
View
27 demos/producer.php
View
@@ -24,23 +24,23 @@ class DemoCEH implements amqp\ChannelEventHandler
function publishConfirm (wire\Method $meth) {
if ($this->evOutput) {
- info("Channel event pubConf - dtag=%s", $meth->getField('delivery-tag'));
+ info("Channel event publishConfirm - message:\n%s", substr($meth->getContent(), 0, 5));
}
- $this->confirms[] = $meth->getField('delivery-tag');
+ $this->confirms[] = $meth;
}
function publishReturn (wire\Method $meth) {
if ($this->evOutput) {
- info("Channel event pubRet - dtag=%s", $meth->getField('delivery-tag'));
+ info("Channel event publishReturn - message:\n%s", substr($meth->getContent(), 0, 5));
}
- $this->returns[] = $meth->getField('delivery-tag');
+ $this->returns[] = $meth;
}
function publishNack (wire\Method $meth) {
if ($this->evOutput) {
- info("Channel event pubNack - dtag=%s", $meth->getField('delivery-tag'));
+ info("Channel event publishNack - message:\n%s", substr($meth->getContent(), 0, 5));
}
- $this->nacks[] = $meth->getField('delivery-tag');
+ $this->nacks[] = $meth;
}
}
@@ -95,6 +95,9 @@ function warn () {
--repeat [integer]
How many times to send the message
+ --recv-tmo [integer]
+ How many seconds to wait for publish confirms (default 3)
+
--confirms
Switch on the streaming confirms feature (default false)
@@ -121,7 +124,7 @@ function warn () {
/** Grab run options from the command line. */
$conf = getopt('', array('help', 'message:', 'repeat:', 'confirms', 'mandatory',
'immediate', 'exchange:', 'routing-key:', 'sleep:', 'ticker:',
- 'show-events', 'message-file:'));
+ 'show-events', 'message-file:', 'recv-tmo:'));
if (array_key_exists('help', $conf)) {
echo $USAGE;
@@ -140,6 +143,10 @@ function warn () {
$routingKey = '';
}
+$recvTmo = array_key_exists('recv-tmo', $conf)
+ ? intval($conf['recv-tmo'])
+ : 3;
+
$content = array();
if (array_key_exists('message', $conf)) {
$content = (array) $conf['message'];
@@ -181,9 +188,9 @@ function warn () {
/** Confirm selected options to the user */
info("Ready to publish:\n Message(s) '%s' \n Send Repeats: %s\n mandatory: %d\n" .
- " immediate: %d\n confirms: %d\n routing-key: %s\n exchange: %s\n sleep: %d\n ticker: %d",
+ " immediate: %d\n confirms: %d\n routing-key: %s\n exchange: %s\n sleep: %d\n ticker: %d\n receive timeout: %d",
implode("','", array_map(function ($m) { return substr($m, 0, 8); }, $content)),
- implode(', ', $N), $mandatory, $immediate, $confirms, $routingKey, $exchange, $sleep, $ticks);
+ implode(', ', $N), $mandatory, $immediate, $confirms, $routingKey, $exchange, $sleep, $ticks, $recvTmo);
/** Initialise the broker connection and send the messages. */
@@ -252,7 +259,7 @@ function warn () {
responses. */
if ($confirms || $mandatory || $immediate) {
/** Never wait more than 3 seconds for responses */
- $conn->pushExitStrategy(amqp\STRAT_TIMEOUT_REL, 3, 0);
+ $conn->pushExitStrategy(amqp\STRAT_TIMEOUT_REL, $recvTmo, 0);
if ($confirms) {
/** In confirm mode, add an additional rule so that the loop
exits as soon as all confirms have returned. */
43 src/amqphp/Channel.php
View
@@ -201,10 +201,9 @@ function invoke (wire\Method $m) {
// Do numbering of basic.publish during confirm mode
if ($this->confirmMode && $m->amqpClass == 'basic.publish') {
$this->confirmSeq++;
- $this->confirmSeqs[] = $this->confirmSeq;
+ $this->confirmSeqs[$this->confirmSeq] = $m;
}
-
return $this->myConn->invoke($m);
}
@@ -402,16 +401,12 @@ private function flushAcks () {
}
switch ($this->ackFlag) {
case CONSUMER_ACK:
- //printf(" (amqp\Channel) - flush acks for messages %s\n", implode(',', $this->pendingAcks));
$ack = $this->basic('ack', array('delivery-tag' => array_pop($this->pendingAcks),
'multiple' => true));
$this->invoke($ack);
break;
case CONSUMER_REJECT:
case CONSUMER_DROP:
-// printf(" (amqp\Channel) - flush %s for messages %s\n",
-// (($this->ackFlag == CONSUMER_REJECT) ? 'rejects' : 'drops'),
-// implode(',', $this->pendingAcks));
$rej = $this->basic('nack', array('delivery-tag' => array_pop($this->pendingAcks),
'multiple' => true,
'requeue' => ($this->ackFlag == CONSUMER_REJECT)));
@@ -430,29 +425,23 @@ private function flushAcks () {
* basic.{n}ack (RMQ Confirm key)
*/
private function removeConfirmSeqs (wire\Method $meth, $event) {
- if ($meth->getField('multiple')) {
+ if (! $this->callbackHandler) {
+ trigger_error("Received publish confirmations with no channel event handler in place", E_USER_WARNING);
+ return;
+ }
- $dtag = $meth->getField('delivery-tag');
- $evh = $this->callbackHandler;
- $this->confirmSeqs =
- array_filter($this->confirmSeqs,
- function ($id) use ($dtag, $evh, $event, $meth) {
- if ($id <= $dtag) {
- if ($evh) {
- $evh->$event($meth);
- }
- return false;
- } else {
- return true;
- }
- });
- } else {
- $dt = $meth->getField('delivery-tag');
- if (isset($this->confirmSeqs)) {
- if ($this->callbackHandler) {
- $this->callbackHandler->$event($meth);
+ $dtag = $meth->getField('delivery-tag');
+ if ($meth->getField('multiple')) {
+ foreach (array_keys($this->confirmSeqs) as $sk) {
+ if ($sk <= $dtag) {
+ $this->callbackHandler->$event($this->confirmSeqs[$sk]);
+ unset($this->confirmSeqs[$sk]);
}
- unset($this->confirmSeqs[array_search($dt, $this->confirmSeqs)]);
+ }
+ } else {
+ if (array_key_exists($dtag, $this->confirmSeqs)) {
+ $this->callbackHandler->$event($this->confirmSeqs[$dtag]);
+ unset($this->confirmSeqs[$dtag]);
}
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.