Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Duplicated namespace level consts as class consts and switched the

internal API  to use  class level  consts.  Nsp  level consts  are now
present for backwards compat purposes only.
  • Loading branch information...
commit d99b60f368b1ead3014f3443945aea98dc29d5ad 1 parent 45dcb79
@BraveSirRobin authored
View
2  build/cpf/amqphp/Channel.php
@@ -1,2 +1,2 @@
<?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 = 1; protected $ackHead; 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] = $m; } 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; } if (! is_array($response)) { $response = array($response); } $shouldAck = (! array_key_exists('no-ack', $consParams) || ! $consParams['no-ack']); foreach ($response as $resp) { switch ($resp) { case CONSUMER_ACK: if ($shouldAck) { $this->ack($meth, CONSUMER_ACK); } break; case CONSUMER_DROP: case CONSUMER_REJECT: if ($shouldAck) { $this->ack($meth, $resp); } break; case CONSUMER_CANCEL: $this->removeConsumerByTag($cons, $ctag); break; default: trigger_error("Invalid consumer response $resp - consumers must " . 'respond with either a single consumer flag, multiple ' . 'consumer flags, or an empty response', E_USER_WARNING); } } 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->ackHead = $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' => $this->ackHead, 'multiple' => ($this->ackBuffer > 1))); $this->invoke($ack); break; case CONSUMER_REJECT: case CONSUMER_DROP: $rej = $this->basic('nack', array('delivery-tag' => $this->ackHead, 'multiple' => ($this->ackBuffer > 1), 'requeue' => ($this->ackFlag == CONSUMER_REJECT))); $this->invoke($rej); break; default: throw new \Exception("Internal (n)ack tracking state error", 2956); } $this->ackFlag = $this->ackHead = null; $this->numPendAcks = 0; } private function removeConfirmSeqs (wire\Method $meth, $event) { if (! $this->callbackHandler) { trigger_error("Received publish confirmations with no channel event handler in place", E_USER_WARNING); return; } $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]); } } } else { if (array_key_exists($dtag, $this->confirmSeqs)) { $this->callbackHandler->$event($this->confirmSeqs[$dtag]); unset($this->confirmSeqs[$dtag]); } } } 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; } }
+ 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 = 1; protected $ackHead; 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] = $m; } 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 = Connection::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 = Connection::CONSUMER_REJECT; } if (! $response) { return; } if (! is_array($response)) { $response = array($response); } $shouldAck = (! array_key_exists('no-ack', $consParams) || ! $consParams['no-ack']); foreach ($response as $resp) { switch ($resp) { case Connection::CONSUMER_ACK: if ($shouldAck) { $this->ack($meth, Connection::CONSUMER_ACK); } break; case Connection::CONSUMER_DROP: case Connection::CONSUMER_REJECT: if ($shouldAck) { $this->ack($meth, $resp); } break; case Connection::CONSUMER_CANCEL: $this->removeConsumerByTag($cons, $ctag); break; default: trigger_error("Invalid consumer response $resp - consumers must " . 'respond with either a single consumer flag, multiple ' . 'consumer flags, or an empty response', E_USER_WARNING); } } 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->ackHead = $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 Connection::CONSUMER_ACK: $ack = $this->basic('ack', array('delivery-tag' => $this->ackHead, 'multiple' => ($this->ackBuffer > 1))); $this->invoke($ack); break; case Connection::CONSUMER_REJECT: case Connection::CONSUMER_DROP: $rej = $this->basic('nack', array('delivery-tag' => $this->ackHead, 'multiple' => ($this->ackBuffer > 1), 'requeue' => ($this->ackFlag == Connection::CONSUMER_REJECT))); $this->invoke($rej); break; default: throw new \Exception("Internal (n)ack tracking state error", 2956); } $this->ackFlag = $this->ackHead = null; $this->numPendAcks = 0; } private function removeConfirmSeqs (wire\Method $meth, $event) { if (! $this->callbackHandler) { trigger_error("Received publish confirmations with no channel event handler in place", E_USER_WARNING); return; } $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]); } } } else { if (array_key_exists($dtag, $this->confirmSeqs)) { $this->callbackHandler->$event($this->confirmSeqs[$dtag]); unset($this->confirmSeqs[$dtag]); } } } 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; } }
View
2  build/cpf/amqphp/Connection.php
@@ -1,2 +1,2 @@
<?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 getHeartbeat () { return $this->heartbeat; } 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(); } function clearSocketErrors () { $this->sock->clearErrors(); } 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: '{$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", $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; } }
+ 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 { const DEBUG = DEBUG; const PROTOCOL_HEADER = PROTOCOL_HEADER; const STRAT_TIMEOUT_ABS = STRAT_TIMEOUT_ABS; const STRAT_TIMEOUT_REL = STRAT_TIMEOUT_REL; const STRAT_MAXLOOPS = STRAT_MAXLOOPS; const STRAT_CALLBACK = STRAT_CALLBACK; const STRAT_COND = STRAT_COND; const CONSUMER_ACK = CONSUMER_ACK; const CONSUMER_REJECT = CONSUMER_REJECT; const CONSUMER_DROP = CONSUMER_DROP; const CONSUMER_CANCEL = CONSUMER_CANCEL; 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(self::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 !== self::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 getHeartbeat () { return $this->heartbeat; } 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(); } function clearSocketErrors () { $this->sock->clearErrors(); } 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 self::STRAT_TIMEOUT_ABS: case self::STRAT_TIMEOUT_REL: @list($epoch, $usecs) = $_args; $this->exStrats[] = $tmp = new TimeoutExitStrategy; return $tmp->configure($mode, $epoch, $usecs); case self::STRAT_MAXLOOPS: $this->exStrats[] = $tmp = new MaxloopExitStrategy; return $tmp->configure(self::STRAT_MAXLOOPS, array_shift($_args)); case self::STRAT_CALLBACK: $cb = array_shift($_args); $this->exStrats[] = $tmp = new CallbackExitStrategy; return $tmp->configure(self::STRAT_CALLBACK, $cb, $_args); case self::STRAT_COND: $this->exStrats[] = $tmp = new ConditionalExitStrategy; return $tmp->configure(self::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: '{$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", $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; } }
View
2  build/cpf/amqphp/Socket.php
@@ -1,2 +1,2 @@
<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; class Socket { const READ_SELECT = 1; const WRITE_SELECT = 2; const READ_LENGTH = 4096; const BLOCK_TIMEOUT = 5; 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); } else if (! socket_set_nonblock($this->sock)) { throw new \Exception("Failed to switch connection in to non-blocking mode.", 2357); } $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(self::BLOCK_TIMEOUT); if ($select === false) { return false; } else if ($select > 0) { $buff = $this->readAll(); } return $buff; } function lastError () { return socket_last_error($this->sock); } function clearErrors () { socket_clear_error($this->sock); } 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) { if (! $this->select(self::BLOCK_TIMEOUT, 0, self::WRITE_SELECT)) { trigger_error('Socket select failed for write (socket err: "' . $this->strError() . ')', E_USER_WARNING); return 0; } $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("Socket write failed: %s", $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; } }
+ namespace amqphp; use amqphp\protocol; use amqphp\wire; class Socket { const READ_SELECT = 1; const WRITE_SELECT = 2; const READ_LENGTH = 4096; const BLOCK_TIMEOUT = 5; 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); } else if (! socket_set_nonblock($this->sock)) { throw new \Exception("Failed to switch connection in to non-blocking mode.", 2357); } $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(self::BLOCK_TIMEOUT); if ($select === false) { return false; } else if ($select > 0) { $buff = $this->readAll(); } return $buff; } function lastError () { return socket_last_error($this->sock); } function clearErrors () { socket_clear_error($this->sock); } 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 (Connection::DEBUG) { echo "\n<read>\n"; echo wire\Hexdump::hexdump($buff); } return $buff; } function write ($buff) { if (! $this->select(self::BLOCK_TIMEOUT, 0, self::WRITE_SELECT)) { trigger_error('Socket select failed for write (socket err: "' . $this->strError() . ')', E_USER_WARNING); return 0; } $bw = 0; $contentLength = strlen($buff); while (true) { if (Connection::DEBUG) { echo "\n<write>\n"; echo wire\Hexdump::hexdump($buff); } if (($tmp = socket_write($this->sock, $buff)) === false) { throw new \Exception(sprintf("Socket write failed: %s", $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; } }
View
2  build/cpf/amqphp/StreamSocket.php
@@ -1,2 +1,2 @@
<?php
- namespace amqphp; use amqphp\protocol; use amqphp\wire; class StreamSocket { const READ_SELECT = 1; const WRITE_SELECT = 2; const READ_LENGTH = 4096; const BLOCK_TIMEOUT = 5; private static $All = array(); private static $Counter = 0; private $host, $port, $vhost; private $id, $connected, $interrupt = false; private $flags; private $stfp; private $errFlag = 0; 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); } } } if (! stream_set_blocking($this->sock, 0)) { throw new \Exception("Failed to place stream connection in non-blocking mode", 2795); } $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 $this->errFlag; } function clearErrors () { $this->errFlag = 0; } function strError () { switch ($this->errFlag) { case 1: return "Read error detected"; case 2: return "Write error detected"; case 3: return "Read and write errors detected"; default: return "No error detected"; } } function readAll ($readLen = self::READ_LENGTH) { $buff = ''; do { $buff .= $chk = fread($this->sock, $readLen); $smd = stream_get_meta_data($this->sock); $readLen = min($smd['unread_bytes'], $readLen); } while ($chk !== false && $smd['unread_bytes'] > 0); if (! $chk) { trigger_error("Stream fread returned false", E_USER_WARNING); $this->errFlag |= 1; } if (DEBUG) { echo "\n<read>\n"; echo wire\Hexdump::hexdump($buff); } return $buff; } function read () { $buff = ''; $select = $this->select(self::BLOCK_TIMEOUT); if ($select === false) { return false; } else if ($select > 0) { $buff = $this->readAll(); } return $buff; } function getUnreadBytes () { return ($smd = stream_get_meta_data($this->sock)) ? $smd['unread_bytes'] : false; } function eof () { return feof($this->sock); } function write ($buff) { if (! $this->select(self::BLOCK_TIMEOUT, 0, self::WRITE_SELECT)) { trigger_error('StreamSocket select failed for write (stream socket err: "' . $this->strError() . ')', E_USER_WARNING); return 0; } $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) { $this->errFlag |= 2; 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; } }
+ namespace amqphp; use amqphp\protocol; use amqphp\wire; class StreamSocket { const READ_SELECT = 1; const WRITE_SELECT = 2; const READ_LENGTH = 4096; const BLOCK_TIMEOUT = 5; private static $All = array(); private static $Counter = 0; private $host, $port, $vhost; private $id, $connected, $interrupt = false; private $flags; private $stfp; private $errFlag = 0; 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); } } } if (! stream_set_blocking($this->sock, 0)) { throw new \Exception("Failed to place stream connection in non-blocking mode", 2795); } $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 $this->errFlag; } function clearErrors () { $this->errFlag = 0; } function strError () { switch ($this->errFlag) { case 1: return "Read error detected"; case 2: return "Write error detected"; case 3: return "Read and write errors detected"; default: return "No error detected"; } } function readAll ($readLen = self::READ_LENGTH) { $buff = ''; do { $buff .= $chk = fread($this->sock, $readLen); $smd = stream_get_meta_data($this->sock); $readLen = min($smd['unread_bytes'], $readLen); } while ($chk !== false && $smd['unread_bytes'] > 0); if (! $chk) { trigger_error("Stream fread returned false", E_USER_WARNING); $this->errFlag |= 1; } if (Connection::DEBUG) { echo "\n<read>\n"; echo wire\Hexdump::hexdump($buff); } return $buff; } function read () { $buff = ''; $select = $this->select(self::BLOCK_TIMEOUT); if ($select === false) { return false; } else if ($select > 0) { $buff = $this->readAll(); } return $buff; } function getUnreadBytes () { return ($smd = stream_get_meta_data($this->sock)) ? $smd['unread_bytes'] : false; } function eof () { return feof($this->sock); } function write ($buff) { if (! $this->select(self::BLOCK_TIMEOUT, 0, self::WRITE_SELECT)) { trigger_error('StreamSocket select failed for write (stream socket err: "' . $this->strError() . ')', E_USER_WARNING); return 0; } $bw = 0; $contentLength = strlen($buff); if ($contentLength == 0) { return 0; } while (true) { if (Connection::DEBUG) { echo "\n<write>\n"; echo wire\Hexdump::hexdump($buff); } if (($tmp = fwrite($this->sock, $buff)) === false) { $this->errFlag |= 2; 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; } }
View
2  build/cpf/amqphp/TimeoutExitStrategy.php
@@ -1,2 +1,2 @@
<?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 () {} }
+ 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 == Connection::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 () {} }
View
10 build/nspf/amqphp.php
@@ -1,16 +1,16 @@
<?php
namespace ${namespace};
- use amqphp\protocol, amqphp\wire, \amqphp\persistent as pers; 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 () {} }
+ use amqphp\protocol, amqphp\wire, \amqphp\persistent as pers; 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 == Connection::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 () {} }
class EventLoop { const HB_TMOBUFF = 50000; private $cons = array(); private static $In = false; private $forceExit = false; private $minHb = -1; function addConnection (Connection $conn) { $this->cons[$conn->getSocketId()] = $conn; $this->setMinHb(); } private function setMinHb () { if ($this->cons) { foreach ($this->cons as $c) { if ((($n = $c->getHeartbeat()) > 0) && $n > $this->minHb) { $this->minHb = $n; } } } else { $this->minHb = -1; } } function removeConnection (Connection $conn) { if (array_key_exists($conn->getSocketId(), $this->cons)) { unset($this->cons[$conn->getSocketId()]); } $this->setMinHb(); } function forceLoopExit () { $this->forceExit = true; } 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; $missedHb = 0; 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(); if ($this->forceExit) { trigger_error("Select loop forced exit over-rides connection looping state", E_USER_WARNING); $this->forceExit = false; break; } $started = true; $selectCalledAt = microtime(); 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) { $missedHb = 0; foreach ($read as $sock) { $c = $this->cons[$sock->getId()]; try { $c->doSelectRead(); $c->deliverAll(); } catch (\Exception $e) { if ($sock->lastError()) { trigger_error("Exception raised on socket {$sock->getId()} during " . "event loop read (nested exception follows). Socket indicates an error, " . "close the connection immediately. Nested exception: '{$e->getMessage()}'", E_USER_WARNING); try { $c->shutdown(); } catch (\Exception $e) { trigger_error("Nested exception swallowed during emergency socket " . "shutdown: '{$e->getMessage()}'", E_USER_WARNING); } $this->removeConnection($c); } else { trigger_error("Exception raised on socket {$sock->getId()} during " . "event loop read (nested exception follows). Socket does NOT " . "indicate an error, try again. Nested exception: '{$e->getMessage()}'", E_USER_WARNING); } } } } else { if ($this->minHb > 0) { list($stUsecs, $stSecs) = explode(' ', $selectCalledAt); list($usecs, $secs) = explode(' ', microtime()); if (($secs + $usecs) - ($stSecs + $stUsecs) > $this->minHb) { if (++$missedHb >= 2) { throw new \Exception("Broker missed too many heartbeats", 2957); } else { trigger_error("Broker heartbeat missed from client side, one more triggers loop exit", E_USER_WARNING); } } } } } 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; } } } } if ($wins && ($this->minHb > 0) && ($wins === true || $wins[0] > $this->minHb || ($wins[0] == $this->minHb && $wins[1] < self::HB_TMOBUFF)) ) { $wins = array($this->minHb, self::HB_TMOBUFF); } return $wins; } private function signal () { foreach ($this->cons as $c) { if ($c->getSignalDispatch()) { pcntl_signal_dispatch(); return; } } } }
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; } }
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 () {} }
- 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 getHeartbeat () { return $this->heartbeat; } 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(); } function clearSocketErrors () { $this->sock->clearErrors(); } 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: '{$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", $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; } }
+ 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 { const DEBUG = DEBUG; const PROTOCOL_HEADER = PROTOCOL_HEADER; const STRAT_TIMEOUT_ABS = STRAT_TIMEOUT_ABS; const STRAT_TIMEOUT_REL = STRAT_TIMEOUT_REL; const STRAT_MAXLOOPS = STRAT_MAXLOOPS; const STRAT_CALLBACK = STRAT_CALLBACK; const STRAT_COND = STRAT_COND; const CONSUMER_ACK = CONSUMER_ACK; const CONSUMER_REJECT = CONSUMER_REJECT; const CONSUMER_DROP = CONSUMER_DROP; const CONSUMER_CANCEL = CONSUMER_CANCEL; 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(self::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 !== self::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 getHeartbeat () { return $this->heartbeat; } 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(); } function clearSocketErrors () { $this->sock->clearErrors(); } 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 self::STRAT_TIMEOUT_ABS: case self::STRAT_TIMEOUT_REL: @list($epoch, $usecs) = $_args; $this->exStrats[] = $tmp = new TimeoutExitStrategy; return $tmp->configure($mode, $epoch, $usecs); case self::STRAT_MAXLOOPS: $this->exStrats[] = $tmp = new MaxloopExitStrategy; return $tmp->configure(self::STRAT_MAXLOOPS, array_shift($_args)); case self::STRAT_CALLBACK: $cb = array_shift($_args); $this->exStrats[] = $tmp = new CallbackExitStrategy; return $tmp->configure(self::STRAT_CALLBACK, $cb, $_args); case self::STRAT_COND: $this->exStrats[] = $tmp = new ConditionalExitStrategy; return $tmp->configure(self::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: '{$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", $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; } }
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)); } }
- 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 = 1; protected $ackHead; 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] = $m; } 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; } if (! is_array($response)) { $response = array($response); } $shouldAck = (! array_key_exists('no-ack', $consParams) || ! $consParams['no-ack']); foreach ($response as $resp) { switch ($resp) { case CONSUMER_ACK: if ($shouldAck) { $this->ack($meth, CONSUMER_ACK); } break; case CONSUMER_DROP: case CONSUMER_REJECT: if ($shouldAck) { $this->ack($meth, $resp); } break; case CONSUMER_CANCEL: $this->removeConsumerByTag($cons, $ctag); break; default: trigger_error("Invalid consumer response $resp - consumers must " . 'respond with either a single consumer flag, multiple ' . 'consumer flags, or an empty response', E_USER_WARNING); } } 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->ackHead = $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' => $this->ackHead, 'multiple' => ($this->ackBuffer > 1))); $this->invoke($ack); break; case CONSUMER_REJECT: case CONSUMER_DROP: $rej = $this->basic('nack', array('delivery-tag' => $this->ackHead, 'multiple' => ($this->ackBuffer > 1), 'requeue' => ($this->ackFlag == CONSUMER_REJECT))); $this->invoke($rej); break; default: throw new \Exception("Internal (n)ack tracking state error", 2956); } $this->ackFlag = $this->ackHead = null; $this->numPendAcks = 0; } private function removeConfirmSeqs (wire\Method $meth, $event) { if (! $this->callbackHandler) { trigger_error("Received publish confirmations with no channel event handler in place", E_USER_WARNING); return; } $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]); } } } else { if (array_key_exists($dtag, $this->confirmSeqs)) { $this->callbackHandler->$event($this->confirmSeqs[$dtag]); unset($this->confirmSeqs[$dtag]); } } } 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; } }
- class StreamSocket { const READ_SELECT = 1; const WRITE_SELECT = 2; const READ_LENGTH = 4096; const BLOCK_TIMEOUT = 5; private static $All = array(); private static $Counter = 0; private $host, $port, $vhost; private $id, $connected, $interrupt = false; private $flags; private $stfp; private $errFlag = 0; 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); } } } if (! stream_set_blocking($this->sock, 0)) { throw new \Exception("Failed to place stream connection in non-blocking mode", 2795); } $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 $this->errFlag; } function clearErrors () { $this->errFlag = 0; } function strError () { switch ($this->errFlag) { case 1: return "Read error detected"; case 2: return "Write error detected"; case 3: return "Read and write errors detected"; default: return "No error detected"; } } function readAll ($readLen = self::READ_LENGTH) { $buff = ''; do { $buff .= $chk = fread($this->sock, $readLen); $smd = stream_get_meta_data($this->sock); $readLen = min($smd['unread_bytes'], $readLen); } while ($chk !== false && $smd['unread_bytes'] > 0); if (! $chk) { trigger_error("Stream fread returned false", E_USER_WARNING); $this->errFlag |= 1; } if (DEBUG) { echo "\n<read>\n"; echo wire\Hexdump::hexdump($buff); } return $buff; } function read () { $buff = ''; $select = $this->select(self::BLOCK_TIMEOUT); if ($select === false) { return false; } else if ($select > 0) { $buff = $this->readAll(); } return $buff; } function getUnreadBytes () { return ($smd = stream_get_meta_data($this->sock)) ? $smd['unread_bytes'] : false; } function eof () { return feof($this->sock); } function write ($buff) { if (! $this->select(self::BLOCK_TIMEOUT, 0, self::WRITE_SELECT)) { trigger_error('StreamSocket select failed for write (stream socket err: "' . $this->strError() . ')', E_USER_WARNING); return 0; } $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) { $this->errFlag |= 2; 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; } }
+ 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 = 1; protected $ackHead; 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] = $m; } 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 = Connection::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 = Connection::CONSUMER_REJECT; } if (! $response) { return; } if (! is_array($response)) { $response = array($response); } $shouldAck = (! array_key_exists('no-ack', $consParams) || ! $consParams['no-ack']); foreach ($response as $resp) { switch ($resp) { case Connection::CONSUMER_ACK: if ($shouldAck) { $this->ack($meth, Connection::CONSUMER_ACK); } break; case Connection::CONSUMER_DROP: case Connection::CONSUMER_REJECT: if ($shouldAck) { $this->ack($meth, $resp); } break; case Connection::CONSUMER_CANCEL: $this->removeConsumerByTag($cons, $ctag); break; default: trigger_error("Invalid consumer response $resp - consumers must " . 'respond with either a single consumer flag, multiple ' . 'consumer flags, or an empty response', E_USER_WARNING); } } 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->ackHead = $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 Connection::CONSUMER_ACK: $ack = $this->basic('ack', array('delivery-tag' => $this->ackHead, 'multiple' => ($this->ackBuffer > 1))); $this->invoke($ack); break; case Connection::CONSUMER_REJECT: case Connection::CONSUMER_DROP: $rej = $this->basic('nack', array('delivery-tag' => $this->ackHead, 'multiple' => ($this->ackBuffer > 1), 'requeue' => ($this->ackFlag == Connection::CONSUMER_REJECT))); $this->invoke($rej); break; default: throw new \Exception("Internal (n)ack tracking state error", 2956); } $this->ackFlag = $this->ackHead = null; $this->numPendAcks = 0; } private function removeConfirmSeqs (wire\Method $meth, $event) { if (! $this->callbackHandler) { trigger_error("Received publish confirmations with no channel event handler in place", E_USER_WARNING); return; } $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]); } } } else { if (array_key_exists($dtag, $this->confirmSeqs)) { $this->callbackHandler->$event($this->confirmSeqs[$dtag]); unset($this->confirmSeqs[$dtag]); } } } 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; } }
+ class StreamSocket { const READ_SELECT = 1; const WRITE_SELECT = 2; const READ_LENGTH = 4096; const BLOCK_TIMEOUT = 5; private static $All = array(); private static $Counter = 0; private $host, $port, $vhost; private $id, $connected, $interrupt = false; private $flags; private $stfp; private $errFlag = 0; 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); } } } if (! stream_set_blocking($this->sock, 0)) { throw new \Exception("Failed to place stream connection in non-blocking mode", 2795); } $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 $this->errFlag; } function clearErrors () { $this->errFlag = 0; } function strError () { switch ($this->errFlag) { case 1: return "Read error detected"; case 2: return "Write error detected"; case 3: return "Read and write errors detected"; default: return "No error detected"; } } function readAll ($readLen = self::READ_LENGTH) { $buff = ''; do { $buff .= $chk = fread($this->sock, $readLen); $smd = stream_get_meta_data($this->sock); $readLen = min($smd['unread_bytes'], $readLen); } while ($chk !== false && $smd['unread_bytes'] > 0); if (! $chk) { trigger_error("Stream fread returned false", E_USER_WARNING); $this->errFlag |= 1; } if (Connection::DEBUG) { echo "\n<read>\n"; echo wire\Hexdump::hexdump($buff); } return $buff; } function read () { $buff = ''; $select = $this->select(self::BLOCK_TIMEOUT); if ($select === false) { return false; } else if ($select > 0) { $buff = $this->readAll(); } return $buff; } function getUnreadBytes () { return ($smd = stream_get_meta_data($this->sock)) ? $smd['unread_bytes'] : false; } function eof () { return feof($this->sock); } function write ($buff) { if (! $this->select(self::BLOCK_TIMEOUT, 0, self::WRITE_SELECT)) { trigger_error('StreamSocket select failed for write (stream socket err: "' . $this->strError() . ')', E_USER_WARNING); return 0; } $bw = 0; $contentLength = strlen($buff); if ($contentLength == 0) { return 0; } while (true) { if (Connection::DEBUG) { echo "\n<write>\n"; echo wire\Hexdump::hexdump($buff); } if (($tmp = fwrite($this->sock, $buff)) === false) { $this->errFlag |= 2; 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; } }
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 () {} }
interface ExitStrategy { function configure ($sMode); function init (Connection $conn); function preSelect ($prev=null); function complete (); }
interface ChannelEventHandler { function publishConfirm (wire\Method $meth); function publishReturn (wire\Method $meth); function publishNack(wire\Method $meth); }
- class Socket { const READ_SELECT = 1; const WRITE_SELECT = 2; const READ_LENGTH = 4096; const BLOCK_TIMEOUT = 5; 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); } else if (! socket_set_nonblock($this->sock)) { throw new \Exception("Failed to switch connection in to non-blocking mode.", 2357); } $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(self::BLOCK_TIMEOUT); if ($select === false) { return false; } else if ($select > 0) { $buff = $this->readAll(); } return $buff; } function lastError () { return socket_last_error($this->sock); } function clearErrors () { socket_clear_error($this->sock); } 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) { if (! $this->select(self::BLOCK_TIMEOUT, 0, self::WRITE_SELECT)) { trigger_error('Socket select failed for write (socket err: "' . $this->strError() . ')', E_USER_WARNING); return 0; } $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("Socket write failed: %s", $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; } }
+ class Socket { const READ_SELECT = 1; const WRITE_SELECT = 2; const READ_LENGTH = 4096; const BLOCK_TIMEOUT = 5; 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); } else if (! socket_set_nonblock($this->sock)) { throw new \Exception("Failed to switch connection in to non-blocking mode.", 2357); } $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(self::BLOCK_TIMEOUT); if ($select === false) { return false; } else if ($select > 0) { $buff = $this->readAll(); } return $buff; } function lastError () { return socket_last_error($this->sock); } function clearErrors () { socket_clear_error($this->sock); } 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 (Connection::DEBUG) { echo "\n<read>\n"; echo wire\Hexdump::hexdump($buff); } return $buff; } function write ($buff) { if (! $this->select(self::BLOCK_TIMEOUT, 0, self::WRITE_SELECT)) { trigger_error('Socket select failed for write (socket err: "' . $this->strError() . ')', E_USER_WARNING); return 0; } $bw = 0; $contentLength = strlen($buff); while (true) { if (Connection::DEBUG) { echo "\n<write>\n"; echo wire\Hexdump::hexdump($buff); } if (($tmp = socket_write($this->sock, $buff)) === false) { throw new \Exception(sprintf("Socket write failed: %s", $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; } }
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); }
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); } }
View
40 demos/consumer.php
@@ -29,9 +29,6 @@
use amqphp\protocol;
use amqphp\wire;
-// HACK : Manually pre-load the connection class so that Amqphp consts
-// are available - these cannot be loaded by the class loader.
-require __DIR__ . '/../src/amqphp/Connection.php';
require __DIR__ . '/class-loader.php';
@@ -50,11 +47,11 @@ class MultiConsumer implements amqp\Consumer, amqp\ChannelEventHandler
{
/** CLI Exit strategy identifier map */
public static $StratMap = array(
- 'cond' => amqp\STRAT_COND,
- 'trel' => amqp\STRAT_TIMEOUT_REL,
- 'tabs' => amqp\STRAT_TIMEOUT_ABS,
- 'maxl' => amqp\STRAT_MAXLOOPS,
- 'callb' => amqp\STRAT_CALLBACK
+ 'cond' => amqp\Connection::STRAT_COND,
+ 'trel' => amqp\Connection::STRAT_TIMEOUT_REL,
+ 'tabs' => amqp\Connection::STRAT_TIMEOUT_ABS,
+ 'maxl' => amqp\Connection::STRAT_MAXLOOPS,
+ 'callb' => amqp\Connection::STRAT_CALLBACK
);
/* Specify default 'action messages' (these are defined in CLI args) */
@@ -167,30 +164,30 @@ function handleDelivery (wire\Method $m, amqp\Channel $chan) {
if ($cNum === false) {
// This should never happen!
warn("Received message for unknown consume tag %s, reject", $cTag);
- return amqp\CONSUMER_REJECT;
+ return amqp\Connection::CONSUMER_REJECT;
}
$content = $m->getContent();
if (! $content) {
info("Empty Message received on consumer %d [%s]", $cNum, $cTag);
- return amqp\CONSUMER_ACK;
+ return amqp\Connection::CONSUMER_ACK;
}
if ($content === $this->exitMessage) {
info("Received exit message, cancel consumer %d", $cNum);
- return array(amqp\CONSUMER_ACK, amqp\CONSUMER_CANCEL);
+ return array(amqp\Connection::CONSUMER_ACK, amqp\Connection::CONSUMER_CANCEL);
} else if ($content === $this->rejectMessage) {
info("Received reject message, reject consumer %d", $cNum);
- return amqp\CONSUMER_REJECT;
+ return amqp\Connection::CONSUMER_REJECT;
} else if ($content === $this->dropMessage) {
info("Received drop message, drop consumer %d", $cNum);
- return amqp\CONSUMER_DROP;
+ return amqp\Connection::CONSUMER_DROP;
} else {
printf("[MSG] consumer-tag=%s [%d]\ndelivery-tag=%s redelivered=%s\nexchange=%s\nrouting-key=%s\n%s\n",
$cTag, $cNum, $m->getField('delivery-tag'), $m->getField('redelivered') ? 't' : 'f', $m->getField('exchange'),
$m->getField('routing-key'), $content);
- return amqp\CONSUMER_ACK;
+ return amqp\Connection::CONSUMER_ACK;
}
}
@@ -272,17 +269,18 @@ function randomExitController () {
http://www.rabbitmq.com/interoperability.html)
--exit-message [string] - when the following message string is
- received, exit the receiving consumer with CONSUMER_CANCEL.
- Default Value: "Cancel."
+ received, exit the receiving consumer with
+ Connection::CONSUMER_CANCEL. Default Value: "Cancel."
--reject-message [string] - when the following message string is
- received, reject the incoming message with CONSUMER_REJECT.
- Default Value: "Reject." (Note: RabbitMQ does not support the
- no-local flag: http://www.rabbitmq.com/interoperability.html)
+ received, reject the incoming message with
+ Connection::CONSUMER_REJECT. Default Value: "Reject." (Note:
+ RabbitMQ does not support the no-local flag:
+ http://www.rabbitmq.com/interoperability.html)
--drop-message [string] - when the following message string is
- received, reject the incoming message with CONSUMER_DROP. Default
- Value: "Drop."
+ received, reject the incoming message with
+ Connection::CONSUMER_DROP. Default Value: "Drop."
You can add more than one consumer by using more than one
View
2  demos/pconnections/web-common.php
@@ -81,7 +81,7 @@ function handleDelivery (wire\Method $meth, amqp\Channel $chan) {
} else {
error_log("!!No handler for message delivery:\n%s", $meth->getContent());
}
- return amqp\CONSUMER_ACK;
+ return amqp\Connection::CONSUMER_ACK;
}
}
View
4 demos/pconnections/web-multi-process-test.php
@@ -149,7 +149,7 @@ private function doConsume ($params) {
$loops = ($params['param1'] && is_numeric($params['param1'])) ?
(int) $params['param1']
: 1;
- $conn->pushExitStrategy(amqp\STRAT_MAXLOOPS, $loops);
+ $conn->pushExitStrategy(amqp\Connection::STRAT_MAXLOOPS, $loops);
$conn->select();
$this->view->messages[] = sprintf("Consumed %d messages on connection %d via. maxloops with trigger %d",
MultiProcessPCTest::$CC, $params['connection'], $loops);
@@ -162,7 +162,7 @@ private function doConsume ($params) {
$usecs = ($params['param2'] && is_numeric($params['param2'])) ?
(int) $params['param2']
: 0;
- $conn->pushExitStrategy(amqp\STRAT_TIMEOUT_REL, (string) $secs, (string) $usecs);
+ $conn->pushExitStrategy(amqp\Connection::STRAT_TIMEOUT_REL, (string) $secs, (string) $usecs);
$conn->select();
$this->view->messages[] = sprintf("Consumed %d messages on connection %d via. timeout with trigger (%s, %s)",
MultiProcessPCTest::$CC, $params['connection'], (string) $secs, (string) $usecs);
View
2  demos/pconnections/web-single-process-test.php
@@ -264,7 +264,7 @@ function consume ($cons) {
foreach ($cons as $k) {
if (array_key_exists($k, $this->cache)) {
- $this->cache[$k]->pushExitStrategy(amqp\STRAT_TIMEOUT_REL, 1, 500000);
+ $this->cache[$k]->pushExitStrategy(amqp\Connection::STRAT_TIMEOUT_REL, 1, 500000);
foreach ($this->cache[$k]->getChannels() as $chan) {
foreach ($chan->getConsumerTags() as $ctag) {
$chan->getConsumerByTag($ctag)->nf = array('PConnHelper', 'ConsumerCallback');
View
6 demos/producer.php
@@ -267,18 +267,18 @@ function warn () {
responses. */
if ($confirms || $mandatory || $immediate) {
/** Never wait more than 3 seconds for responses */
- $conn->pushExitStrategy(amqp\STRAT_TIMEOUT_REL, $recvTmo, 0);
+ $conn->pushExitStrategy(amqp\Connection::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. */
- $conn->pushExitStrategy(amqp\STRAT_COND);
+ $conn->pushExitStrategy(amqp\Connection::STRAT_COND);
} else {
/** Add a callback exit strategy so that we can exit early if
all messages are returned */
$callback = function () use ($n, $ceh) {
return (count($ceh->returns) < $n);
};
- $conn->pushExitStrategy(amqp\STRAT_CALLBACK, $callback);
+ $conn->pushExitStrategy(amqp\Connection::STRAT_CALLBACK, $callback);
}
$evl = new amqp\EventLoop;
$evl->addConnection($conn);
View
2  demos/thumper/Consumer.php
@@ -45,7 +45,7 @@ function setCallback ($cb) {
function consume ($n) {
$this->n = $n;
- $this->connection->pushExitStrategy(amqp\STRAT_CALLBACK, array($this, 'loopCallbackHandler'));
+ $this->connection->pushExitStrategy(amqp\Connection::STRAT_CALLBACK, array($this, 'loopCallbackHandler'));
$this->connection->select();
}
View
2  demos/thumper/RpcClient.php
@@ -64,7 +64,7 @@ public function addRequest($msgBody, $server, $requestId = null, $routingKey = '
* to wait for RPC replies. */
public function getReplies() {
$evh = new amqp\EventLoop;
- $this->connection->pushExitStrategy(amqp\STRAT_CALLBACK, array($this, 'loopCallbackHandler'));
+ $this->connection->pushExitStrategy(amqp\Connection::STRAT_CALLBACK, array($this, 'loopCallbackHandler'));
$evh->addConnection($this->connection);
$evh->select();
$this->channel->removeAllConsumers();
View
24 src/amqphp/Channel.php
@@ -120,7 +120,7 @@ class Channel
/**
* Specifies the kind of responses that are queued up as local
- * acks, one of the CONSUMER_* consts.
+ * acks, one of the Connection::CONSUMER_* consts.
*/
protected $ackFlag;
@@ -341,12 +341,12 @@ private function deliverConsumerMessage ($meth) {
$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;
+ $response = Connection::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;
+ $response = Connection::CONSUMER_REJECT;
}
if (! $response) {
@@ -359,18 +359,18 @@ private function deliverConsumerMessage ($meth) {
$shouldAck = (! array_key_exists('no-ack', $consParams) || ! $consParams['no-ack']);
foreach ($response as $resp) {
switch ($resp) {
- case CONSUMER_ACK:
+ case Connection::CONSUMER_ACK:
if ($shouldAck) {
- $this->ack($meth, CONSUMER_ACK);
+ $this->ack($meth, Connection::CONSUMER_ACK);
}
break;
- case CONSUMER_DROP:
- case CONSUMER_REJECT:
+ case Connection::CONSUMER_DROP:
+ case Connection::CONSUMER_REJECT:
if ($shouldAck) {
$this->ack($meth, $resp);
}
break;
- case CONSUMER_CANCEL:
+ case Connection::CONSUMER_CANCEL:
$this->removeConsumerByTag($cons, $ctag);
break;
default:
@@ -414,16 +414,16 @@ private function flushAcks () {
return;
}
switch ($this->ackFlag) {
- case CONSUMER_ACK:
+ case Connection::CONSUMER_ACK:
$ack = $this->basic('ack', array('delivery-tag' => $this->ackHead,
'multiple' => ($this->ackBuffer > 1)));
$this->invoke($ack);
break;
- case CONSUMER_REJECT:
- case CONSUMER_DROP:
+ case Connection::CONSUMER_REJECT:
+ case Connection::CONSUMER_DROP:
$rej = $this->basic('nack', array('delivery-tag' => $this->ackHead,
'multiple' => ($this->ackBuffer > 1),
- 'requeue' => ($this->ackFlag == CONSUMER_REJECT)));
+ 'requeue' => ($this->ackFlag == Connection::CONSUMER_REJECT)));
$this->invoke($rej);
break;
default:
View
54 src/amqphp/Connection.php
@@ -18,6 +18,20 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+/**
+ * A note on the use of consts.
+ *
+ * I originally wrote Amqphp to use namespace-level consts, i.e. the
+ * ones defined below, but have since come to realise this is a bit of
+ * a cock-up because namespace level consts cannot be autoloaded.
+ * Therefore, I have duplicated all of these consts as class level
+ * consts inside the Connection class, this should make it easier to
+ * use Amqphp with standard PSR-0 autoloaders. I'll leave all
+ * namespace level consts in place for the foreseeable future so as
+ * not to break existing code, however please DO NOT use these, use
+ * the class level ones instead.
+ */
+
namespace amqphp;
use amqphp\protocol;
@@ -59,6 +73,24 @@
*/
class Connection
{
+
+ /**
+ * Duplicate all namespace level consts in an autoloadable
+ * location. See the notes on this at the top of this file.
+ */
+ const DEBUG = DEBUG;
+ const PROTOCOL_HEADER = PROTOCOL_HEADER;
+ const STRAT_TIMEOUT_ABS = STRAT_TIMEOUT_ABS;
+ const STRAT_TIMEOUT_REL = STRAT_TIMEOUT_REL;
+ const STRAT_MAXLOOPS = STRAT_MAXLOOPS;
+ const STRAT_CALLBACK = STRAT_CALLBACK;
+ const STRAT_COND = STRAT_COND;
+ const CONSUMER_ACK = CONSUMER_ACK;
+ const CONSUMER_REJECT = CONSUMER_REJECT;
+ const CONSUMER_DROP = CONSUMER_DROP;
+ const CONSUMER_CANCEL = CONSUMER_CANCEL;
+
+
/** Default client-properties field used during connection setup */
public static $ClientProperties = array(
'product' => ' BraveSirRobin/amqphp',
@@ -212,13 +244,13 @@ function connect () {
* @throws \Exception
*/
protected function doConnectionStartup () {
- if (! $this->write(PROTOCOL_HEADER)) {
+ if (! $this->write(self::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) {
+ if (substr($raw, 0, 4) == 'AMQP' && $raw !== self::PROTOCOL_HEADER) {
// Unexpected AMQP version
throw new \Exception("Connection initialisation failed (3)", 9875);
}
@@ -488,7 +520,7 @@ function select () {
* 5) Conditional exit (automatic) (current impl)
* 6) Infinite
- * @param integer $mode One of the STRAT_XXX consts.
+ * @param integer $mode One of the self::STRAT_XXX consts.
* @param ... Following 0 or more params are $mode dependant
* @return boolean True if the mode was set OK
*/
@@ -503,21 +535,21 @@ function pushExitStrategy () {
return false;
}
switch ($mode = array_shift($_args)) {
- case STRAT_TIMEOUT_ABS:
- case STRAT_TIMEOUT_REL:
+ case self::STRAT_TIMEOUT_ABS:
+ case self::STRAT_TIMEOUT_REL:
@list($epoch, $usecs) = $_args;
$this->exStrats[] = $tmp = new TimeoutExitStrategy;
return $tmp->configure($mode, $epoch, $usecs);
- case STRAT_MAXLOOPS:
+ case self::STRAT_MAXLOOPS:
$this->exStrats[] = $tmp = new MaxloopExitStrategy;
- return $tmp->configure(STRAT_MAXLOOPS, array_shift($_args));
- case STRAT_CALLBACK:
+ return $tmp->configure(self::STRAT_MAXLOOPS, array_shift($_args));
+ case self::STRAT_CALLBACK:
$cb = array_shift($_args);
$this->exStrats[] = $tmp = new CallbackExitStrategy;
- return $tmp->configure(STRAT_CALLBACK, $cb, $_args);
- case STRAT_COND:
+ return $tmp->configure(self::STRAT_CALLBACK, $cb, $_args);
+ case self::STRAT_COND:
$this->exStrats[] = $tmp = new ConditionalExitStrategy;
- return $tmp->configure(STRAT_COND, $this);
+ return $tmp->configure(self::STRAT_COND, $this);
default:
trigger_error("Select mode - mode not found", E_USER_WARNING);
return false;
View
7 src/amqphp/Consumer.php
@@ -47,9 +47,10 @@ function handleCancel (wire\Method $meth, Channel $chan);
function handleConsumeOk (wire\Method $meth, Channel $chan);
/**
- * Must handle message deliveries
- * @return array A list of \amqphp\CONSUMER_* consts, these are
- * sent to the broker by $chan
+ * Must handle message deliveries
+ *
+ * @return array A list of \amqphp\Connection::CONSUMER_* consts,
+ * these are sent to the broker by $chan
*/
function handleDelivery (wire\Method $meth, Channel $chan);
View
4 src/amqphp/Socket.php
@@ -208,7 +208,7 @@ function readAll ($readLen = self::READ_LENGTH) {
while (@socket_recv($this->sock, $tmp, $readLen, MSG_DONTWAIT)) {
$buff .= $tmp;
}
- if (DEBUG) {
+ if (Connection::DEBUG) {
echo "\n<read>\n";
echo wire\Hexdump::hexdump($buff);
}
@@ -227,7 +227,7 @@ function write ($buff) {
$bw = 0;
$contentLength = strlen($buff);
while (true) {
- if (DEBUG) {
+ if (Connection::DEBUG) {
echo "\n<write>\n";
echo wire\Hexdump::hexdump($buff);
}
View
4 src/amqphp/StreamSocket.php
@@ -270,7 +270,7 @@ function readAll ($readLen = self::READ_LENGTH) {
trigger_error("Stream fread returned false", E_USER_WARNING);
$this->errFlag |= 1;
}
- if (DEBUG) {
+ if (Connection::DEBUG) {
echo "\n<read>\n";
echo wire\Hexdump::hexdump($buff);
}
@@ -321,7 +321,7 @@ function write ($buff) {
return 0;
}
while (true) {
- if (DEBUG) {
+ if (Connection::DEBUG) {
echo "\n<write>\n";
echo wire\Hexdump::hexdump($buff);
}
View
4 src/amqphp/TimeoutExitStrategy.php
@@ -31,7 +31,7 @@
*/
class TimeoutExitStrategy implements ExitStrategy
{
- /** Config param, one of STRAT_TIMEOUT_ABS or STRAT_TIMEOUT_REL */
+ /** Config param, one of Connection::STRAT_TIMEOUT_ABS or Connection::STRAT_TIMEOUT_REL */
private $toStyle;
/** Config param */
@@ -56,7 +56,7 @@ function configure ($sMode, $secs=null, $usecs=null) {
}
function init (Connection $conn) {
- if ($this->toStyle == STRAT_TIMEOUT_REL) {
+ if ($this->toStyle == Connection::STRAT_TIMEOUT_REL) {
list($uSecs, $epoch) = explode(' ', microtime());
$uSecs = bcmul($uSecs, '1000000');
$this->usecs = bcadd($this->usecs, $uSecs);
Please sign in to comment.
Something went wrong with that request. Please try again.