Merge pull request #3299 from gcoeugnet/master

Add Canopsis transport
This commit is contained in:
Neil Lathwood
2016-03-31 17:04:34 +01:00
49 changed files with 8785 additions and 0 deletions

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,419 @@
<?php
namespace PhpAmqpLib\Channel;
use PhpAmqpLib\Connection\AbstractConnection;
use PhpAmqpLib\Exception\AMQPOutOfBoundsException;
use PhpAmqpLib\Exception\AMQPOutOfRangeException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Helper\MiscHelper;
use PhpAmqpLib\Helper\Protocol\MethodMap080;
use PhpAmqpLib\Helper\Protocol\MethodMap091;
use PhpAmqpLib\Helper\Protocol\Protocol080;
use PhpAmqpLib\Helper\Protocol\Protocol091;
use PhpAmqpLib\Helper\Protocol\Wait080;
use PhpAmqpLib\Helper\Protocol\Wait091;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPReader;
class AbstractChannel
{
const PROTOCOL_080 = '0.8';
const PROTOCOL_091 = '0.9.1';
public static $PROTOCOL_CONSTANTS_CLASS;
/** @var array */
protected $frame_queue;
/** @var array */
protected $method_queue;
/** @var bool */
protected $auto_decode;
/** @var string */
protected $amqp_protocol_header;
/** @var bool */
protected $debug;
/** @var \PhpAmqpLib\Connection\AbstractConnection */
protected $connection;
/** @var string */
protected $protocolVersion;
/**
* @var int
*/
protected $body_size_max = null;
/** @var \PhpAmqpLib\Helper\Protocol\Protocol080|\PhpAmqpLib\Helper\Protocol\Protocol091 */
protected $protocolWriter;
/** @var \PhpAmqpLib\Helper\Protocol\Wait080|\PhpAmqpLib\Helper\Protocol\Wait091 */
protected $waitHelper;
/** @var \PhpAmqpLib\Helper\Protocol\MethodMap080|\PhpAmqpLib\Helper\Protocol\MethodMap091 */
protected $methodMap;
/** @var string */
protected $channel_id;
/** @var \PhpAmqpLib\Wire\AMQPReader */
protected $msg_property_reader;
/** @var \PhpAmqpLib\Wire\AMQPReader */
protected $wait_content_reader;
/** @var \PhpAmqpLib\Wire\AMQPReader */
protected $dispatch_reader;
/**
* @param AbstractConnection $connection
* @param $channel_id
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
*/
public function __construct(AbstractConnection $connection, $channel_id)
{
$this->connection = $connection;
$this->channel_id = $channel_id;
$connection->channels[$channel_id] = $this;
$this->frame_queue = array(); // Lower level queue for frames
$this->method_queue = array(); // Higher level queue for methods
$this->auto_decode = false;
$this->debug = defined('AMQP_DEBUG') ? AMQP_DEBUG : false;
$this->msg_property_reader = new AMQPReader(null);
$this->wait_content_reader = new AMQPReader(null);
$this->dispatch_reader = new AMQPReader(null);
$this->protocolVersion = self::getProtocolVersion();
switch ($this->protocolVersion) {
case self::PROTOCOL_091:
self::$PROTOCOL_CONSTANTS_CLASS = 'PhpAmqpLib\Wire\Constants091';
$c = self::$PROTOCOL_CONSTANTS_CLASS;
$this->amqp_protocol_header = $c::$AMQP_PROTOCOL_HEADER;
$this->protocolWriter = new Protocol091();
$this->waitHelper = new Wait091();
$this->methodMap = new MethodMap091();
break;
case self::PROTOCOL_080:
self::$PROTOCOL_CONSTANTS_CLASS = 'PhpAmqpLib\Wire\Constants080';
$c = self::$PROTOCOL_CONSTANTS_CLASS;
$this->amqp_protocol_header = $c::$AMQP_PROTOCOL_HEADER;
$this->protocolWriter = new Protocol080();
$this->waitHelper = new Wait080();
$this->methodMap = new MethodMap080();
break;
default:
//this is logic exception (requires code changes to fix), so OutOfRange, not OutOfBounds or Runtime
throw new AMQPOutOfRangeException(sprintf('Protocol version %s not implemented.', $this->protocolVersion));
}
}
/**
* @return string
* @throws AMQPOutOfRangeException
*/
public static function getProtocolVersion()
{
$protocol = defined('AMQP_PROTOCOL') ? AMQP_PROTOCOL : self::PROTOCOL_091;
//adding check here to catch unknown protocol ASAP, as this method may be called from the outside
if (!in_array($protocol, array(self::PROTOCOL_080, self::PROTOCOL_091), TRUE)) {
throw new AMQPOutOfRangeException(sprintf('Protocol version %s not implemented.', $protocol));
}
return $protocol;
}
/**
* @return string
*/
public function getChannelId()
{
return $this->channel_id;
}
public function setBodySizeLimit($max_bytes)
{
$max_bytes = intval($max_bytes);
if ( $max_bytes > 0 ) {
$this->body_size_max = $max_bytes;
} else {
$this->body_size_max = null;
}
}
/**
* @return AbstractConnection
*/
public function getConnection()
{
return $this->connection;
}
/**
* @return array
*/
public function getMethodQueue()
{
return $this->method_queue;
}
/**
* @param string $method_sig
* @param string $args
* @param $content
* @return mixed
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
*/
public function dispatch($method_sig, $args, $content)
{
if (!$this->methodMap->valid_method($method_sig)) {
throw new AMQPRuntimeException(sprintf(
'Unknown AMQP method "%s"',
$method_sig
));
}
$amqp_method = $this->methodMap->get_method($method_sig);
if (!method_exists($this, $amqp_method)) {
throw new AMQPRuntimeException(sprintf(
'Method: "%s" not implemented by class: %s',
$amqp_method,
get_class($this)
));
}
$this->dispatch_reader->reuse($args);
if ($content == null) {
return call_user_func(array($this, $amqp_method), $this->dispatch_reader);
}
return call_user_func(array($this, $amqp_method), $this->dispatch_reader, $content);
}
/**
* @param int $timeout
* @return array|mixed
*/
public function next_frame($timeout = 0)
{
if ($this->debug) {
MiscHelper::debug_msg('waiting for a new frame');
}
if (!empty($this->frame_queue)) {
return array_shift($this->frame_queue);
}
return $this->connection->wait_channel($this->channel_id, $timeout);
}
/**
* @param $method_sig
* @param string $args
*/
protected function send_method_frame($method_sig, $args = '')
{
$this->connection->send_channel_method_frame($this->channel_id, $method_sig, $args);
}
/**
* This is here for performance reasons to batch calls to fwrite from basic.publish
*
* @param $method_sig
* @param string $args
* @param null $pkt
* @return null|\PhpAmqpLib\Wire\AMQPWriter
*/
protected function prepare_method_frame($method_sig, $args = '', $pkt = null)
{
return $this->connection->prepare_channel_method_frame($this->channel_id, $method_sig, $args, $pkt);
}
/**
* @return AMQPMessage
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
*/
public function wait_content()
{
$frm = $this->next_frame();
$frame_type = $frm[0];
$payload = $frm[1];
if ($frame_type != 2) {
throw new AMQPRuntimeException('Expecting Content header');
}
$this->wait_content_reader->reuse(mb_substr($payload, 0, 12, 'ASCII'));
// $payload_reader = new AMQPReader(substr($payload,0,12));
$class_id = $this->wait_content_reader->read_short();
$weight = $this->wait_content_reader->read_short();
$body_size = $this->wait_content_reader->read_longlong();
//hack to avoid creating new instances of AMQPReader;
$this->msg_property_reader->reuse(mb_substr($payload, 12, mb_strlen($payload, 'ASCII') - 12, 'ASCII'));
$msg = new AMQPMessage();
$msg->load_properties($this->msg_property_reader);
$msg->body_size = $body_size;
$body_parts = array();
$body_received = 0;
while (bccomp($body_size, $body_received, 0) == 1) {
$frm = $this->next_frame();
$frame_type = $frm[0];
$payload = $frm[1];
if ($frame_type != 3) {
$PROTOCOL_CONSTANTS_CLASS = self::$PROTOCOL_CONSTANTS_CLASS;
throw new AMQPRuntimeException(sprintf(
'Expecting Content body, received frame type %s (%s)',
$frame_type,
$PROTOCOL_CONSTANTS_CLASS::$FRAME_TYPES[$frame_type]
));
}
$body_received = bcadd($body_received, mb_strlen($payload, 'ASCII'), 0);
if ( ! is_null($this->body_size_max) && $body_received > $this->body_size_max ) {
$msg->is_truncated = true;
continue;
}
$body_parts[] = $payload;
}
$msg->body = implode('', $body_parts);
if ($this->auto_decode && isset($msg->content_encoding)) {
try {
$msg->body = $msg->body->decode($msg->content_encoding);
} catch (\Exception $e) {
if ($this->debug) {
MiscHelper::debug_msg('Ignoring body decoding exception: ' . $e->getMessage());
}
}
}
return $msg;
}
/**
* Wait for some expected AMQP methods and dispatch to them.
* Unexpected methods are queued up for later calls to this PHP
* method.
*
* @param array $allowed_methods
* @param bool $non_blocking
* @param int $timeout
* @throws \PhpAmqpLib\Exception\AMQPOutOfBoundsException
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
* @return mixed
*/
public function wait($allowed_methods = null, $non_blocking = false, $timeout = 0)
{
$PROTOCOL_CONSTANTS_CLASS = self::$PROTOCOL_CONSTANTS_CLASS;
if ($allowed_methods && $this->debug) {
MiscHelper::debug_msg('waiting for ' . implode(', ', $allowed_methods));
} elseif ($this->debug) {
MiscHelper::debug_msg('waiting for any method');
}
//Process deferred methods
foreach ($this->method_queue as $qk => $queued_method) {
if ($this->debug) {
MiscHelper::debug_msg('checking queue method ' . $qk);
}
$method_sig = $queued_method[0];
if ($allowed_methods == null || in_array($method_sig, $allowed_methods)) {
unset($this->method_queue[$qk]);
if ($this->debug) {
MiscHelper::debug_msg(sprintf(
'Executing queued method: %s: %s',
$method_sig,
$PROTOCOL_CONSTANTS_CLASS::$GLOBAL_METHOD_NAMES[MiscHelper::methodSig($method_sig)]
));
}
return $this->dispatch($queued_method[0], $queued_method[1], $queued_method[2]);
}
}
// No deferred methods? wait for new ones
while (true) {
$frm = $this->next_frame($timeout);
$frame_type = $frm[0];
$payload = $frm[1];
if ($frame_type != 1) {
throw new AMQPRuntimeException(sprintf(
'Expecting AMQP method, received frame type: %s (%s)',
$frame_type,
$PROTOCOL_CONSTANTS_CLASS::$FRAME_TYPES[$frame_type]
));
}
if (mb_strlen($payload, 'ASCII') < 4) {
throw new AMQPOutOfBoundsException('Method frame too short');
}
$method_sig_array = unpack('n2', mb_substr($payload, 0, 4, 'ASCII'));
$method_sig = '' . $method_sig_array[1] . ',' . $method_sig_array[2];
$args = mb_substr($payload, 4, mb_strlen($payload, 'ASCII') - 4, 'ASCII');
if ($this->debug) {
MiscHelper::debug_msg(sprintf(
'> %s: %s',
$method_sig,
$PROTOCOL_CONSTANTS_CLASS::$GLOBAL_METHOD_NAMES[MiscHelper::methodSig($method_sig)]
));
}
if (in_array($method_sig, $PROTOCOL_CONSTANTS_CLASS::$CONTENT_METHODS)) {
$content = $this->wait_content();
} else {
$content = null;
}
if ($allowed_methods == null ||
in_array($method_sig, $allowed_methods) ||
in_array($method_sig, $PROTOCOL_CONSTANTS_CLASS::$CLOSE_METHODS)
) {
return $this->dispatch($method_sig, $args, $content);
}
// Wasn't what we were looking for? save it for later
if ($this->debug) {
MiscHelper::debug_msg('Queueing for later: $method_sig: '
. $PROTOCOL_CONSTANTS_CLASS::$GLOBAL_METHOD_NAMES[MiscHelper::methodSig($method_sig)]);
}
$this->method_queue[] = array($method_sig, $args, $content);
if ($non_blocking) {
break;
}
}
}
/**
* @param $handler
* @param array $arguments
*/
protected function dispatch_to_handler($handler, array $arguments)
{
if (is_callable($handler)) {
call_user_func_array($handler, $arguments);
}
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace PhpAmqpLib\Connection;
/**
* Class AMQPConnection
*
* Kept for BC
*
* @deprecated
*/
class AMQPConnection extends AMQPStreamConnection
{
}

View File

@@ -0,0 +1,49 @@
<?php
namespace PhpAmqpLib\Connection;
class AMQPLazyConnection extends AMQPConnection
{
/**
* Gets socket from current connection
*
* @deprecated
*/
public function getSocket()
{
$this->connect();
return parent::getSocket();
}
/**
* {@inheritdoc}
*/
public function channel($channel_id = null)
{
$this->connect();
return parent::channel($channel_id);
}
/**
* @return null|\PhpAmqpLib\Wire\IO\AbstractIO
*/
protected function getIO()
{
if (!$this->io) {
$this->connect();
}
return $this->io;
}
/**
* Should the connection be attempted during construction?
*
* @return bool
*/
public function connectOnConstruct()
{
return false;
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace PhpAmqpLib\Connection;
class AMQPSSLConnection extends AMQPStreamConnection
{
/**
* @param AbstractConnection $host
* @param int $port
* @param string $user
* @param bool $password
* @param string $vhost
* @param array $ssl_options
* @param array $options
*/
public function __construct(
$host,
$port,
$user,
$password,
$vhost = '/',
$ssl_options = array(),
$options = array()
) {
$ssl_context = empty($ssl_options) ? null : $this->create_ssl_context($ssl_options);
parent::__construct(
$host,
$port,
$user,
$password,
$vhost,
isset($options['insist']) ? $options['insist'] : false,
isset($options['login_method']) ? $options['login_method'] : 'AMQPLAIN',
isset($options['login_response']) ? $options['login_response'] : null,
isset($options['locale']) ? $options['locale'] : 'en_US',
isset($options['connection_timeout']) ? $options['connection_timeout'] : 3,
isset($options['read_write_timeout']) ? $options['read_write_timeout'] : 3,
$ssl_context,
isset($options['keepalive']) ? $options['keepalive'] : false,
isset($options['heartbeat']) ? $options['heartbeat'] : 0
);
}
/**
* @param $options
* @return resource
*/
private function create_ssl_context($options)
{
$ssl_context = stream_context_create();
foreach ($options as $k => $v) {
stream_context_set_option($ssl_context, 'ssl', $k, $v);
}
return $ssl_context;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace PhpAmqpLib\Connection;
use PhpAmqpLib\Wire\IO\SocketIO;
class AMQPSocketConnection extends AbstractConnection
{
/**
* @param AbstractConnection $host
* @param int $port
* @param string $user
* @param bool $password
* @param string $vhost
* @param bool $insist
* @param string $login_method
* @param null $login_response
* @param string $locale
* @param int $timeout
* @param bool $keepalive
*/
public function __construct(
$host,
$port,
$user,
$password,
$vhost = '/',
$insist = false,
$login_method = 'AMQPLAIN',
$login_response = null,
$locale = 'en_US',
$timeout = 3,
$keepalive = false
) {
$io = new SocketIO($host, $port, $timeout, $keepalive);
parent::__construct($user, $password, $vhost, $insist, $login_method, $login_response, $locale, $io);
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace PhpAmqpLib\Connection;
use PhpAmqpLib\Wire\IO\StreamIO;
class AMQPStreamConnection extends AbstractConnection
{
/**
* @param AbstractConnection $host
* @param string $port
* @param string $user
* @param bool $password
* @param string $vhost
* @param bool $insist
* @param string $login_method
* @param null $login_response
* @param string $locale
* @param int $connection_timeout
* @param int $read_write_timeout
* @param null $context
* @param bool $keepalive
*/
public function __construct(
$host,
$port,
$user,
$password,
$vhost = '/',
$insist = false,
$login_method = 'AMQPLAIN',
$login_response = null,
$locale = 'en_US',
$connection_timeout = 3,
$read_write_timeout = 3,
$context = null,
$keepalive = false,
$heartbeat = 0
) {
$io = new StreamIO($host, $port, $connection_timeout, $read_write_timeout, $context, $keepalive, $heartbeat);
parent::__construct($user, $password, $vhost, $insist, $login_method, $login_response, $locale, $io, $heartbeat);
// save the params for the use of __clone, this will overwrite the parent
$this->construct_params = func_get_args();
}
}

View File

@@ -0,0 +1,972 @@
<?php
namespace PhpAmqpLib\Connection;
use PhpAmqpLib\Channel\AbstractChannel;
use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Exception\AMQPProtocolConnectionException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Exception\AMQPTimeoutException;
use PhpAmqpLib\Helper\MiscHelper;
use PhpAmqpLib\Wire\AMQPReader;
use PhpAmqpLib\Wire\AMQPWriter;
use PhpAmqpLib\Wire\IO\AbstractIO;
use PhpAmqpLib\Wire\IO\SocketIO;
class AbstractConnection extends AbstractChannel
{
/** @var array */
public static $LIBRARY_PROPERTIES = array(
'product' => array('S', 'AMQPLib'),
'platform' => array('S', 'PHP'),
'version' => array('S', '2.4'),
'information' => array('S', ''),
'copyright' => array('S', ''),
'capabilities' => array(
'F',
array(
'authentication_failure_close' => array('t', true),
'publisher_confirms' => array('t', true),
'consumer_cancel_notify' => array('t', true),
'exchange_exchange_bindings' => array('t', true),
'basic.nack' => array('t', true),
'connection.blocked' => array('t', true)
)
)
);
/** @var AMQPChannel[] */
public $channels = array();
/** @var int */
protected $version_major;
/** @var int */
protected $version_minor;
/** @var array */
protected $server_properties;
/** @var array */
protected $mechanisms;
/** @var array */
protected $locales;
/** @var bool */
protected $wait_tune_ok;
/** @var string */
protected $known_hosts;
/** @var AMQPReader */
protected $input;
/** @var string */
protected $vhost;
/** @var bool */
protected $insist;
/** @var string */
protected $login_method;
/** @var AMQPWriter */
protected $login_response;
/** @var string */
protected $locale;
/** @var int */
protected $heartbeat;
/** @var SocketIO */
protected $sock;
/** @var int */
protected $channel_max = 65535;
/** @var int */
protected $frame_max = 131072;
/** @var array Constructor parameters for clone */
protected $construct_params;
/** @var bool Close the connection in destructor */
protected $close_on_destruct = true;
/** @var bool Maintain connection status */
protected $is_connected = false;
/** @var \PhpAmqpLib\Wire\IO\AbstractIO */
protected $io;
/** @var \PhpAmqpLib\Wire\AMQPReader */
protected $wait_frame_reader;
/** @var callable Handles connection blocking from the server */
private $connection_block_handler;
/** @var callable Handles connection unblocking from the server */
private $connection_unblock_handler;
/**
* Circular buffer to speed up prepare_content().
* Max size limited by $prepare_content_cache_max_size.
*
* @var array
* @see prepare_content()
*/
private $prepare_content_cache;
/** @var int Maximal size of $prepare_content_cache */
private $prepare_content_cache_max_size;
/**
* @param AbstractConnection $user
* @param string $password
* @param string $vhost
* @param bool $insist
* @param string $login_method
* @param null $login_response
* @param string $locale
* @param AbstractIO $io
* @param int $heartbeat
* @throws \Exception
*/
public function __construct(
$user,
$password,
$vhost = '/',
$insist = false,
$login_method = 'AMQPLAIN',
$login_response = null,
$locale = 'en_US',
AbstractIO $io,
$heartbeat = 0
) {
// save the params for the use of __clone
$this->construct_params = func_get_args();
$this->wait_frame_reader = new AMQPReader(null);
$this->vhost = $vhost;
$this->insist = $insist;
$this->login_method = $login_method;
$this->login_response = $login_response;
$this->locale = $locale;
$this->io = $io;
$this->heartbeat = $heartbeat;
if ($user && $password) {
$this->login_response = new AMQPWriter();
$this->login_response->write_table(array(
'LOGIN' => array('S', $user),
'PASSWORD' => array('S', $password)
));
// Skip the length
$responseValue = $this->login_response->getvalue();
$this->login_response = mb_substr($responseValue, 4, mb_strlen($responseValue, 'ASCII') - 4, 'ASCII');
} else {
$this->login_response = null;
}
$this->prepare_content_cache = array();
$this->prepare_content_cache_max_size = 100;
// Lazy Connection waits on connecting
if ($this->connectOnConstruct()) {
$this->connect();
}
}
/**
* Connects to the AMQP server
*/
protected function connect()
{
try {
// Loop until we connect
while (!$this->isConnected()) {
// Assume we will connect, until we dont
$this->setIsConnected(true);
// Connect the socket
$this->getIO()->connect();
$this->channels = array();
// The connection object itself is treated as channel 0
parent::__construct($this, 0);
$this->input = new AMQPReader(null, $this->getIO());
$this->write($this->amqp_protocol_header);
$this->wait(array($this->waitHelper->get_wait('connection.start')));
$this->x_start_ok(self::$LIBRARY_PROPERTIES, $this->login_method, $this->login_response, $this->locale);
$this->wait_tune_ok = true;
while ($this->wait_tune_ok) {
$this->wait(array(
$this->waitHelper->get_wait('connection.secure'),
$this->waitHelper->get_wait('connection.tune')
));
}
$host = $this->x_open($this->vhost, '', $this->insist);
if (!$host) {
return; // we weren't redirected
}
$this->setIsConnected(false);
$this->closeChannels();
// we were redirected, close the socket, loop and try again
$this->close_socket();
}
} catch (\Exception $e) {
// Something went wrong, set the connection status
$this->setIsConnected(false);
$this->closeChannels();
throw $e; // Rethrow exception
}
}
/**
* Reconnects using the original connection settings.
* This will not recreate any channels that were established previously
*/
public function reconnect()
{
// Try to close the AMQP connection
$this->safeClose();
// Reconnect the socket/stream then AMQP
$this->getIO()->reconnect();
$this->setIsConnected(false); // getIO can initiate the connection setting via LazyConnection, set it here to be sure
$this->connect();
}
/**
* Cloning will use the old properties to make a new connection to the same server
*/
public function __clone()
{
call_user_func_array(array($this, '__construct'), $this->construct_params);
}
public function __destruct()
{
if ($this->close_on_destruct) {
$this->safeClose();
}
}
/**
* Attempts to close the connection safely
*/
protected function safeClose()
{
try {
if (isset($this->input) && $this->input) {
$this->close();
}
} catch (\Exception $e) {
// Nothing here
}
}
/**
* @param int $sec
* @param int $usec
* @return mixed
*/
public function select($sec, $usec = 0)
{
return $this->getIO()->select($sec, $usec);
}
/**
* Allows to not close the connection
* it's useful after the fork when you don't want to close parent process connection
*
* @param bool $close
*/
public function set_close_on_destruct($close = true)
{
$this->close_on_destruct = (bool) $close;
}
protected function close_input()
{
if ($this->debug) {
MiscHelper::debug_msg('closing input');
}
if (!is_null($this->input)) {
$this->input->close();
$this->input = null;
}
}
protected function close_socket()
{
if ($this->debug) {
MiscHelper::debug_msg('closing socket');
}
if (!is_null($this->getIO())) {
$this->getIO()->close();
}
}
/**
* @param $data
*/
public function write($data)
{
if ($this->debug) {
MiscHelper::debug_msg(sprintf(
'< [hex]: %s%s',
PHP_EOL,
MiscHelper::hexdump($data, $htmloutput = false, $uppercase = true, $return = true)
));
}
try {
$this->getIO()->write($data);
} catch (AMQPRuntimeException $e) {
$this->setIsConnected(false);
throw $e;
}
}
protected function do_close()
{
$this->setIsConnected(false);
$this->close_input();
$this->close_socket();
}
/**
* @return int
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
*/
public function get_free_channel_id()
{
for ($i = 1; $i <= $this->channel_max; $i++) {
if (!isset($this->channels[$i])) {
return $i;
}
}
throw new AMQPRuntimeException('No free channel ids');
}
/**
* @param string $channel
* @param int $class_id
* @param int $weight
* @param int $body_size
* @param string $packed_properties
* @param string $body
* @param AMQPWriter $pkt
*/
public function send_content($channel, $class_id, $weight, $body_size, $packed_properties, $body, $pkt = null)
{
$this->prepare_content($channel, $class_id, $weight, $body_size, $packed_properties, $body, $pkt);
$this->write($pkt->getvalue());
}
/**
* Returns a new AMQPWriter or mutates the provided $pkt
*
* @param string $channel
* @param int $class_id
* @param int $weight
* @param int $body_size
* @param string $packed_properties
* @param string $body
* @param AMQPWriter $pkt
* @return AMQPWriter
*/
public function prepare_content($channel, $class_id, $weight, $body_size, $packed_properties, $body, $pkt = null)
{
if (empty($pkt)) {
$pkt = new AMQPWriter();
}
// Content already prepared ?
$key_cache = sprintf(
'%s|%s|%s|%s',
$channel,
$packed_properties,
$class_id,
$weight
);
if (!isset($this->prepare_content_cache[$key_cache])) {
$w = new AMQPWriter();
$w->write_octet(2);
$w->write_short($channel);
$w->write_long(mb_strlen($packed_properties, 'ASCII') + 12);
$w->write_short($class_id);
$w->write_short($weight);
$this->prepare_content_cache[$key_cache] = $w->getvalue();
if (count($this->prepare_content_cache) > $this->prepare_content_cache_max_size) {
reset($this->prepare_content_cache);
$old_key = key($this->prepare_content_cache);
unset($this->prepare_content_cache[$old_key]);
}
}
$pkt->write($this->prepare_content_cache[$key_cache]);
$pkt->write_longlong($body_size);
$pkt->write($packed_properties);
$pkt->write_octet(0xCE);
// memory efficiency: walk the string instead of biting it. good for very large packets (close in size to memory_limit setting)
$position = 0;
$bodyLength = mb_strlen($body,'ASCII');
while ($position <= $bodyLength) {
$payload = mb_substr($body, $position, $this->frame_max - 8, 'ASCII');
$position += $this->frame_max - 8;
$pkt->write_octet(3);
$pkt->write_short($channel);
$pkt->write_long(mb_strlen($payload, 'ASCII'));
$pkt->write($payload);
$pkt->write_octet(0xCE);
}
return $pkt;
}
/**
* @param $channel
* @param $method_sig
* @param string $args
* @param null $pkt
*/
protected function send_channel_method_frame($channel, $method_sig, $args = '', $pkt = null)
{
$pkt = $this->prepare_channel_method_frame($channel, $method_sig, $args, $pkt);
$this->write($pkt->getvalue());
if ($this->debug) {
$PROTOCOL_CONSTANTS_CLASS = self::$PROTOCOL_CONSTANTS_CLASS;
MiscHelper::debug_msg(sprintf(
'< %s: %s',
MiscHelper::methodSig($method_sig),
$PROTOCOL_CONSTANTS_CLASS::$GLOBAL_METHOD_NAMES[MiscHelper::methodSig($method_sig)]
));
}
}
/**
* Returns a new AMQPWriter or mutates the provided $pkt
*
* @param $channel
* @param $method_sig
* @param string $args
* @param AMQPWriter $pkt
* @return null|AMQPWriter
*/
protected function prepare_channel_method_frame($channel, $method_sig, $args = '', $pkt = null)
{
if ($args instanceof AMQPWriter) {
$args = $args->getvalue();
}
if (empty($pkt)) {
$pkt = new AMQPWriter();
}
$pkt->write_octet(1);
$pkt->write_short($channel);
$pkt->write_long(mb_strlen($args, 'ASCII') + 4); // 4 = length of class_id and method_id
// in payload
$pkt->write_short($method_sig[0]); // class_id
$pkt->write_short($method_sig[1]); // method_id
$pkt->write($args);
$pkt->write_octet(0xCE);
if ($this->debug) {
$PROTOCOL_CONSTANTS_CLASS = self::$PROTOCOL_CONSTANTS_CLASS;
MiscHelper::debug_msg(sprintf(
'< %s: %s',
MiscHelper::methodSig($method_sig),
$PROTOCOL_CONSTANTS_CLASS::$GLOBAL_METHOD_NAMES[MiscHelper::methodSig($method_sig)]
));
}
return $pkt;
}
/**
* Waits for a frame from the server
*
* @param int $timeout
* @return array
* @throws \Exception
* @throws \PhpAmqpLib\Exception\AMQPTimeoutException
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
*/
protected function wait_frame($timeout = 0)
{
$currentTimeout = $this->input->getTimeout();
$this->input->setTimeout($timeout);
try {
// frame_type + channel_id + size
$this->wait_frame_reader->reuse(
$this->input->read(AMQPReader::OCTET + AMQPReader::SHORT + AMQPReader::LONG)
);
$frame_type = $this->wait_frame_reader->read_octet();
$channel = $this->wait_frame_reader->read_short();
$size = $this->wait_frame_reader->read_long();
// payload + ch
$this->wait_frame_reader->reuse($this->input->read(AMQPReader::OCTET + (int) $size));
$payload = $this->wait_frame_reader->read($size);
$ch = $this->wait_frame_reader->read_octet();
} catch (AMQPTimeoutException $e) {
$this->input->setTimeout($currentTimeout);
throw $e;
}
$this->input->setTimeout($currentTimeout);
if ($ch != 0xCE) {
throw new AMQPRuntimeException(sprintf(
'Framing error, unexpected byte: %x',
$ch
));
}
return array($frame_type, $channel, $payload);
}
/**
* Waits for a frame from the server destined for a particular channel.
*
* @param string $channel_id
* @param int $timeout
* @return array
*/
protected function wait_channel($channel_id, $timeout = 0)
{
while (true) {
list($frame_type, $frame_channel, $payload) = $this->wait_frame($timeout);
if ($frame_channel === 0 && $frame_type === 8) {
// skip heartbeat frames
continue;
} else {
if ($frame_channel == $channel_id) {
return array($frame_type, $payload);
}
// Not the channel we were looking for. Queue this frame
//for later, when the other channel is looking for frames.
array_push($this->channels[$frame_channel]->frame_queue, array($frame_type, $payload));
// If we just queued up a method for channel 0 (the Connection
// itself) it's probably a close method in reaction to some
// error, so deal with it right away.
if (($frame_type == 1) && ($frame_channel == 0)) {
$this->wait();
}
}
}
}
/**
* Fetches a channel object identified by the numeric channel_id, or
* create that object if it doesn't already exist.
*
* @param string $channel_id
* @return AMQPChannel
*/
public function channel($channel_id = null)
{
if (isset($this->channels[$channel_id])) {
return $this->channels[$channel_id];
} else {
$channel_id = $channel_id ? $channel_id : $this->get_free_channel_id();
$ch = new AMQPChannel($this->connection, $channel_id);
$this->channels[$channel_id] = $ch;
return $ch;
}
}
/**
* Requests a connection close
*
* @param int $reply_code
* @param string $reply_text
* @param array $method_sig
* @return mixed|null
*/
public function close($reply_code = 0, $reply_text = '', $method_sig = array(0, 0))
{
if (!$this->protocolWriter || !$this->isConnected()) {
return null;
}
$this->closeChannels();
list($class_id, $method_id, $args) = $this->protocolWriter->connectionClose(
$reply_code,
$reply_text,
$method_sig[0],
$method_sig[1]
);
$this->send_method_frame(array($class_id, $method_id), $args);
$this->setIsConnected(false);
return $this->wait(array(
$this->waitHelper->get_wait('connection.close_ok')
));
}
/**
* @param $table
* @return string
*/
public static function dump_table($table)
{
$tokens = array();
foreach ($table as $name => $value) {
switch ($value[0]) {
case 'D':
$val = $value[1]->n . 'E' . $value[1]->e;
break;
case 'F':
$val = '(' . self::dump_table($value[1]) . ')';
break;
case 'T':
$val = date('Y-m-d H:i:s', $value[1]);
break;
default:
$val = $value[1];
}
$tokens[] = $name . '=' . $val;
}
return implode(', ', $tokens);
}
/**
* @param AMQPReader $args
* @throws \PhpAmqpLib\Exception\AMQPProtocolConnectionException
*/
protected function connection_close($args)
{
$reply_code = $args->read_short();
$reply_text = $args->read_shortstr();
$class_id = $args->read_short();
$method_id = $args->read_short();
$this->x_close_ok();
throw new AMQPProtocolConnectionException($reply_code, $reply_text, array($class_id, $method_id));
}
/**
* Confirms a connection close
*/
protected function x_close_ok()
{
$this->send_method_frame(
explode(',', $this->waitHelper->get_wait('connection.close_ok'))
);
$this->do_close();
}
/**
* Confirm a connection close
*/
protected function connection_close_ok($args)
{
$this->do_close();
}
/**
* @param string $virtual_host
* @param string $capabilities
* @param bool $insist
* @return mixed
*/
protected function x_open($virtual_host, $capabilities = '', $insist = false)
{
$args = new AMQPWriter();
$args->write_shortstr($virtual_host);
$args->write_shortstr($capabilities);
$args->write_bits(array($insist));
$this->send_method_frame(array(10, 40), $args);
$wait = array(
$this->waitHelper->get_wait('connection.open_ok')
);
if ($this->protocolVersion == '0.8') {
$wait[] = $this->waitHelper->get_wait('connection.redirect');
}
return $this->wait($wait);
}
/**
* Signals that the connection is ready
*
* @param AMQPReader $args
*/
protected function connection_open_ok($args)
{
$this->known_hosts = $args->read_shortstr();
if ($this->debug) {
MiscHelper::debug_msg('Open OK! known_hosts: ' . $this->known_hosts);
}
}
/**
* Asks the client to use a different server
*
* @param AMQPReader $args
* @return string
*/
protected function connection_redirect($args)
{
$host = $args->read_shortstr();
$this->known_hosts = $args->read_shortstr();
if ($this->debug) {
MiscHelper::debug_msg(sprintf(
'Redirected to [%s], known_hosts [%s]',
$host,
$this->known_hosts
));
}
return $host;
}
/**
* Security mechanism challenge
*
* @param AMQPReader $args
*/
protected function connection_secure($args)
{
$challenge = $args->read_longstr();
}
/**
* Security mechanism response
*/
protected function x_secure_ok($response)
{
$args = new AMQPWriter();
$args->write_longstr($response);
$this->send_method_frame(array(10, 21), $args);
}
/**
* Starts connection negotiation
*
* @param AMQPReader $args
*/
protected function connection_start($args)
{
$this->version_major = $args->read_octet();
$this->version_minor = $args->read_octet();
$this->server_properties = $args->read_table();
$this->mechanisms = explode(' ', $args->read_longstr());
$this->locales = explode(' ', $args->read_longstr());
if ($this->debug) {
MiscHelper::debug_msg(sprintf(
'Start from server, version: %d.%d, properties: %s, mechanisms: %s, locales: %s',
$this->version_major,
$this->version_minor,
self::dump_table($this->server_properties),
implode(', ', $this->mechanisms),
implode(', ', $this->locales)
));
}
}
/**
* @param $client_properties
* @param $mechanism
* @param $response
* @param $locale
*/
protected function x_start_ok($client_properties, $mechanism, $response, $locale)
{
$args = new AMQPWriter();
$args->write_table($client_properties);
$args->write_shortstr($mechanism);
$args->write_longstr($response);
$args->write_shortstr($locale);
$this->send_method_frame(array(10, 11), $args);
}
/**
* Proposes connection tuning parameters
*
* @param AMQPReader $args
*/
protected function connection_tune($args)
{
$v = $args->read_short();
if ($v) {
$this->channel_max = $v;
}
$v = $args->read_long();
if ($v) {
$this->frame_max = $v;
}
// use server proposed value if not set
if ($this->heartbeat === null) {
$this->heartbeat = $args->read_short();
}
$this->x_tune_ok($this->channel_max, $this->frame_max, $this->heartbeat);
}
/**
* Negotiates connection tuning parameters
*
* @param $channel_max
* @param $frame_max
* @param $heartbeat
*/
protected function x_tune_ok($channel_max, $frame_max, $heartbeat)
{
$args = new AMQPWriter();
$args->write_short($channel_max);
$args->write_long($frame_max);
$args->write_short($heartbeat);
$this->send_method_frame(array(10, 31), $args);
$this->wait_tune_ok = false;
}
/**
* @return SocketIO
*/
public function getSocket()
{
return $this->io->getSocket();
}
/**
* @return \PhpAmqpLib\Wire\IO\AbstractIO
*/
protected function getIO()
{
return $this->io;
}
/**
* Handles connection blocked notifications
*
* @param AMQPReader $args
*/
protected function connection_blocked(AMQPReader $args)
{
// Call the block handler and pass in the reason
$this->dispatch_to_handler($this->connection_block_handler, array($args->read_shortstr()));
}
/**
* Handles connection unblocked notifications
*/
protected function connection_unblocked(AMQPReader $args)
{
// No args to an unblock event
$this->dispatch_to_handler($this->connection_unblock_handler, array());
}
/**
* Sets a handler which is called whenever a connection.block is sent from the server
*
* @param callable $callback
*/
public function set_connection_block_handler($callback)
{
$this->connection_block_handler = $callback;
}
/**
* Sets a handler which is called whenever a connection.block is sent from the server
*
* @param callable $callback
*/
public function set_connection_unblock_handler($callback)
{
$this->connection_unblock_handler = $callback;
}
/**
* Gets the connection status
*
* @return bool
*/
public function isConnected()
{
return $this->is_connected;
}
/**
* Set the connection status
*
* @param bool $is_connected
*/
protected function setIsConnected($is_connected)
{
$this->is_connected = $is_connected;
}
/**
* Closes all available channels
*/
protected function closeChannels()
{
foreach ($this->channels as $key => $channel) {
// channels[0] is this connection object, so don't close it yet
if ($key === 0) {
continue;
}
try {
$channel->close();
} catch (\Exception $e) {
/* Ignore closing errors */
}
}
}
/**
* Should the connection be attempted during construction?
*
* @return bool
*/
public function connectOnConstruct()
{
return true;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace PhpAmqpLib\Exception;
class AMQPBasicCancelException extends \Exception implements AMQPExceptionInterface
{
/** @var string */
public $consumerTag;
/**
* @param string $consumerTag
*/
public function __construct($consumerTag)
{
$this->consumerTag = $consumerTag;
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace PhpAmqpLib\Exception;
/**
* @deprecated use AMQPProtocolChannelException instead
*/
class AMQPChannelException extends AMQPException
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace PhpAmqpLib\Exception;
/**
* @deprecated use AMQPProtocolConnectionException instead
*/
class AMQPConnectionException extends AMQPException
{
}

View File

@@ -0,0 +1,46 @@
<?php
namespace PhpAmqpLib\Exception;
//TODO refactor usage of static methods
use PhpAmqpLib\Channel\AbstractChannel;
use PhpAmqpLib\Helper\MiscHelper;
/**
* @deprecated use AMQPProtocolException instead
*/
class AMQPException extends \Exception
{
/** @var string */
public $amqp_reply_code;
/** @var int */
public $amqp_reply_text;
/** @var \Exception */
public $amqp_method_sig;
/** @var array */
public $args;
/**
* @param string $reply_code
* @param int $reply_text
* @param \Exception $method_sig
*/
public function __construct($reply_code, $reply_text, $method_sig)
{
parent::__construct($reply_text, $reply_code);
$this->amqp_reply_code = $reply_code; // redundant, but kept for BC
$this->amqp_reply_text = $reply_text; // redundant, but kept for BC
$this->amqp_method_sig = $method_sig;
$ms = MiscHelper::methodSig($method_sig);
$PROTOCOL_CONSTANTS_CLASS = AbstractChannel::$PROTOCOL_CONSTANTS_CLASS;
$mn = isset($PROTOCOL_CONSTANTS_CLASS::$GLOBAL_METHOD_NAMES[$ms])
? $PROTOCOL_CONSTANTS_CLASS::$GLOBAL_METHOD_NAMES[$ms]
: $mn = '';
$this->args = array($reply_code, $reply_text, $method_sig, $mn);
}
}

View File

@@ -0,0 +1,6 @@
<?php
namespace PhpAmqpLib\Exception;
interface AMQPExceptionInterface
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace PhpAmqpLib\Exception;
class AMQPIOException extends \Exception implements AMQPExceptionInterface
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace PhpAmqpLib\Exception;
class AMQPInvalidArgumentException extends \RuntimeException implements AMQPExceptionInterface
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace PhpAmqpLib\Exception;
class AMQPLogicException extends \LogicException implements AMQPExceptionInterface
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace PhpAmqpLib\Exception;
class AMQPOutOfBoundsException extends \OutOfBoundsException implements AMQPExceptionInterface
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace PhpAmqpLib\Exception;
class AMQPOutOfRangeException extends \OutOfRangeException implements AMQPExceptionInterface
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace PhpAmqpLib\Exception;
class AMQPProtocolChannelException extends AMQPProtocolException
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace PhpAmqpLib\Exception;
class AMQPProtocolConnectionException extends AMQPProtocolException
{
}

View File

@@ -0,0 +1,44 @@
<?php
namespace PhpAmqpLib\Exception;
//TODO refactor usage of static methods
use PhpAmqpLib\Channel\AbstractChannel;
use PhpAmqpLib\Helper\MiscHelper;
class AMQPProtocolException extends \Exception implements AMQPExceptionInterface
{
/** @var string */
public $amqp_reply_code;
/** @var int */
public $amqp_reply_text;
/** @var \Exception */
public $amqp_method_sig;
/** @var array */
public $args;
/**
* @param string $reply_code
* @param int $reply_text
* @param \Exception $method_sig
*/
public function __construct($reply_code, $reply_text, $method_sig)
{
parent::__construct($reply_text, $reply_code);
$this->amqp_reply_code = $reply_code; // redundant, but kept for BC
$this->amqp_reply_text = $reply_text; // redundant, but kept for BC
$this->amqp_method_sig = $method_sig;
$ms = MiscHelper::methodSig($method_sig);
$PROTOCOL_CONSTANTS_CLASS = AbstractChannel::$PROTOCOL_CONSTANTS_CLASS;
$mn = isset($PROTOCOL_CONSTANTS_CLASS::$GLOBAL_METHOD_NAMES[$ms])
? $PROTOCOL_CONSTANTS_CLASS::$GLOBAL_METHOD_NAMES[$ms]
: $mn = '';
$this->args = array($reply_code, $reply_text, $method_sig, $mn);
}
}

View File

@@ -0,0 +1,6 @@
<?php
namespace PhpAmqpLib\Exception;
class AMQPRuntimeException extends \RuntimeException implements AMQPExceptionInterface
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace PhpAmqpLib\Exception;
class AMQPTimeoutException extends \RuntimeException implements AMQPExceptionInterface
{
}

View File

@@ -0,0 +1,126 @@
<?php
namespace PhpAmqpLib\Helper;
class MiscHelper
{
public static function debug_msg($s)
{
echo $s . PHP_EOL;
}
/**
* @param $a
* @return string
*/
public static function methodSig($a)
{
if (is_string($a)) {
return $a;
} else {
return sprintf('%d,%d', $a[0], $a[1]);
}
}
/**
* @param $bytes
*/
public static function saveBytes($bytes)
{
$fh = fopen('/tmp/bytes', 'wb');
fwrite($fh, $bytes);
fclose($fh);
}
/**
* Gets a number (either int or float) and returns an array containing its integer part as first element and its
* decimal part mutliplied by 10^6. Useful for some PHP stream functions that need seconds and microseconds as
* different arguments
*
* @param $number
* @return array
*/
public static function splitSecondsMicroseconds($number)
{
return array(floor($number), ($number - floor($number)) * 1000000);
}
/**
* View any string as a hexdump.
*
* This is most commonly used to view binary data from streams
* or sockets while debugging, but can be used to view any string
* with non-viewable characters.
*
* @version 1.3.2
* @author Aidan Lister <aidan@php.net>
* @author Peter Waller <iridum@php.net>
* @link http://aidanlister.com/repos/v/function.hexdump.php
*
* @param string $data The string to be dumped
* @param bool $htmloutput Set to false for non-HTML output
* @param bool $uppercase Set to true for uppercase hex
* @param bool $return Set to true to return the dump
* @return string
*/
public static function hexdump($data, $htmloutput = true, $uppercase = false, $return = false)
{
// Init
$hexi = '';
$ascii = '';
$dump = $htmloutput ? '<pre>' : '';
$offset = 0;
$len = mb_strlen($data, 'ASCII');
// Upper or lower case hexidecimal
$hexFormat = $uppercase ? 'X' : 'x';
// Iterate string
for ($i = $j = 0; $i < $len; $i++) {
// Convert to hexidecimal
// We must use concatenation here because the $hexFormat value
// is needed for sprintf() to parse the format
$hexi .= sprintf('%02' . $hexFormat . ' ', ord($data[$i]));
// Replace non-viewable bytes with '.'
if (ord($data[$i]) >= 32) {
$ascii .= $htmloutput ? htmlentities($data[$i]) : $data[$i];
} else {
$ascii .= '.';
}
// Add extra column spacing
if ($j === 7) {
$hexi .= ' ';
$ascii .= ' ';
}
// Add row
if (++$j === 16 || $i === $len - 1) {
// Join the hexi / ascii output
// We must use concatenation here because the $hexFormat value
// is needed for sprintf() to parse the format
$dump .= sprintf('%04' . $hexFormat . ' %-49s %s', $offset, $hexi, $ascii);
// Reset vars
$hexi = $ascii = '';
$offset += 16;
$j = 0;
// Add newline
if ($i !== $len - 1) {
$dump .= PHP_EOL;
}
}
}
// Finish dump
$dump .= $htmloutput ? '</pre>' : '';
$dump .= PHP_EOL;
if ($return) {
return $dump;
}
echo $dump;
}
}

View File

@@ -0,0 +1,120 @@
<?php
/* This file was autogenerated by spec/parser.php - Do not modify */
namespace PhpAmqpLib\Helper\Protocol;
class MethodMap080
{
protected $method_map = array(
'10,10' => 'connection_start',
'10,11' => 'connection_start_ok',
'10,20' => 'connection_secure',
'10,21' => 'connection_secure_ok',
'10,30' => 'connection_tune',
'10,31' => 'connection_tune_ok',
'10,40' => 'connection_open',
'10,41' => 'connection_open_ok',
'10,50' => 'connection_redirect',
'10,60' => 'connection_close',
'10,61' => 'connection_close_ok',
'20,10' => 'channel_open',
'20,11' => 'channel_open_ok',
'20,20' => 'channel_flow',
'20,21' => 'channel_flow_ok',
'20,30' => 'channel_alert',
'20,40' => 'channel_close',
'20,41' => 'channel_close_ok',
'30,10' => 'access_request',
'30,11' => 'access_request_ok',
'40,10' => 'exchange_declare',
'40,11' => 'exchange_declare_ok',
'40,20' => 'exchange_delete',
'40,21' => 'exchange_delete_ok',
'50,10' => 'queue_declare',
'50,11' => 'queue_declare_ok',
'50,20' => 'queue_bind',
'50,21' => 'queue_bind_ok',
'50,30' => 'queue_purge',
'50,31' => 'queue_purge_ok',
'50,40' => 'queue_delete',
'50,41' => 'queue_delete_ok',
'50,50' => 'queue_unbind',
'50,51' => 'queue_unbind_ok',
'60,10' => 'basic_qos',
'60,11' => 'basic_qos_ok',
'60,20' => 'basic_consume',
'60,21' => 'basic_consume_ok',
'60,30' => 'basic_cancel',
'60,31' => 'basic_cancel_ok',
'60,40' => 'basic_publish',
'60,50' => 'basic_return',
'60,60' => 'basic_deliver',
'60,70' => 'basic_get',
'60,71' => 'basic_get_ok',
'60,72' => 'basic_get_empty',
'60,80' => 'basic_ack',
'60,90' => 'basic_reject',
'60,100' => 'basic_recover_async',
'60,110' => 'basic_recover',
'60,111' => 'basic_recover_ok',
'70,10' => 'file_qos',
'70,11' => 'file_qos_ok',
'70,20' => 'file_consume',
'70,21' => 'file_consume_ok',
'70,30' => 'file_cancel',
'70,31' => 'file_cancel_ok',
'70,40' => 'file_open',
'70,41' => 'file_open_ok',
'70,50' => 'file_stage',
'70,60' => 'file_publish',
'70,70' => 'file_return',
'70,80' => 'file_deliver',
'70,90' => 'file_ack',
'70,100' => 'file_reject',
'80,10' => 'stream_qos',
'80,11' => 'stream_qos_ok',
'80,20' => 'stream_consume',
'80,21' => 'stream_consume_ok',
'80,30' => 'stream_cancel',
'80,31' => 'stream_cancel_ok',
'80,40' => 'stream_publish',
'80,50' => 'stream_return',
'80,60' => 'stream_deliver',
'90,10' => 'tx_select',
'90,11' => 'tx_select_ok',
'90,20' => 'tx_commit',
'90,21' => 'tx_commit_ok',
'90,30' => 'tx_rollback',
'90,31' => 'tx_rollback_ok',
'100,10' => 'dtx_select',
'100,11' => 'dtx_select_ok',
'100,20' => 'dtx_start',
'100,21' => 'dtx_start_ok',
'110,10' => 'tunnel_request',
'120,10' => 'test_integer',
'120,11' => 'test_integer_ok',
'120,20' => 'test_string',
'120,21' => 'test_string_ok',
'120,30' => 'test_table',
'120,31' => 'test_table_ok',
'120,40' => 'test_content',
'120,41' => 'test_content_ok',
);
public function get_method($method_sig)
{
return $this->method_map[$method_sig];
}
public function valid_method($method_sig)
{
return array_key_exists($method_sig, $this->method_map);
}
}

View File

@@ -0,0 +1,91 @@
<?php
/* This file was autogenerated by spec/parser.php - Do not modify */
namespace PhpAmqpLib\Helper\Protocol;
class MethodMap091
{
protected $method_map = array(
'10,10' => 'connection_start',
'10,11' => 'connection_start_ok',
'10,20' => 'connection_secure',
'10,21' => 'connection_secure_ok',
'10,30' => 'connection_tune',
'10,31' => 'connection_tune_ok',
'10,40' => 'connection_open',
'10,41' => 'connection_open_ok',
'10,50' => 'connection_close',
'10,51' => 'connection_close_ok',
'10,60' => 'connection_blocked',
'10,61' => 'connection_unblocked',
'20,10' => 'channel_open',
'20,11' => 'channel_open_ok',
'20,20' => 'channel_flow',
'20,21' => 'channel_flow_ok',
'20,40' => 'channel_close',
'20,41' => 'channel_close_ok',
'30,10' => 'access_request',
'30,11' => 'access_request_ok',
'40,10' => 'exchange_declare',
'40,11' => 'exchange_declare_ok',
'40,20' => 'exchange_delete',
'40,21' => 'exchange_delete_ok',
'40,30' => 'exchange_bind',
'40,31' => 'exchange_bind_ok',
'40,40' => 'exchange_unbind',
'40,51' => 'exchange_unbind_ok',
'50,10' => 'queue_declare',
'50,11' => 'queue_declare_ok',
'50,20' => 'queue_bind',
'50,21' => 'queue_bind_ok',
'50,30' => 'queue_purge',
'50,31' => 'queue_purge_ok',
'50,40' => 'queue_delete',
'50,41' => 'queue_delete_ok',
'50,50' => 'queue_unbind',
'50,51' => 'queue_unbind_ok',
'60,10' => 'basic_qos',
'60,11' => 'basic_qos_ok',
'60,20' => 'basic_consume',
'60,21' => 'basic_consume_ok',
'60,30' => 'basic_cancel_from_server',
'60,31' => 'basic_cancel_ok',
'60,40' => 'basic_publish',
'60,50' => 'basic_return',
'60,60' => 'basic_deliver',
'60,70' => 'basic_get',
'60,71' => 'basic_get_ok',
'60,72' => 'basic_get_empty',
'60,80' => 'basic_ack_from_server',
'60,90' => 'basic_reject',
'60,100' => 'basic_recover_async',
'60,110' => 'basic_recover',
'60,111' => 'basic_recover_ok',
'60,120' => 'basic_nack_from_server',
'90,10' => 'tx_select',
'90,11' => 'tx_select_ok',
'90,20' => 'tx_commit',
'90,21' => 'tx_commit_ok',
'90,30' => 'tx_rollback',
'90,31' => 'tx_rollback_ok',
'85,10' => 'confirm_select',
'85,11' => 'confirm_select_ok',
);
public function get_method($method_sig)
{
return $this->method_map[$method_sig];
}
public function valid_method($method_sig)
{
return array_key_exists($method_sig, $this->method_map);
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,870 @@
<?php
/* This file was autogenerated by spec/parser.php - Do not modify */
namespace PhpAmqpLib\Helper\Protocol;
use PhpAmqpLib\Wire\AMQPWriter;
use PhpAmqpLib\Wire\AMQPReader;
class Protocol091
{
/**
* @return array
*/
public function connectionStart($version_major = 0, $version_minor = 9, $server_properties, $mechanisms = 'PLAIN', $locales = 'en_US')
{
$args = new AMQPWriter();
$args->write_octet($version_major);
$args->write_octet($version_minor);
$args->write_table(empty($server_properties) ? array() : $server_properties);
$args->write_longstr($mechanisms);
$args->write_longstr($locales);
return array(10, 10, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function connectionStartOk($args)
{
$ret = array();
$ret[] = $args->read_table();
$ret[] = $args->read_shortstr();
$ret[] = $args->read_longstr();
$ret[] = $args->read_shortstr();
return $ret;
}
/**
* @return array
*/
public function connectionSecure($challenge)
{
$args = new AMQPWriter();
$args->write_longstr($challenge);
return array(10, 20, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function connectionSecureOk($args)
{
$ret = array();
$ret[] = $args->read_longstr();
return $ret;
}
/**
* @return array
*/
public function connectionTune($channel_max = 0, $frame_max = 0, $heartbeat = 0)
{
$args = new AMQPWriter();
$args->write_short($channel_max);
$args->write_long($frame_max);
$args->write_short($heartbeat);
return array(10, 30, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function connectionTuneOk($args)
{
$ret = array();
$ret[] = $args->read_short();
$ret[] = $args->read_long();
$ret[] = $args->read_short();
return $ret;
}
/**
* @return array
*/
public function connectionOpen($virtual_host = '/', $capabilities = '', $insist = false)
{
$args = new AMQPWriter();
$args->write_shortstr($virtual_host);
$args->write_shortstr($capabilities);
$args->write_bits(array($insist));
return array(10, 40, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function connectionOpenOk($args)
{
$ret = array();
$ret[] = $args->read_shortstr();
return $ret;
}
/**
* @return array
*/
public function connectionClose($reply_code, $reply_text = '', $class_id, $method_id)
{
$args = new AMQPWriter();
$args->write_short($reply_code);
$args->write_shortstr($reply_text);
$args->write_short($class_id);
$args->write_short($method_id);
return array(10, 50, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function connectionCloseOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function connectionBlocked($reason = '')
{
$args = new AMQPWriter();
$args->write_shortstr($reason);
return array(10, 60, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function connectionUnblocked($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function channelOpen($out_of_band = '')
{
$args = new AMQPWriter();
$args->write_shortstr($out_of_band);
return array(20, 10, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function channelOpenOk($args)
{
$ret = array();
$ret[] = $args->read_longstr();
return $ret;
}
/**
* @return array
*/
public function channelFlow($active)
{
$args = new AMQPWriter();
$args->write_bits(array($active));
return array(20, 20, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function channelFlowOk($args)
{
$ret = array();
$ret[] = $args->read_bit();
return $ret;
}
/**
* @return array
*/
public function channelClose($reply_code, $reply_text = '', $class_id, $method_id)
{
$args = new AMQPWriter();
$args->write_short($reply_code);
$args->write_shortstr($reply_text);
$args->write_short($class_id);
$args->write_short($method_id);
return array(20, 40, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function channelCloseOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function accessRequest($realm = '/data', $exclusive = false, $passive = true, $active = true, $write = true, $read = true)
{
$args = new AMQPWriter();
$args->write_shortstr($realm);
$args->write_bits(array($exclusive, $passive, $active, $write, $read));
return array(30, 10, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function accessRequestOk($args)
{
$ret = array();
$ret[] = $args->read_short();
return $ret;
}
/**
* @return array
*/
public function exchangeDeclare($ticket = 0, $exchange, $type = 'direct', $passive = false, $durable = false, $auto_delete = false, $internal = false, $nowait = false, $arguments = array())
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($exchange);
$args->write_shortstr($type);
$args->write_bits(array($passive, $durable, $auto_delete, $internal, $nowait));
$args->write_table(empty($arguments) ? array() : $arguments);
return array(40, 10, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function exchangeDeclareOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function exchangeDelete($ticket = 0, $exchange, $if_unused = false, $nowait = false)
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($exchange);
$args->write_bits(array($if_unused, $nowait));
return array(40, 20, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function exchangeDeleteOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function exchangeBind($ticket = 0, $destination, $source, $routing_key = '', $nowait = false, $arguments = array())
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($destination);
$args->write_shortstr($source);
$args->write_shortstr($routing_key);
$args->write_bits(array($nowait));
$args->write_table(empty($arguments) ? array() : $arguments);
return array(40, 30, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function exchangeBindOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function exchangeUnbind($ticket = 0, $destination, $source, $routing_key = '', $nowait = false, $arguments = array())
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($destination);
$args->write_shortstr($source);
$args->write_shortstr($routing_key);
$args->write_bits(array($nowait));
$args->write_table(empty($arguments) ? array() : $arguments);
return array(40, 40, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function exchangeUnbindOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function queueDeclare($ticket = 0, $queue = '', $passive = false, $durable = false, $exclusive = false, $auto_delete = false, $nowait = false, $arguments = array())
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($queue);
$args->write_bits(array($passive, $durable, $exclusive, $auto_delete, $nowait));
$args->write_table(empty($arguments) ? array() : $arguments);
return array(50, 10, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function queueDeclareOk($args)
{
$ret = array();
$ret[] = $args->read_shortstr();
$ret[] = $args->read_long();
$ret[] = $args->read_long();
return $ret;
}
/**
* @return array
*/
public function queueBind($ticket = 0, $queue = '', $exchange, $routing_key = '', $nowait = false, $arguments = array())
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($queue);
$args->write_shortstr($exchange);
$args->write_shortstr($routing_key);
$args->write_bits(array($nowait));
$args->write_table(empty($arguments) ? array() : $arguments);
return array(50, 20, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function queueBindOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function queuePurge($ticket = 0, $queue = '', $nowait = false)
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($queue);
$args->write_bits(array($nowait));
return array(50, 30, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function queuePurgeOk($args)
{
$ret = array();
$ret[] = $args->read_long();
return $ret;
}
/**
* @return array
*/
public function queueDelete($ticket = 0, $queue = '', $if_unused = false, $if_empty = false, $nowait = false)
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($queue);
$args->write_bits(array($if_unused, $if_empty, $nowait));
return array(50, 40, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function queueDeleteOk($args)
{
$ret = array();
$ret[] = $args->read_long();
return $ret;
}
/**
* @return array
*/
public function queueUnbind($ticket = 0, $queue = '', $exchange, $routing_key = '', $arguments = array())
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($queue);
$args->write_shortstr($exchange);
$args->write_shortstr($routing_key);
$args->write_table(empty($arguments) ? array() : $arguments);
return array(50, 50, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function queueUnbindOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function basicQos($prefetch_size = 0, $prefetch_count = 0, $global = false)
{
$args = new AMQPWriter();
$args->write_long($prefetch_size);
$args->write_short($prefetch_count);
$args->write_bits(array($global));
return array(60, 10, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function basicQosOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function basicConsume($ticket = 0, $queue = '', $consumer_tag = '', $no_local = false, $no_ack = false, $exclusive = false, $nowait = false, $arguments = array())
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($queue);
$args->write_shortstr($consumer_tag);
$args->write_bits(array($no_local, $no_ack, $exclusive, $nowait));
$args->write_table(empty($arguments) ? array() : $arguments);
return array(60, 20, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function basicConsumeOk($args)
{
$ret = array();
$ret[] = $args->read_shortstr();
return $ret;
}
/**
* @return array
*/
public function basicCancel($consumer_tag, $nowait = false)
{
$args = new AMQPWriter();
$args->write_shortstr($consumer_tag);
$args->write_bits(array($nowait));
return array(60, 30, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function basicCancelOk($args)
{
$ret = array();
$ret[] = $args->read_shortstr();
return $ret;
}
/**
* @return array
*/
public function basicPublish($ticket = 0, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false)
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($exchange);
$args->write_shortstr($routing_key);
$args->write_bits(array($mandatory, $immediate));
return array(60, 40, $args);
}
/**
* @return array
*/
public function basicReturn($reply_code, $reply_text = '', $exchange, $routing_key)
{
$args = new AMQPWriter();
$args->write_short($reply_code);
$args->write_shortstr($reply_text);
$args->write_shortstr($exchange);
$args->write_shortstr($routing_key);
return array(60, 50, $args);
}
/**
* @return array
*/
public function basicDeliver($consumer_tag, $delivery_tag, $redelivered = false, $exchange, $routing_key)
{
$args = new AMQPWriter();
$args->write_shortstr($consumer_tag);
$args->write_longlong($delivery_tag);
$args->write_bits(array($redelivered));
$args->write_shortstr($exchange);
$args->write_shortstr($routing_key);
return array(60, 60, $args);
}
/**
* @return array
*/
public function basicGet($ticket = 0, $queue = '', $no_ack = false)
{
$args = new AMQPWriter();
$args->write_short($ticket);
$args->write_shortstr($queue);
$args->write_bits(array($no_ack));
return array(60, 70, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function basicGetOk($args)
{
$ret = array();
$ret[] = $args->read_longlong();
$ret[] = $args->read_bit();
$ret[] = $args->read_shortstr();
$ret[] = $args->read_shortstr();
$ret[] = $args->read_long();
return $ret;
}
/**
* @param AMQPReader $args
* @return array
*/
public static function basicGetEmpty($args)
{
$ret = array();
$ret[] = $args->read_shortstr();
return $ret;
}
/**
* @return array
*/
public function basicAck($delivery_tag = 0, $multiple = false)
{
$args = new AMQPWriter();
$args->write_longlong($delivery_tag);
$args->write_bits(array($multiple));
return array(60, 80, $args);
}
/**
* @return array
*/
public function basicReject($delivery_tag, $requeue = true)
{
$args = new AMQPWriter();
$args->write_longlong($delivery_tag);
$args->write_bits(array($requeue));
return array(60, 90, $args);
}
/**
* @return array
*/
public function basicRecoverAsync($requeue = false)
{
$args = new AMQPWriter();
$args->write_bits(array($requeue));
return array(60, 100, $args);
}
/**
* @return array
*/
public function basicRecover($requeue = false)
{
$args = new AMQPWriter();
$args->write_bits(array($requeue));
return array(60, 110, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function basicRecoverOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function basicNack($delivery_tag = 0, $multiple = false, $requeue = true)
{
$args = new AMQPWriter();
$args->write_longlong($delivery_tag);
$args->write_bits(array($multiple, $requeue));
return array(60, 120, $args);
}
/**
* @return array
*/
public function txSelect()
{
$args = new AMQPWriter();
return array(90, 10, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function txSelectOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function txCommit()
{
$args = new AMQPWriter();
return array(90, 20, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function txCommitOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function txRollback()
{
$args = new AMQPWriter();
return array(90, 30, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function txRollbackOk($args)
{
$ret = array();
return $ret;
}
/**
* @return array
*/
public function confirmSelect($nowait = false)
{
$args = new AMQPWriter();
$args->write_bits(array($nowait));
return array(85, 10, $args);
}
/**
* @param AMQPReader $args
* @return array
*/
public static function confirmSelectOk($args)
{
$ret = array();
return $ret;
}
}

View File

@@ -0,0 +1,113 @@
<?php
/* This file was autogenerated by spec/parser.php - Do not modify */
namespace PhpAmqpLib\Helper\Protocol;
class Wait080
{
protected $wait = array(
'connection.start' => '10,10',
'connection.start_ok' => '10,11',
'connection.secure' => '10,20',
'connection.secure_ok' => '10,21',
'connection.tune' => '10,30',
'connection.tune_ok' => '10,31',
'connection.open' => '10,40',
'connection.open_ok' => '10,41',
'connection.redirect' => '10,50',
'connection.close' => '10,60',
'connection.close_ok' => '10,61',
'channel.open' => '20,10',
'channel.open_ok' => '20,11',
'channel.flow' => '20,20',
'channel.flow_ok' => '20,21',
'channel.alert' => '20,30',
'channel.close' => '20,40',
'channel.close_ok' => '20,41',
'access.request' => '30,10',
'access.request_ok' => '30,11',
'exchange.declare' => '40,10',
'exchange.declare_ok' => '40,11',
'exchange.delete' => '40,20',
'exchange.delete_ok' => '40,21',
'queue.declare' => '50,10',
'queue.declare_ok' => '50,11',
'queue.bind' => '50,20',
'queue.bind_ok' => '50,21',
'queue.purge' => '50,30',
'queue.purge_ok' => '50,31',
'queue.delete' => '50,40',
'queue.delete_ok' => '50,41',
'queue.unbind' => '50,50',
'queue.unbind_ok' => '50,51',
'basic.qos' => '60,10',
'basic.qos_ok' => '60,11',
'basic.consume' => '60,20',
'basic.consume_ok' => '60,21',
'basic.cancel' => '60,30',
'basic.cancel_ok' => '60,31',
'basic.publish' => '60,40',
'basic.return' => '60,50',
'basic.deliver' => '60,60',
'basic.get' => '60,70',
'basic.get_ok' => '60,71',
'basic.get_empty' => '60,72',
'basic.ack' => '60,80',
'basic.reject' => '60,90',
'basic.recover_async' => '60,100',
'basic.recover' => '60,110',
'basic.recover_ok' => '60,111',
'file.qos' => '70,10',
'file.qos_ok' => '70,11',
'file.consume' => '70,20',
'file.consume_ok' => '70,21',
'file.cancel' => '70,30',
'file.cancel_ok' => '70,31',
'file.open' => '70,40',
'file.open_ok' => '70,41',
'file.stage' => '70,50',
'file.publish' => '70,60',
'file.return' => '70,70',
'file.deliver' => '70,80',
'file.ack' => '70,90',
'file.reject' => '70,100',
'stream.qos' => '80,10',
'stream.qos_ok' => '80,11',
'stream.consume' => '80,20',
'stream.consume_ok' => '80,21',
'stream.cancel' => '80,30',
'stream.cancel_ok' => '80,31',
'stream.publish' => '80,40',
'stream.return' => '80,50',
'stream.deliver' => '80,60',
'tx.select' => '90,10',
'tx.select_ok' => '90,11',
'tx.commit' => '90,20',
'tx.commit_ok' => '90,21',
'tx.rollback' => '90,30',
'tx.rollback_ok' => '90,31',
'dtx.select' => '100,10',
'dtx.select_ok' => '100,11',
'dtx.start' => '100,20',
'dtx.start_ok' => '100,21',
'tunnel.request' => '110,10',
'test.integer' => '120,10',
'test.integer_ok' => '120,11',
'test.string' => '120,20',
'test.string_ok' => '120,21',
'test.table' => '120,30',
'test.table_ok' => '120,31',
'test.content' => '120,40',
'test.content_ok' => '120,41',
);
public function get_wait($method)
{
return $this->wait[$method];
}
}

View File

@@ -0,0 +1,84 @@
<?php
/* This file was autogenerated by spec/parser.php - Do not modify */
namespace PhpAmqpLib\Helper\Protocol;
class Wait091
{
protected $wait = array(
'connection.start' => '10,10',
'connection.start_ok' => '10,11',
'connection.secure' => '10,20',
'connection.secure_ok' => '10,21',
'connection.tune' => '10,30',
'connection.tune_ok' => '10,31',
'connection.open' => '10,40',
'connection.open_ok' => '10,41',
'connection.close' => '10,50',
'connection.close_ok' => '10,51',
'connection.blocked' => '10,60',
'connection.unblocked' => '10,61',
'channel.open' => '20,10',
'channel.open_ok' => '20,11',
'channel.flow' => '20,20',
'channel.flow_ok' => '20,21',
'channel.close' => '20,40',
'channel.close_ok' => '20,41',
'access.request' => '30,10',
'access.request_ok' => '30,11',
'exchange.declare' => '40,10',
'exchange.declare_ok' => '40,11',
'exchange.delete' => '40,20',
'exchange.delete_ok' => '40,21',
'exchange.bind' => '40,30',
'exchange.bind_ok' => '40,31',
'exchange.unbind' => '40,40',
'exchange.unbind_ok' => '40,51',
'queue.declare' => '50,10',
'queue.declare_ok' => '50,11',
'queue.bind' => '50,20',
'queue.bind_ok' => '50,21',
'queue.purge' => '50,30',
'queue.purge_ok' => '50,31',
'queue.delete' => '50,40',
'queue.delete_ok' => '50,41',
'queue.unbind' => '50,50',
'queue.unbind_ok' => '50,51',
'basic.qos' => '60,10',
'basic.qos_ok' => '60,11',
'basic.consume' => '60,20',
'basic.consume_ok' => '60,21',
'basic.cancel' => '60,30',
'basic.cancel_ok' => '60,31',
'basic.publish' => '60,40',
'basic.return' => '60,50',
'basic.deliver' => '60,60',
'basic.get' => '60,70',
'basic.get_ok' => '60,71',
'basic.get_empty' => '60,72',
'basic.ack' => '60,80',
'basic.reject' => '60,90',
'basic.recover_async' => '60,100',
'basic.recover' => '60,110',
'basic.recover_ok' => '60,111',
'basic.nack' => '60,120',
'tx.select' => '90,10',
'tx.select_ok' => '90,11',
'tx.commit' => '90,20',
'tx.commit_ok' => '90,21',
'tx.rollback' => '90,30',
'tx.rollback_ok' => '90,31',
'confirm.select' => '85,10',
'confirm.select_ok' => '85,11',
);
public function get_wait($method)
{
return $this->wait[$method];
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace PhpAmqpLib\Message;
use PhpAmqpLib\Wire\GenericContent;
/**
* A Message for use with the Channnel.basic_* methods.
*/
class AMQPMessage extends GenericContent
{
/** @var string */
public $body;
public $body_size;
public $is_truncated = false;
/** @var string */
public $content_encoding;
/** @var array */
protected static $propertyDefinitions = array(
'content_type' => 'shortstr',
'content_encoding' => 'shortstr',
'application_headers' => 'table_object',
'delivery_mode' => 'octet',
'priority' => 'octet',
'correlation_id' => 'shortstr',
'reply_to' => 'shortstr',
'expiration' => 'shortstr',
'message_id' => 'shortstr',
'timestamp' => 'timestamp',
'type' => 'shortstr',
'user_id' => 'shortstr',
'app_id' => 'shortstr',
'cluster_id' => 'shortstr',
);
/**
* @param string $body
* @param null $properties
*/
public function __construct($body = '', $properties = null)
{
$this->setBody($body);
parent::__construct($properties, static::$propertyDefinitions);
}
/**
* Sets the message payload
*
* @param mixed $body
* @return $this
*/
public function setBody($body)
{
$this->body = $body;
}
}

View File

@@ -0,0 +1,446 @@
<?php
namespace PhpAmqpLib\Wire;
use PhpAmqpLib\Channel\AbstractChannel;
use PhpAmqpLib\Exception;
/**
* Iterator implemented for transparent integration with AMQPWriter::write_[array|table]()
*/
abstract class AMQPAbstractCollection implements \Iterator
{
//protocol defines available field types and their corresponding symbols
const PROTOCOL_080 = AbstractChannel::PROTOCOL_080;
const PROTOCOL_091 = AbstractChannel::PROTOCOL_091;
const PROTOCOL_RBT = 'rabbit'; //pseudo proto
//Abstract data types
const T_INT_SHORTSHORT = 1;
const T_INT_SHORTSHORT_U = 2;
const T_INT_SHORT = 3;
const T_INT_SHORT_U = 4;
const T_INT_LONG = 5;
const T_INT_LONG_U = 6;
const T_INT_LONGLONG = 7;
const T_INT_LONGLONG_U = 8;
const T_DECIMAL = 9;
const T_TIMESTAMP = 10;
const T_VOID = 11;
const T_BOOL = 12;
const T_STRING_SHORT = 13;
const T_STRING_LONG = 14;
const T_ARRAY = 15;
const T_TABLE = 16;
/**
* @var string
*/
private static $_protocol = null;
/*
* Field types messy mess http://www.rabbitmq.com/amqp-0-9-1-errata.html#section_3
* Default behaviour is to use rabbitMQ compatible field-set
* Define AMQP_STRICT_FLD_TYPES=true to use strict AMQP instead
*/
private static $_types_080 = array(
self::T_INT_LONG => 'I',
self::T_DECIMAL => 'D',
self::T_TIMESTAMP => 'T',
self::T_STRING_LONG => 'S',
self::T_TABLE => 'F'
);
/**
* @var array
*/
private static $_types_091 = array(
self::T_INT_SHORTSHORT => 'b',
self::T_INT_SHORTSHORT_U => 'B',
self::T_INT_SHORT => 'U',
self::T_INT_SHORT_U => 'u',
self::T_INT_LONG => 'I',
self::T_INT_LONG_U => 'i',
self::T_INT_LONGLONG => 'L',
self::T_INT_LONGLONG_U => 'l',
self::T_DECIMAL => 'D',
self::T_TIMESTAMP => 'T',
self::T_VOID => 'V',
self::T_BOOL => 't',
self::T_STRING_SHORT => 's',
self::T_STRING_LONG => 'S',
self::T_ARRAY => 'A',
self::T_TABLE => 'F'
);
/**
* @var array
*/
private static $_types_rabbit = array(
self::T_INT_SHORTSHORT => 'b',
self::T_INT_SHORT => 's',
self::T_INT_LONG => 'I',
self::T_INT_LONGLONG => 'l',
self::T_DECIMAL => 'D',
self::T_TIMESTAMP => 'T',
self::T_VOID => 'V',
self::T_BOOL => 't',
self::T_STRING_LONG => 'S',
self::T_ARRAY => 'A',
self::T_TABLE => 'F'
);
/**
* @var array
*/
protected $data = array();
public function __construct(array $data = null)
{
if (!empty($data)) {
$this->data = $this->encodeCollection($data);
}
}
/**
* @return int
*/
abstract public function getType();
/**
* @param mixed $val
* @param int $type
* @param string $key
*/
final protected function setValue($val, $type = null, $key = null)
{
if ($val instanceof self) {
if ($type && ($type != $val->getType())) {
throw new Exception\AMQPInvalidArgumentException(
'Attempted to add instance of ' . get_class($val) . ' representing type [' . $val->getType() . '] as mismatching type [' . $type . ']'
);
}
$type = $val->getType();
} elseif ($type) { //ensuring data integrity and that all members are properly validated
switch ($type) {
case self::T_ARRAY:
throw new Exception\AMQPInvalidArgumentException('Arrays must be passed as AMQPArray instance');
break;
case self::T_TABLE:
throw new Exception\AMQPInvalidArgumentException('Tables must be passed as AMQPTable instance');
break;
case self::T_DECIMAL:
if (!($val instanceof AMQPDecimal)) {
throw new Exception\AMQPInvalidArgumentException('Decimal values must be instance of AMQPDecimal');
}
break;
}
}
if ($type) {
self::checkDataTypeIsSupported($type, false);
$val = array($type, $val);
} else {
$val = $this->encodeValue($val);
}
if ($key === null) {
$this->data[] = $val;
} else {
$this->data[$key] = $val;
}
}
/**
* @return array
*/
final public function getNativeData()
{
return $this->decodeCollection($this->data);
}
/**
* @param array $val
* @return array
*/
final protected function encodeCollection(array $val)
{
foreach ($val as &$v) {
$v = $this->encodeValue($v);
}
unset($v);
return $val;
}
/**
* @param array $val
* @return array
*/
final protected function decodeCollection(array $val)
{
foreach ($val as &$v) {
$v = $this->decodeValue($v[1], $v[0]);
}
unset($v);
return $val;
}
/**
* @param mixed $val
* @return mixed
* @throws Exception\AMQPOutOfBoundsException
*/
protected function encodeValue($val)
{
if (is_string($val)) {
$val = $this->encodeString($val);
} elseif (is_float($val)) {
$val = $this->encodeFloat($val);
} elseif (is_int($val)) {
$val = $this->encodeInt($val);
} elseif (is_bool($val)) {
$val = $this->encodeBool($val);
} elseif (is_null($val)) {
$val = $this->encodeVoid();
} elseif ($val instanceof \DateTime) {
$val = array(self::T_TIMESTAMP, $val->getTimestamp());
} elseif ($val instanceof AMQPDecimal) {
$val = array(self::T_DECIMAL, $val);
} elseif ($val instanceof self) {
//avoid silent type correction of strictly typed values
self::checkDataTypeIsSupported($val->getType(), false);
$val = array($val->getType(), $val);
} elseif (is_array($val)) {
//AMQP specs says "Field names MUST start with a letter, '$' or '#'"
//so beware, some servers may raise an exception with 503 code in cases when indexed array is encoded as table
if (self::isProtocol(self::PROTOCOL_080)) {
//080 doesn't support arrays, forcing table
$val = array(self::T_TABLE, new AMQPTable($val));
} elseif (empty($val) || (array_keys($val) === range(0, count($val) - 1))) {
$val = array(self::T_ARRAY, new AMQPArray($val));
} else {
$val = array(self::T_TABLE, new AMQPTable($val));
}
} else {
throw new Exception\AMQPOutOfBoundsException(sprintf('Encountered value of unsupported type: %s', gettype($val)));
}
return $val;
}
/**
* @param mixed $val
* @param integer $type
* @return array|bool|\DateTime|null
*/
protected function decodeValue($val, $type)
{
if ($val instanceof self) {
//covering arrays and tables
$val = $val->getNativeData();
} else {
switch ($type) {
case self::T_BOOL:
$val = (bool) $val;
break;
case self::T_TIMESTAMP:
$val = \DateTime::createFromFormat('U', $val);
break;
case self::T_VOID:
$val = null;
break;
case self::T_ARRAY:
case self::T_TABLE:
throw new Exception\AMQPLogicException(
'Encountered an array/table struct which is not an instance of AMQPCollection. ' .
'This is considered a bug and should be fixed, please report'
);
}
}
return $val;
}
/**
* @param string $val
* @return array
*/
protected function encodeString($val)
{
return array(self::T_STRING_LONG, $val);
}
/**
* @param int $val
* @return array
*/
protected function encodeInt($val)
{
if (($val >= -2147483648) && ($val <= 2147483647)) {
$ev = array(self::T_INT_LONG, $val);
} elseif (self::isProtocol(self::PROTOCOL_080)) {
//080 doesn't support longlong
$ev = $this->encodeString((string) $val);
} else {
$ev = array(self::T_INT_LONGLONG, $val);
}
return $ev;
}
/**
* @param float $val
* @return array
*/
protected function encodeFloat($val)
{
return static::encodeString((string) $val);
}
/**
* @param bool $val
* @return array
*/
protected function encodeBool($val)
{
$val = (bool) $val;
return self::isProtocol(self::PROTOCOL_080) ? array(self::T_INT_LONG, (int) $val) : array(self::T_BOOL, $val);
}
/**
* @return array
*/
protected function encodeVoid()
{
return self::isProtocol(self::PROTOCOL_080) ? $this->encodeString('') : array(self::T_VOID, null);
}
/**
* @return string
*/
final public static function getProtocol()
{
if (self::$_protocol === null) {
self::$_protocol = defined('AMQP_STRICT_FLD_TYPES') && AMQP_STRICT_FLD_TYPES ?
AbstractChannel::getProtocolVersion() :
self::PROTOCOL_RBT;
}
return self::$_protocol;
}
/**
* @param string $proto
* @return bool
*/
final public static function isProtocol($proto)
{
return self::getProtocol() == $proto;
}
/**
* @return array [dataTypeConstant => dataTypeSymbol]
*/
final public static function getSupportedDataTypes()
{
switch ($proto = self::getProtocol()) {
case self::PROTOCOL_080:
$types = self::$_types_080;
break;
case self::PROTOCOL_091:
$types = self::$_types_091;
break;
case self::PROTOCOL_RBT:
$types = self::$_types_rabbit;
break;
default:
throw new Exception\AMQPOutOfRangeException(sprintf('Unknown protocol: %s', $proto));
}
return $types;
}
/**
* @param string $type
* @param bool $return Whether to return or raise AMQPOutOfRangeException
* @return boolean
*/
final public static function checkDataTypeIsSupported($type, $return = true)
{
try {
$supported = self::getSupportedDataTypes();
if (!isset($supported[$type])) {
throw new Exception\AMQPOutOfRangeException(sprintf('AMQP-%s doesn\'t support data of type [%s]', self::getProtocol(), $type));
}
return true;
} catch (Exception\AMQPOutOfRangeException $ex) {
if (!$return) {
throw $ex;
}
return false;
}
}
/**
* @param integer $type
* @return string
*/
final public static function getSymbolForDataType($type)
{
$types = self::getSupportedDataTypes();
if (!isset($types[$type])) {
throw new Exception\AMQPOutOfRangeException(sprintf('AMQP-%s doesn\'t support data of type [%s]', self::getProtocol(), $type));
}
return $types[$type];
}
/**
* @param string $symbol
* @return integer
*/
final public static function getDataTypeForSymbol($symbol)
{
$symbols = array_flip(self::getSupportedDataTypes());
if (!isset($symbols[$symbol])) {
throw new Exception\AMQPOutOfRangeException(sprintf('AMQP-%s doesn\'t define data of type [%s]', self::getProtocol(), $symbol));
}
return $symbols[$symbol];
}
public function current()
{
return current($this->data);
}
public function key()
{
return key($this->data);
}
public function next()
{
next($this->data);
}
public function rewind()
{
reset($this->data);
}
public function valid()
{
return key($this->data) !== null;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace PhpAmqpLib\Wire;
class AMQPArray extends AMQPAbstractCollection
{
public function __construct(array $data = null)
{
parent::__construct(empty($data) ? null : array_values($data));
}
/**
* @return int
*/
final public function getType()
{
return self::T_ARRAY;
}
/**
* @param mixed $val
* @param null $type
* @return $this
*/
public function push($val, $type = null)
{
$this->setValue($val, $type);
return $this;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace PhpAmqpLib\Wire;
use PhpAmqpLib\Exception\AMQPOutOfBoundsException;
/**
* AMQP protocol decimal value.
*
* Values are represented as (n,e) pairs. The actual value
* is n * 10^(-e).
*
* From 0.8 spec: Decimal values are
* not intended to support floating point values, but rather
* business values such as currency rates and amounts. The
* 'decimals' octet is not signed.
*/
class AMQPDecimal
{
/** @var int */
protected $n;
/** @var int */
protected $e;
/**
* @param $n
* @param $e
* @throws \PhpAmqpLib\Exception\AMQPOutOfBoundsException
*/
public function __construct($n, $e)
{
if ($e < 0) {
throw new AMQPOutOfBoundsException('Decimal exponent value must be unsigned!');
}
$this->n = $n;
$this->e = $e;
}
/**
* @return string
*/
public function asBCvalue()
{
return bcdiv($this->n, bcpow(10, $this->e));
}
}

View File

@@ -0,0 +1,534 @@
<?php
namespace PhpAmqpLib\Wire;
use PhpAmqpLib\Exception\AMQPInvalidArgumentException;
use PhpAmqpLib\Exception\AMQPOutOfBoundsException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Exception\AMQPTimeoutException;
use PhpAmqpLib\Helper\MiscHelper;
use PhpAmqpLib\Wire\IO\AbstractIO;
/**
* This class can read from a string or from a stream
*
* TODO : split this class: AMQPStreamReader and a AMQPBufferReader
*/
class AMQPReader extends AbstractClient
{
const BIT = 1;
const OCTET = 1;
const SHORTSTR = 1;
const SHORT = 2;
const LONG = 4;
const SIGNED_LONG = 4;
const READ_PHP_INT = 4; // use READ_ to avoid possible clashes with PHP
const LONGLONG = 8;
const TIMESTAMP = 8;
/** @var string */
protected $str;
/** @var int */
protected $str_length;
/** @var int */
protected $offset;
/** @var int */
protected $bitcount;
/** @var bool */
protected $is64bits;
/** @var int */
protected $timeout;
/** @var int */
protected $bits;
/** @var \PhpAmqpLib\Wire\IO\AbstractIO */
protected $io;
/**
* @param string $str
* @param AbstractIO $io
* @param int $timeout
*/
public function __construct($str, AbstractIO $io = null, $timeout = 0)
{
parent::__construct();
$this->str = $str;
$this->str_length = mb_strlen($this->str, 'ASCII');
$this->io = $io;
$this->offset = 0;
$this->bitcount = $this->bits = 0;
$this->timeout = $timeout;
}
/**
* Resets the object from the injected param
*
* Used to not need to create a new AMQPReader instance every time.
* when we can just pass a string and reset the object state.
* NOTE: since we are working with strings we don't need to pass an AbstractIO
* or a timeout.
*
* @param string $str
*/
public function reuse($str)
{
$this->str = $str;
$this->str_length = mb_strlen($this->str, 'ASCII');
$this->offset = 0;
$this->bitcount = $this->bits = 0;
}
/**
* Closes the stream
*/
public function close()
{
if ($this->io) {
$this->io->close();
}
}
/**
* @param $n
* @return string
*/
public function read($n)
{
$this->bitcount = $this->bits = 0;
return $this->rawread($n);
}
/**
* Waits until some data is retrieved from the socket.
*
* AMQPTimeoutException can be raised if the timeout is set
*
* @throws \PhpAmqpLib\Exception\AMQPTimeoutException
*/
protected function wait()
{
if ($this->timeout == 0) {
return;
}
// wait ..
list($sec, $usec) = MiscHelper::splitSecondsMicroseconds($this->timeout);
$result = $this->io->select($sec, $usec);
if ($result === false) {
throw new AMQPRuntimeException('A network error occured while awaiting for incoming data');
}
if ($result === 0) {
throw new AMQPTimeoutException(sprintf(
'The connection timed out after %s sec while awaiting incoming data',
$this->getTimeout()
));
}
}
/**
* @param $n
* @return string
* @throws \RuntimeException
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
*/
protected function rawread($n)
{
if ($this->io) {
$this->wait();
$res = $this->io->read($n);
$this->offset += $n;
} else {
if ($this->str_length < $n) {
throw new AMQPRuntimeException(sprintf(
'Error reading data. Requested %s bytes while string buffer has only %s',
$n,
$this->str_length
));
}
$res = mb_substr($this->str, 0, $n, 'ASCII');
$this->str = mb_substr($this->str, $n, mb_strlen($this->str, 'ASCII') - $n, 'ASCII');
$this->str_length -= $n;
$this->offset += $n;
}
return $res;
}
/**
* @return bool
*/
public function read_bit()
{
if (!$this->bitcount) {
$this->bits = ord($this->rawread(1));
$this->bitcount = 8;
}
$result = ($this->bits & 1) == 1;
$this->bits >>= 1;
$this->bitcount -= 1;
return $result;
}
/**
* @return mixed
*/
public function read_octet()
{
$this->bitcount = $this->bits = 0;
list(, $res) = unpack('C', $this->rawread(1));
return $res;
}
/**
* @return mixed
*/
public function read_signed_octet()
{
$this->bitcount = $this->bits = 0;
list(, $res) = unpack('c', $this->rawread(1));
return $res;
}
/**
* @return mixed
*/
public function read_short()
{
$this->bitcount = $this->bits = 0;
list(, $res) = unpack('n', $this->rawread(2));
return $res;
}
/**
* @return mixed
*/
public function read_signed_short()
{
$this->bitcount = $this->bits = 0;
list(, $res) = unpack('s', $this->correctEndianness($this->rawread(2)));
return $res;
}
/**
* Reads 32 bit integer in big-endian byte order.
*
* On 64 bit systems it will return always unsigned int
* value in 0..2^32 range.
*
* On 32 bit systems it will return signed int value in
* -2^31...+2^31 range.
*
* Use with caution!
*/
public function read_php_int()
{
list(, $res) = unpack('N', $this->rawread(4));
if ($this->is64bits) {
$sres = sprintf('%u', $res);
return (int) $sres;
} else {
return $res;
}
}
/**
* PHP does not have unsigned 32 bit int,
* so we return it as a string
*
* @return string
*/
public function read_long()
{
$this->bitcount = $this->bits = 0;
list(, $res) = unpack('N', $this->rawread(4));
return !$this->is64bits && self::getLongMSB($res) ? sprintf('%u', $res) : $res;
}
/**
* @return integer
*/
private function read_signed_long()
{
$this->bitcount = $this->bits = 0;
list(, $res) = unpack('l', $this->correctEndianness($this->rawread(4)));
return $res;
}
/**
* Even on 64 bit systems PHP integers are singed.
* Since we need an unsigned value here we return it
* as a string.
*
* @return string
*/
public function read_longlong()
{
$this->bitcount = $this->bits = 0;
list(, $hi, $lo) = unpack('N2', $this->rawread(8));
$msb = self::getLongMSB($hi);
if (!$this->is64bits) {
if ($msb) {
$hi = sprintf('%u', $hi);
}
if (self::getLongMSB($lo)) {
$lo = sprintf('%u', $lo);
}
}
return bcadd($this->is64bits && !$msb ? $hi << 32 : bcmul($hi, '4294967296', 0), $lo, 0);
}
/**
* @return string
*/
public function read_signed_longlong()
{
$this->bitcount = $this->bits = 0;
list(, $hi, $lo) = unpack('N2', $this->rawread(8));
if ($this->is64bits) {
return bcadd($hi << 32, $lo, 0);
} else {
return bcadd(bcmul($hi, '4294967296', 0), self::getLongMSB($lo) ? sprintf('%u', $lo) : $lo, 0);
}
}
/**
* @param int $longInt
* @return bool
*/
private static function getLongMSB($longInt)
{
return (bool) ($longInt & 0x80000000);
}
/**
* Read a utf-8 encoded string that's stored in up to
* 255 bytes. Return it decoded as a PHP unicode object.
*/
public function read_shortstr()
{
$this->bitcount = $this->bits = 0;
list(, $slen) = unpack('C', $this->rawread(1));
return $this->rawread($slen);
}
/**
* Read a string that's up to 2**32 bytes, the encoding
* isn't specified in the AMQP spec, so just return it as
* a plain PHP string.
*/
public function read_longstr()
{
$this->bitcount = $this->bits = 0;
$slen = $this->read_php_int();
if ($slen < 0) {
throw new AMQPOutOfBoundsException('Strings longer than supported on this platform');
}
return $this->rawread($slen);
}
/**
* Read and AMQP timestamp, which is a 64-bit integer representing
* seconds since the Unix epoch in 1-second resolution.
*/
public function read_timestamp()
{
return $this->read_longlong();
}
/**
* Read an AMQP table, and return as a PHP array. keys are strings,
* values are (type,value) tuples.
*
* @param bool $returnObject Whether to return AMQPArray instance instead of plain array
* @return array|AMQPTable
*/
public function read_table($returnObject = false)
{
$this->bitcount = $this->bits = 0;
$tlen = $this->read_php_int();
if ($tlen < 0) {
throw new AMQPOutOfBoundsException('Table is longer than supported');
}
$table_data = new AMQPReader($this->rawread($tlen), null);
$result = $returnObject ? new AMQPTable() : array();
while ($table_data->tell() < $tlen) {
$name = $table_data->read_shortstr();
$ftype = AMQPAbstractCollection::getDataTypeForSymbol($ftypeSym = $table_data->rawread(1));
$val = $table_data->read_value($ftype, $returnObject);
$returnObject ? $result->set($name, $val, $ftype) : $result[$name] = array($ftypeSym, $val);
}
return $result;
}
/**
* @return array|AMQPTable
*/
public function read_table_object()
{
return $this->read_table(true);
}
/**
* Reads the array in the next value.
*
* @param bool $returnObject Whether to return AMQPArray instance instead of plain array
* @return array|AMQPArray
*/
public function read_array($returnObject = false)
{
$this->bitcount = $this->bits = 0;
// Determine array length and its end position
$arrayLength = $this->read_php_int();
$endOffset = $this->offset + $arrayLength;
$result = $returnObject ? new AMQPArray() : array();
// Read values until we reach the end of the array
while ($this->offset < $endOffset) {
$fieldType = AMQPAbstractCollection::getDataTypeForSymbol($this->rawread(1));
$fieldValue = $this->read_value($fieldType, $returnObject);
$returnObject ? $result->push($fieldValue, $fieldType) : $result[] = $fieldValue;
}
return $result;
}
/**
* @return array|AMQPArray
*/
public function read_array_object()
{
return $this->read_array(true);
}
/**
* Reads the next value as the provided field type.
*
* @param int $fieldType One of AMQPAbstractCollection::T_* constants
* @param bool $collectionsAsObjects Description
* @return mixed
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
*/
public function read_value($fieldType, $collectionsAsObjects = false)
{
$this->bitcount = $this->bits = 0;
$val = null;
switch ($fieldType) {
case AMQPAbstractCollection::T_INT_SHORTSHORT:
//according to AMQP091 spec, 'b' is not bit, it is short-short-int, also valid for rabbit/qpid
//$val=$this->read_bit();
$val = $this->read_signed_octet();
break;
case AMQPAbstractCollection::T_INT_SHORTSHORT_U:
$val = $this->read_octet();
break;
case AMQPAbstractCollection::T_INT_SHORT:
$val = $this->read_signed_short();
break;
case AMQPAbstractCollection::T_INT_SHORT_U:
$val = $this->read_short();
break;
case AMQPAbstractCollection::T_INT_LONG:
$val = $this->read_signed_long();
break;
case AMQPAbstractCollection::T_INT_LONG_U:
$val = $this->read_long();
break;
case AMQPAbstractCollection::T_INT_LONGLONG:
$val = $this->read_signed_longlong();
break;
case AMQPAbstractCollection::T_INT_LONGLONG_U:
$val = $this->read_longlong();
break;
case AMQPAbstractCollection::T_DECIMAL:
$e = $this->read_octet();
$n = $this->read_signed_long();
$val = new AMQPDecimal($n, $e);
break;
case AMQPAbstractCollection::T_TIMESTAMP:
$val = $this->read_timestamp();
break;
case AMQPAbstractCollection::T_BOOL:
$val = $this->read_octet();
break;
case AMQPAbstractCollection::T_STRING_SHORT:
$val = $this->read_shortstr();
break;
case AMQPAbstractCollection::T_STRING_LONG:
$val = $this->read_longstr();
break;
case AMQPAbstractCollection::T_ARRAY:
$val = $this->read_array($collectionsAsObjects);
break;
case AMQPAbstractCollection::T_TABLE:
$val = $this->read_table($collectionsAsObjects);
break;
case AMQPAbstractCollection::T_VOID:
$val = null;
break;
default:
throw new AMQPInvalidArgumentException(sprintf(
'Unsupported type "%s"',
$fieldType
));
}
return $val;
}
/**
* @return int
*/
protected function tell()
{
return $this->offset;
}
/**
* Sets the timeout (second)
*
* @param $timeout
*/
public function setTimeout($timeout)
{
$this->timeout = $timeout;
}
/**
* @return int
*/
public function getTimeout()
{
return $this->timeout;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace PhpAmqpLib\Wire;
use PhpAmqpLib\Exception;
class AMQPTable extends AMQPAbstractCollection
{
/**
* @return int
*/
final public function getType()
{
return self::T_TABLE;
}
/**
* @param string $key
* @param mixed $val
* @param integer $type
*/
public function set($key, $val, $type = null)
{
//https://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf, https://www.rabbitmq.com/resources/specs/amqp0-8.pdf
//Field names MUST start with a letter, '$' or '#' and may continue with letters, '$' or '#', digits, or underlines, to a maximum length of 128 characters
//The server SHOULD validate field names and upon receiving an invalid field name, it SHOULD signal a connection exception with reply code 503 (syntax error)
//validating length only and delegating other stuff to server, as rabbit seems to currently support numeric keys
if (!($len = strlen($key)) || ($len > 128)) {
throw new Exception\AMQPInvalidArgumentException('Table key must be non-empty string up to 128 chars in length');
}
$this->setValue($val, $type, $key);
}
}

View File

@@ -0,0 +1,494 @@
<?php
namespace PhpAmqpLib\Wire;
use PhpAmqpLib\Exception\AMQPInvalidArgumentException;
use PhpAmqpLib\Exception\AMQPOutOfBoundsException;
class AMQPWriter extends AbstractClient
{
/** @var string */
protected $out;
/** @var array */
protected $bits;
/** @var int */
protected $bitcount;
public function __construct()
{
parent::__construct();
$this->out = '';
$this->bits = array();
$this->bitcount = 0;
}
/**
* Packs integer into raw byte string in big-endian order
* Supports positive and negative ints represented as PHP int or string (except scientific notation)
*
* Floats has some precision issues and so intentionally not supported.
* Beware that floats out of PHP_INT_MAX range will be represented in scientific (exponential) notation when casted to string
*
* @param int|string $x Value to pack
* @param int $bytes Must be multiply of 2
* @return string
*/
private static function packBigEndian($x, $bytes)
{
if (($bytes <= 0) || ($bytes % 2)) {
throw new AMQPInvalidArgumentException(sprintf('Expected bytes count must be multiply of 2, %s given', $bytes));
}
$ox = $x; //purely for dbg purposes (overflow exception)
$isNeg = false;
if (is_int($x)) {
if ($x < 0) {
$isNeg = true;
$x = abs($x);
}
} elseif (is_string($x)) {
if (!is_numeric($x)) {
throw new AMQPInvalidArgumentException(sprintf('Unknown numeric string format: %s', $x));
}
$x = preg_replace('/^-/', '', $x, 1, $isNeg);
} else {
throw new AMQPInvalidArgumentException('Only integer and numeric string values are supported');
}
if ($isNeg) {
$x = bcadd($x, -1, 0);
} //in negative domain starting point is -1, not 0
$res = array();
for ($b = 0; $b < $bytes; $b += 2) {
$chnk = (int) bcmod($x, 65536);
$x = bcdiv($x, 65536, 0);
$res[] = pack('n', $isNeg ? ~$chnk : $chnk);
}
if ($x || ($isNeg && ($chnk & 0x8000))) {
throw new AMQPOutOfBoundsException(sprintf('Overflow detected while attempting to pack %s into %s bytes', $ox, $bytes));
}
return implode(array_reverse($res));
}
private function flushbits()
{
if (!empty($this->bits)) {
$this->out .= implode('', array_map('chr', $this->bits));
$this->bits = array();
$this->bitcount = 0;
}
}
/**
* Get what's been encoded so far.
*/
public function getvalue()
{
/* temporarily needed for compatibility with write_bit unit tests */
if ($this->bitcount) {
$this->flushbits();
}
return $this->out;
}
/**
* Write a plain PHP string, with no special encoding.
*/
public function write($s)
{
$this->out .= $s;
return $this;
}
/**
* Write a boolean value.
* (deprecated, use write_bits instead)
*
* @deprecated
* @param $b
* @return $this
*/
public function write_bit($b)
{
$b = (int) (bool) $b;
$shift = $this->bitcount % 8;
if ($shift == 0) {
$last = 0;
} else {
$last = array_pop($this->bits);
}
$last |= ($b << $shift);
array_push($this->bits, $last);
$this->bitcount += 1;
return $this;
}
/**
* Write multiple bits as an octet
*
* @param $bits
* @return $this
*/
public function write_bits($bits)
{
$value = 0;
foreach ($bits as $n => $bit) {
$bit = $bit ? 1 : 0;
$value |= ($bit << $n);
}
$this->out .= chr($value);
return $this;
}
/**
* Write an integer as an unsigned 8-bit value
*
* @param $n
* @return $this
* @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
*/
public function write_octet($n)
{
if ($n < 0 || $n > 255) {
throw new AMQPInvalidArgumentException('Octet out of range: ' . $n);
}
$this->out .= chr($n);
return $this;
}
public function write_signed_octet($n)
{
if (($n < -128) || ($n > 127)) {
throw new AMQPInvalidArgumentException('Signed octet out of range: ' . $n);
}
$this->out .= pack('c', $n);
return $this;
}
/**
* Write an integer as an unsigned 16-bit value
*
* @param $n
* @return $this
* @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
*/
public function write_short($n)
{
if ($n < 0 || $n > 65535) {
throw new AMQPInvalidArgumentException('Short out of range: ' . $n);
}
$this->out .= pack('n', $n);
return $this;
}
public function write_signed_short($n)
{
if (($n < -32768) || ($n > 32767)) {
throw new AMQPInvalidArgumentException('Signed short out of range: ' . $n);
}
$this->out .= $this->correctEndianness(pack('s', $n));
return $this;
}
/**
* Write an integer as an unsigned 32-bit value
*
* @param $n
* @return $this
*/
public function write_long($n)
{
if (($n < 0) || ($n > 4294967295)) {
throw new AMQPInvalidArgumentException('Long out of range: ' . $n);
}
//Numeric strings >PHP_INT_MAX on 32bit are casted to PHP_INT_MAX, damn PHP
if (!$this->is64bits && is_string($n)) {
$n = (float) $n;
}
$this->out .= pack('N', $n);
return $this;
}
/**
* @param $n
* @return $this
*/
private function write_signed_long($n)
{
if (($n < -2147483648) || ($n > 2147483647)) {
throw new AMQPInvalidArgumentException('Signed long out of range: ' . $n);
}
//on my 64bit debian this approach is slightly faster than splitIntoQuads()
$this->out .= $this->correctEndianness(pack('l', $n));
return $this;
}
/**
* Write an integer as an unsigned 64-bit value
*
* @param $n
* @return $this
*/
public function write_longlong($n)
{
if ($n < 0) {
throw new AMQPInvalidArgumentException('Longlong out of range: ' . $n);
}
// if PHP_INT_MAX is big enough for that
// direct $n<=PHP_INT_MAX check is unreliable on 64bit (values close to max) due to limited float precision
if (bcadd($n, -PHP_INT_MAX, 0) <= 0) {
// trick explained in http://www.php.net/manual/fr/function.pack.php#109328
if ($this->is64bits) {
list($hi, $lo) = $this->splitIntoQuads($n);
} else {
$hi = 0;
$lo = $n;
} //on 32bits hi quad is 0 a priori
$this->out .= pack('NN', $hi, $lo);
} else {
try {
$this->out .= self::packBigEndian($n, 8);
} catch (AMQPOutOfBoundsException $ex) {
throw new AMQPInvalidArgumentException('Longlong out of range: ' . $n, 0, $ex);
}
}
return $this;
}
public function write_signed_longlong($n)
{
if ((bcadd($n, PHP_INT_MAX, 0) >= -1) && (bcadd($n, -PHP_INT_MAX, 0) <= 0)) {
if ($this->is64bits) {
list($hi, $lo) = $this->splitIntoQuads($n);
} else {
$hi = $n < 0 ? -1 : 0;
$lo = $n;
} //0xffffffff for negatives
$this->out .= pack('NN', $hi, $lo);
} elseif ($this->is64bits) {
throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n);
} else {
if (bcadd($n, '-9223372036854775807', 0) > 0) {
throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n);
}
try {
//will catch only negative overflow, as values >9223372036854775807 are valid for 8bytes range (unsigned)
$this->out .= self::packBigEndian($n, 8);
} catch (AMQPOutOfBoundsException $ex) {
throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n, 0, $ex);
}
}
return $this;
}
/**
* @param int $n
* @return array
*/
private function splitIntoQuads($n)
{
$n = (int) $n;
return array($n >> 32, $n & 0x00000000ffffffff);
}
/**
* Write a string up to 255 bytes long after encoding.
* Assume UTF-8 encoding
*
* @param $s
* @return $this
* @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
*/
public function write_shortstr($s)
{
$len = mb_strlen($s, 'ASCII');
if ($len > 255) {
throw new AMQPInvalidArgumentException('String too long');
}
$this->write_octet($len);
$this->out .= $s;
return $this;
}
/**
* Write a string up to 2**32 bytes long. Assume UTF-8 encoding
*
* @param $s
* @return $this
*/
public function write_longstr($s)
{
$this->write_long(mb_strlen($s, 'ASCII'));
$this->out .= $s;
return $this;
}
/**
* Supports the writing of Array types, so that you can implement
* array methods, like Rabbitmq's HA parameters
*
* @param AMQPArray|array $a Instance of AMQPArray or PHP array WITHOUT format hints (unlike write_table())
* @return self
*/
public function write_array($a)
{
if (!($a instanceof AMQPArray)) {
$a = new AMQPArray($a);
}
$data = new AMQPWriter();
foreach ($a as $v) {
$data->write_value($v[0], $v[1]);
}
$data = $data->getvalue();
$this->write_long(mb_strlen($data, 'ASCII'));
$this->write($data);
return $this;
}
/**
* Write unix time_t value as 64 bit timestamp
*
* @param $v
* @return $this
*/
public function write_timestamp($v)
{
$this->write_longlong($v);
return $this;
}
/**
* Write PHP array, as table. Input array format: keys are strings,
* values are (type,value) tuples.
*
* @param AMQPTable|array $d Instance of AMQPTable or PHP array WITH format hints (unlike write_array())
* @return self
* @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
*/
public function write_table($d)
{
$typeIsSym = !($d instanceof AMQPTable); //purely for back-compat purposes
$table_data = new AMQPWriter();
foreach ($d as $k => $va) {
list($ftype, $v) = $va;
$table_data->write_shortstr($k);
$table_data->write_value($typeIsSym ? AMQPAbstractCollection::getDataTypeForSymbol($ftype) : $ftype, $v);
}
$table_data = $table_data->getvalue();
$this->write_long(mb_strlen($table_data, 'ASCII'));
$this->write($table_data);
return $this;
}
/**
* for compat with method mapping used by AMQPMessage
*/
public function write_table_object($d)
{
return $this->write_table($d);
}
/**
* @param int $type One of AMQPAbstractCollection::T_* constants
* @param mixed $val
*/
private function write_value($type, $val)
{
//This will find appropriate symbol for given data type for currently selected protocol
//Also will raise an exception on unknown type
$this->write(AMQPAbstractCollection::getSymbolForDataType($type));
switch ($type) {
case AMQPAbstractCollection::T_INT_SHORTSHORT:
$this->write_signed_octet($val);
break;
case AMQPAbstractCollection::T_INT_SHORTSHORT_U:
$this->write_octet($val);
break;
case AMQPAbstractCollection::T_INT_SHORT:
$this->write_signed_short($val);
break;
case AMQPAbstractCollection::T_INT_SHORT_U:
$this->write_short($val);
break;
case AMQPAbstractCollection::T_INT_LONG:
$this->write_signed_long($val);
break;
case AMQPAbstractCollection::T_INT_LONG_U:
$this->write_long($val);
break;
case AMQPAbstractCollection::T_INT_LONGLONG:
$this->write_signed_longlong($val);
break;
case AMQPAbstractCollection::T_INT_LONGLONG_U:
$this->write_longlong($val);
break;
case AMQPAbstractCollection::T_DECIMAL:
$this->write_octet($val->e);
$this->write_signed_long($val->n);
break;
case AMQPAbstractCollection::T_TIMESTAMP:
$this->write_timestamp($val);
break;
case AMQPAbstractCollection::T_BOOL:
$this->write_octet($val ? 1 : 0);
break;
case AMQPAbstractCollection::T_STRING_SHORT:
$this->write_shortstr($val);
break;
case AMQPAbstractCollection::T_STRING_LONG:
$this->write_longstr($val);
break;
case AMQPAbstractCollection::T_ARRAY:
$this->write_array($val);
break;
case AMQPAbstractCollection::T_TABLE:
$this->write_table($val);
break;
case AMQPAbstractCollection::T_VOID:
break;
default:
throw new AMQPInvalidArgumentException(sprintf(
'Unsupported type "%s"',
$type
));
}
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace PhpAmqpLib\Wire;
class AbstractClient
{
/**
* @var bool
*/
protected $is64bits;
/**
* @var bool
*/
protected $isLittleEndian;
public function __construct()
{
$this->is64bits = PHP_INT_SIZE == 8;
$tmp = unpack('S', "\x01\x00"); // to maintain 5.3 compatibility
$this->isLittleEndian = $tmp[1] == 1;
}
/**
* Converts byte-string between native and network byte order, in both directions
*
* @param string $byteStr
* @return string
*/
protected function correctEndianness($byteStr)
{
return $this->isLittleEndian ? strrev($byteStr) : $byteStr;
}
}

View File

@@ -0,0 +1,142 @@
<?php
/* This file was autogenerated by spec/parser.php - Do not modify */
namespace PhpAmqpLib\Wire;
class Constants080
{
public static $AMQP_PROTOCOL_HEADER = "AMQP\x01\x01\x08\x00";
public static $FRAME_TYPES = array(
1 => 'FRAME-METHOD',
2 => 'FRAME-HEADER',
3 => 'FRAME-BODY',
4 => 'FRAME-OOB-METHOD',
5 => 'FRAME-OOB-HEADER',
6 => 'FRAME-OOB-BODY',
7 => 'FRAME-TRACE',
8 => 'FRAME-HEARTBEAT',
4096 => 'FRAME-MIN-SIZE',
206 => 'FRAME-END',
501 => 'FRAME-ERROR',
);
public static $CONTENT_METHODS = array(
0 => '60,40',
1 => '60,50',
2 => '60,60',
3 => '60,71',
4 => '70,50',
5 => '70,70',
6 => '80,40',
7 => '80,50',
8 => '80,60',
9 => '110,10',
10 => '120,40',
11 => '120,41',
);
public static $CLOSE_METHODS = array(
0 => '10,60',
1 => '20,40',
);
public static $GLOBAL_METHOD_NAMES = array(
'10,10' => 'Connection.start',
'10,11' => 'Connection.start_ok',
'10,20' => 'Connection.secure',
'10,21' => 'Connection.secure_ok',
'10,30' => 'Connection.tune',
'10,31' => 'Connection.tune_ok',
'10,40' => 'Connection.open',
'10,41' => 'Connection.open_ok',
'10,50' => 'Connection.redirect',
'10,60' => 'Connection.close',
'10,61' => 'Connection.close_ok',
'20,10' => 'Channel.open',
'20,11' => 'Channel.open_ok',
'20,20' => 'Channel.flow',
'20,21' => 'Channel.flow_ok',
'20,30' => 'Channel.alert',
'20,40' => 'Channel.close',
'20,41' => 'Channel.close_ok',
'30,10' => 'Access.request',
'30,11' => 'Access.request_ok',
'40,10' => 'Exchange.declare',
'40,11' => 'Exchange.declare_ok',
'40,20' => 'Exchange.delete',
'40,21' => 'Exchange.delete_ok',
'50,10' => 'Queue.declare',
'50,11' => 'Queue.declare_ok',
'50,20' => 'Queue.bind',
'50,21' => 'Queue.bind_ok',
'50,30' => 'Queue.purge',
'50,31' => 'Queue.purge_ok',
'50,40' => 'Queue.delete',
'50,41' => 'Queue.delete_ok',
'50,50' => 'Queue.unbind',
'50,51' => 'Queue.unbind_ok',
'60,10' => 'Basic.qos',
'60,11' => 'Basic.qos_ok',
'60,20' => 'Basic.consume',
'60,21' => 'Basic.consume_ok',
'60,30' => 'Basic.cancel',
'60,31' => 'Basic.cancel_ok',
'60,40' => 'Basic.publish',
'60,50' => 'Basic.return',
'60,60' => 'Basic.deliver',
'60,70' => 'Basic.get',
'60,71' => 'Basic.get_ok',
'60,72' => 'Basic.get_empty',
'60,80' => 'Basic.ack',
'60,90' => 'Basic.reject',
'60,100' => 'Basic.recover_async',
'60,110' => 'Basic.recover',
'60,111' => 'Basic.recover_ok',
'70,10' => 'File.qos',
'70,11' => 'File.qos_ok',
'70,20' => 'File.consume',
'70,21' => 'File.consume_ok',
'70,30' => 'File.cancel',
'70,31' => 'File.cancel_ok',
'70,40' => 'File.open',
'70,41' => 'File.open_ok',
'70,50' => 'File.stage',
'70,60' => 'File.publish',
'70,70' => 'File.return',
'70,80' => 'File.deliver',
'70,90' => 'File.ack',
'70,100' => 'File.reject',
'80,10' => 'Stream.qos',
'80,11' => 'Stream.qos_ok',
'80,20' => 'Stream.consume',
'80,21' => 'Stream.consume_ok',
'80,30' => 'Stream.cancel',
'80,31' => 'Stream.cancel_ok',
'80,40' => 'Stream.publish',
'80,50' => 'Stream.return',
'80,60' => 'Stream.deliver',
'90,10' => 'Tx.select',
'90,11' => 'Tx.select_ok',
'90,20' => 'Tx.commit',
'90,21' => 'Tx.commit_ok',
'90,30' => 'Tx.rollback',
'90,31' => 'Tx.rollback_ok',
'100,10' => 'Dtx.select',
'100,11' => 'Dtx.select_ok',
'100,20' => 'Dtx.start',
'100,21' => 'Dtx.start_ok',
'110,10' => 'Tunnel.request',
'120,10' => 'Test.integer',
'120,11' => 'Test.integer_ok',
'120,20' => 'Test.string',
'120,21' => 'Test.string_ok',
'120,30' => 'Test.table',
'120,31' => 'Test.table_ok',
'120,40' => 'Test.content',
'120,41' => 'Test.content_ok',
);
}

View File

@@ -0,0 +1,101 @@
<?php
/* This file was autogenerated by spec/parser.php - Do not modify */
namespace PhpAmqpLib\Wire;
class Constants091
{
public static $AMQP_PROTOCOL_HEADER = "AMQP\x00\x00\x09\x01";
public static $FRAME_TYPES = array(
1 => 'FRAME-METHOD',
2 => 'FRAME-HEADER',
3 => 'FRAME-BODY',
8 => 'FRAME-HEARTBEAT',
4096 => 'FRAME-MIN-SIZE',
206 => 'FRAME-END',
501 => 'FRAME-ERROR',
);
public static $CONTENT_METHODS = array(
0 => '60,40',
1 => '60,50',
2 => '60,60',
3 => '60,71',
);
public static $CLOSE_METHODS = array(
0 => '10,50',
1 => '20,40',
);
public static $GLOBAL_METHOD_NAMES = array(
'10,10' => 'Connection.start',
'10,11' => 'Connection.start_ok',
'10,20' => 'Connection.secure',
'10,21' => 'Connection.secure_ok',
'10,30' => 'Connection.tune',
'10,31' => 'Connection.tune_ok',
'10,40' => 'Connection.open',
'10,41' => 'Connection.open_ok',
'10,50' => 'Connection.close',
'10,51' => 'Connection.close_ok',
'10,60' => 'Connection.blocked',
'10,61' => 'Connection.unblocked',
'20,10' => 'Channel.open',
'20,11' => 'Channel.open_ok',
'20,20' => 'Channel.flow',
'20,21' => 'Channel.flow_ok',
'20,40' => 'Channel.close',
'20,41' => 'Channel.close_ok',
'30,10' => 'Access.request',
'30,11' => 'Access.request_ok',
'40,10' => 'Exchange.declare',
'40,11' => 'Exchange.declare_ok',
'40,20' => 'Exchange.delete',
'40,21' => 'Exchange.delete_ok',
'40,30' => 'Exchange.bind',
'40,31' => 'Exchange.bind_ok',
'40,40' => 'Exchange.unbind',
'40,51' => 'Exchange.unbind_ok',
'50,10' => 'Queue.declare',
'50,11' => 'Queue.declare_ok',
'50,20' => 'Queue.bind',
'50,21' => 'Queue.bind_ok',
'50,30' => 'Queue.purge',
'50,31' => 'Queue.purge_ok',
'50,40' => 'Queue.delete',
'50,41' => 'Queue.delete_ok',
'50,50' => 'Queue.unbind',
'50,51' => 'Queue.unbind_ok',
'60,10' => 'Basic.qos',
'60,11' => 'Basic.qos_ok',
'60,20' => 'Basic.consume',
'60,21' => 'Basic.consume_ok',
'60,30' => 'Basic.cancel',
'60,31' => 'Basic.cancel_ok',
'60,40' => 'Basic.publish',
'60,50' => 'Basic.return',
'60,60' => 'Basic.deliver',
'60,70' => 'Basic.get',
'60,71' => 'Basic.get_ok',
'60,72' => 'Basic.get_empty',
'60,80' => 'Basic.ack',
'60,90' => 'Basic.reject',
'60,100' => 'Basic.recover_async',
'60,110' => 'Basic.recover',
'60,111' => 'Basic.recover_ok',
'60,120' => 'Basic.nack',
'90,10' => 'Tx.select',
'90,11' => 'Tx.select_ok',
'90,20' => 'Tx.commit',
'90,21' => 'Tx.commit_ok',
'90,30' => 'Tx.rollback',
'90,31' => 'Tx.rollback_ok',
'85,10' => 'Confirm.select',
'85,11' => 'Confirm.select_ok',
);
}

View File

@@ -0,0 +1,209 @@
<?php
namespace PhpAmqpLib\Wire;
use PhpAmqpLib\Channel\AMQPChannel;
/**
* Abstract base class for AMQP content. Subclasses should override
* the PROPERTIES attribute.
*/
abstract class GenericContent
{
/** @var AMQPChannel[] */
public $delivery_info = array();
/** @var array Final property definitions */
protected $prop_types;
/** @var array Properties content */
private $properties = array();
/** @var string Compiled properties */
private $serialized_properties;
/**
* @var array
*/
protected static $PROPERTIES = array(
'dummy' => 'shortstr'
);
/**
* @param $props
* @param null $prop_types
*/
public function __construct($props, $prop_types = null)
{
if ($prop_types) {
$this->prop_types = $prop_types;
} else {
$this->prop_types = self::$PROPERTIES;
}
if ($props) {
$this->properties = array_intersect_key($props, $this->prop_types);
}
}
/**
* Check whether a property exists in the 'properties' dictionary
* or if present - in the 'delivery_info' dictionary.
*
* @param string $name
* @return bool
*/
public function has($name)
{
return isset($this->properties[$name]) || isset($this->delivery_info[$name]);
}
/**
* Look for additional properties in the 'properties' dictionary,
* and if present - the 'delivery_info' dictionary.
*
* @param string $name
* @throws \OutOfBoundsException
* @return mixed|AMQPChannel
*/
public function get($name)
{
if (isset($this->properties[$name])) {
return $this->properties[$name];
}
if (isset($this->delivery_info[$name])) {
return $this->delivery_info[$name];
}
throw new \OutOfBoundsException(sprintf(
'No "%s" property',
$name
));
}
/**
* Returns the properties content
*
* @return array
*/
public function get_properties()
{
return $this->properties;
}
/**
* Sets a property value
*
* @param string $name The property name (one of the property definition)
* @param mixed $value The property value
* @throws \OutOfBoundsException
*/
public function set($name, $value)
{
if (!array_key_exists($name, $this->prop_types)) {
throw new \OutOfBoundsException(sprintf(
'No "%s" property',
$name
));
}
$this->properties[$name] = $value;
}
/**
* Given the raw bytes containing the property-flags and
* property-list from a content-frame-header, parse and insert
* into a dictionary stored in this object as an attribute named
* 'properties'.
*
* @param AMQPReader $r
* NOTE: do not mutate $reader
*/
public function load_properties($r)
{
// Read 16-bit shorts until we get one with a low bit set to zero
$flags = array();
while (true) {
$flag_bits = $r->read_short();
$flags[] = $flag_bits;
if (($flag_bits & 1) == 0) {
break;
}
}
$shift = 0;
$d = array();
foreach ($this->prop_types as $key => $proptype) {
if ($shift == 0) {
if (!$flags) {
break;
}
$flag_bits = array_shift($flags);
$shift = 15;
}
if ($flag_bits & (1 << $shift)) {
$d[$key] = $r->{'read_' . $proptype}();
}
$shift -= 1;
}
$this->properties = $d;
}
/**
* Serializes the 'properties' attribute (a dictionary) into the
* raw bytes making up a set of property flags and a property
* list, suitable for putting into a content frame header.
*
* @return string
* @todo Inject the AMQPWriter to make the method easier to test
*/
public function serialize_properties()
{
if (!empty($this->serialized_properties)) {
return $this->serialized_properties;
}
$shift = 15;
$flag_bits = 0;
$flags = array();
$raw_bytes = new AMQPWriter();
foreach ($this->prop_types as $key => $prototype) {
$val = isset($this->properties[$key]) ? $this->properties[$key] : null;
// Very important: PHP type eval is weak, use the === to test the
// value content. Zero or false value should not be removed
if ($val === null) {
$shift -= 1;
continue;
}
if ($shift === 0) {
$flags[] = $flag_bits;
$flag_bits = 0;
$shift = 15;
}
$flag_bits |= (1 << $shift);
if ($prototype != 'bit') {
$raw_bytes->{'write_' . $prototype}($val);
}
$shift -= 1;
}
$flags[] = $flag_bits;
$result = new AMQPWriter();
foreach ($flags as $flag_bits) {
$result->write_short($flag_bits);
}
$result->write($raw_bytes->getvalue());
$this->serialized_properties = $result->getvalue();
return $this->serialized_properties;
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace PhpAmqpLib\Wire\IO;
abstract class AbstractIO
{
/**
* @param $n
* @return mixed
*/
abstract public function read($n);
/**
* @param $data
* @return mixed
*/
abstract public function write($data);
/**
* @return mixed
*/
abstract public function close();
/**
* @param $sec
* @param $usec
* @return mixed
*/
abstract public function select($sec, $usec);
/**
* @return mixed
*/
abstract public function connect();
/**
* @return mixed
*/
abstract public function reconnect();
/**
* @return mixed
*/
abstract public function getSocket();
}

View File

@@ -0,0 +1,195 @@
<?php
namespace PhpAmqpLib\Wire\IO;
use PhpAmqpLib\Exception\AMQPIOException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
class SocketIO extends AbstractIO
{
/** @var string */
protected $host;
/** @var int */
protected $port;
/** @var int */
protected $timeout;
/** @var resource */
private $sock;
/** @var bool */
private $keepalive;
/**
* @param string $host
* @param int $port
* @param int $timeout
* @param bool $keepalive
*/
public function __construct($host, $port, $timeout, $keepalive = false)
{
$this->host = $host;
$this->port = $port;
$this->timeout = $timeout;
$this->keepalive = $keepalive;
}
/**
* Sets up the socket connection
*
* @throws \Exception
*/
public function connect()
{
$this->sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($this->sock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $this->timeout, 'usec' => 0));
socket_set_option($this->sock, SOL_SOCKET, SO_SNDTIMEO, array('sec' => $this->timeout, 'usec' => 0));
if (!socket_connect($this->sock, $this->host, $this->port)) {
$errno = socket_last_error($this->sock);
$errstr = socket_strerror($errno);
throw new AMQPIOException(sprintf(
'Error Connecting to server (%s): %s',
$errno,
$errstr
), $errno);
}
socket_set_block($this->sock);
socket_set_option($this->sock, SOL_TCP, TCP_NODELAY, 1);
if ($this->keepalive) {
$this->enable_keepalive();
}
}
/**
* @return resource
*/
public function getSocket()
{
return $this->sock;
}
/**
* Reconnects the socket
*/
public function reconnect()
{
$this->close();
$this->connect();
}
/**
* @param $n
* @return mixed|string
* @throws \PhpAmqpLib\Exception\AMQPIOException
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
*/
public function read($n)
{
$res = '';
$read = 0;
$buf = socket_read($this->sock, $n);
while ($read < $n && $buf !== '' && $buf !== false) {
// Null sockets are invalid, throw exception
if (is_null($this->sock)) {
throw new AMQPRuntimeException(sprintf(
'Socket was null! Last SocketError was: %s',
socket_strerror(socket_last_error())
));
}
$read += mb_strlen($buf, 'ASCII');
$res .= $buf;
$buf = socket_read($this->sock, $n - $read);
}
if (mb_strlen($res, 'ASCII') != $n) {
throw new AMQPIOException(sprintf(
'Error reading data. Received %s instead of expected %s bytes',
mb_strlen($res, 'ASCII'),
$n
));
}
return $res;
}
/**
* @param $data
* @return mixed|void
* @throws \PhpAmqpLib\Exception\AMQPIOException
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
*/
public function write($data)
{
$len = mb_strlen($data, 'ASCII');
while (true) {
// Null sockets are invalid, throw exception
if (is_null($this->sock)) {
throw new AMQPRuntimeException(sprintf(
'Socket was null! Last SocketError was: %s',
socket_strerror(socket_last_error())
));
}
$sent = socket_write($this->sock, $data, $len);
if ($sent === false) {
throw new AMQPIOException(sprintf(
'Error sending data. Last SocketError: %s',
socket_strerror(socket_last_error())
));
}
// Check if the entire message has been sent
if ($sent < $len) {
// If not sent the entire message.
// Get the part of the message that has not yet been sent as message
$data = mb_substr($data, $sent, mb_strlen($data, 'ASCII') - $sent, 'ASCII');
// Get the length of the not sent part
$len -= $sent;
} else {
break;
}
}
}
public function close()
{
if (is_resource($this->sock)) {
socket_close($this->sock);
}
$this->sock = null;
}
/**
* @param $sec
* @param $usec
* @return int|mixed
*/
public function select($sec, $usec)
{
$read = array($this->sock);
$write = null;
$except = null;
return socket_select($read, $write, $except, $sec, $usec);
}
/**
* @throws \PhpAmqpLib\Exception\AMQPIOException
*/
protected function enable_keepalive()
{
if (!defined('SOL_SOCKET') || !defined('SO_KEEPALIVE')) {
throw new AMQPIOException('Can not enable keepalive: SOL_SOCKET or SO_KEEPALIVE is not defined');
}
socket_set_option($this->sock, SOL_SOCKET, SO_KEEPALIVE, 1);
}
}

View File

@@ -0,0 +1,309 @@
<?php
namespace PhpAmqpLib\Wire\IO;
use PhpAmqpLib\Exception\AMQPIOException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Exception\AMQPTimeoutException;
use PhpAmqpLib\Helper\MiscHelper;
use PhpAmqpLib\Wire\AMQPWriter;
class StreamIO extends AbstractIO
{
/** @var string */
protected $host;
/** @var int */
protected $port;
/** @var int */
protected $connection_timeout;
/** @var int */
protected $read_write_timeout;
/** @var resource */
protected $context;
/** @var bool */
protected $keepalive;
/** @var int */
protected $heartbeat;
/** @var float */
protected $last_read;
/** @var float */
protected $last_write;
/** @var resource */
private $sock;
/** @var bool */
private $canDispatchPcntlSignal;
/**
* @param string $host
* @param int $port
* @param int $connection_timeout
* @param int $read_write_timeout
* @param null $context
* @param bool $keepalive
* @param int $heartbeat
*/
public function __construct(
$host,
$port,
$connection_timeout,
$read_write_timeout,
$context = null,
$keepalive = false,
$heartbeat = 0
) {
$this->host = $host;
$this->port = $port;
$this->connection_timeout = $connection_timeout;
$this->read_write_timeout = $read_write_timeout;
$this->context = $context;
$this->keepalive = $keepalive;
$this->heartbeat = $heartbeat;
$this->canDispatchPcntlSignal = extension_loaded('pcntl') && function_exists('pcntl_signal_dispatch')
&& (defined('AMQP_WITHOUT_SIGNALS') ? !AMQP_WITHOUT_SIGNALS : true);
}
/**
* Sets up the stream connection
*
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
* @throws \Exception
*/
public function connect()
{
$errstr = $errno = null;
if ($this->context) {
$remote = sprintf('ssl://%s:%s', $this->host, $this->port);
$this->sock = @stream_socket_client(
$remote,
$errno,
$errstr,
$this->connection_timeout,
STREAM_CLIENT_CONNECT,
$this->context
);
} else {
$remote = sprintf('tcp://%s:%s', $this->host, $this->port);
$this->sock = @stream_socket_client(
$remote,
$errno,
$errstr,
$this->connection_timeout,
STREAM_CLIENT_CONNECT
);
}
if (!$this->sock) {
throw new AMQPRuntimeException(sprintf(
'Error Connecting to server (%s): %s',
$errno,
$errstr
), $errno);
}
list($sec, $uSec) = MiscHelper::splitSecondsMicroseconds($this->read_write_timeout);
if (!stream_set_timeout($this->sock, $sec, $uSec)) {
throw new AMQPIOException('Timeout could not be set');
}
stream_set_blocking($this->sock, 1);
if ($this->keepalive) {
$this->enable_keepalive();
}
}
/**
* Reconnects the socket
*/
public function reconnect()
{
$this->close();
$this->connect();
}
/**
* @param $n
* @throws \PhpAmqpLib\Exception\AMQPIOException
* @return mixed|string
*/
public function read($n)
{
$res = '';
$read = 0;
while ($read < $n && !feof($this->sock) && (false !== ($buf = fread($this->sock, $n - $read)))) {
$this->check_heartbeat();
if ($buf === '') {
if ($this->canDispatchPcntlSignal) {
pcntl_signal_dispatch();
}
continue;
}
$read += mb_strlen($buf, 'ASCII');
$res .= $buf;
$this->last_read = microtime(true);
}
if (mb_strlen($res, 'ASCII') != $n) {
throw new AMQPIOException(sprintf(
'Error reading data. Received %s instead of expected %s bytes',
mb_strlen($res, 'ASCII'),
$n
));
}
return $res;
}
/**
* @param $data
* @return mixed|void
* @throws \PhpAmqpLib\Exception\AMQPRuntimeException
* @throws \PhpAmqpLib\Exception\AMQPTimeoutException
*/
public function write($data)
{
$len = mb_strlen($data, 'ASCII');
while (true) {
if (is_null($this->sock)) {
throw new AMQPRuntimeException('Broken pipe or closed connection');
}
if (false === ($written = @fwrite($this->sock, $data))) {
throw new AMQPRuntimeException('Error sending data');
}
if ($written === 0) {
throw new AMQPRuntimeException('Broken pipe or closed connection');
}
if ($this->timed_out()) {
throw new AMQPTimeoutException('Error sending data. Socket connection timed out');
}
$len = $len - $written;
if ($len > 0) {
$data = mb_substr($data, 0 - $len, 0 - $len, 'ASCII');
} else {
$this->last_write = microtime(true);
break;
}
}
}
/**
* Heartbeat logic: check connection health here
*/
protected function check_heartbeat()
{
// ignore unless heartbeat interval is set
if ($this->heartbeat !== 0 && $this->last_read && $this->last_write) {
$t = microtime(true);
$t_read = round($t - $this->last_read);
$t_write = round($t - $this->last_write);
// server has gone away
if (($this->heartbeat * 2) < $t_read) {
$this->reconnect();
}
// time for client to send a heartbeat
if (($this->heartbeat / 2) < $t_write) {
$this->write_heartbeat();
}
}
}
/**
* Sends a heartbeat message
*/
protected function write_heartbeat()
{
$pkt = new AMQPWriter();
$pkt->write_octet(8);
$pkt->write_short(0);
$pkt->write_long(0);
$pkt->write_octet(0xCE);
$val = $pkt->getvalue();
$this->write($pkt->getvalue());
}
public function close()
{
if (is_resource($this->sock)) {
fclose($this->sock);
}
$this->sock = null;
}
/**
* @return resource
*/
public function get_socket()
{
return $this->sock;
}
/**
* @return resource
*/
public function getSocket()
{
return $this->get_socket();
}
/**
* @param $sec
* @param $usec
* @return int|mixed
*/
public function select($sec, $usec)
{
$read = array($this->sock);
$write = null;
$except = null;
return stream_select($read, $write, $except, $sec, $usec);
}
/**
* @return mixed
*/
protected function timed_out()
{
// get status of socket to determine whether or not it has timed out
$info = stream_get_meta_data($this->sock);
return $info['timed_out'];
}
/**
* @throws \PhpAmqpLib\Exception\AMQPIOException
*/
protected function enable_keepalive()
{
if (!function_exists('socket_import_stream')) {
throw new AMQPIOException('Can not enable keepalive: function socket_import_stream does not exist');
}
if (!defined('SOL_SOCKET') || !defined('SO_KEEPALIVE')) {
throw new AMQPIOException('Can not enable keepalive: SOL_SOCKET or SO_KEEPALIVE is not defined');
}
$socket = socket_import_stream($this->sock);
socket_set_option($socket, SOL_SOCKET, SO_KEEPALIVE, 1);
}
}

View File

@@ -0,0 +1,64 @@
<?php
// @codingStandardsIgnoreFile
// @codeCoverageIgnoreStart
// this is an autogenerated file - do not edit
spl_autoload_register(
function($class) {
static $classes = null;
if ($classes === null) {
$classes = array(
'phpamqplib\\channel\\abstractchannel' => '/Channel/AbstractChannel.php',
'phpamqplib\\channel\\amqpchannel' => '/Channel/AMQPChannel.php',
'phpamqplib\\connection\\abstractconnection' => '/Connection/AbstractConnection.php',
'phpamqplib\\connection\\amqpconnection' => '/Connection/AMQPConnection.php',
'phpamqplib\\connection\\amqplazyconnection' => '/Connection/AMQPLazyConnection.php',
'phpamqplib\\connection\\amqpsocketconnection' => '/Connection/AMQPSocketConnection.php',
'phpamqplib\\connection\\amqpsslconnection' => '/Connection/AMQPSSLConnection.php',
'phpamqplib\\connection\\amqpstreamconnection' => '/Connection/AMQPStreamConnection.php',
'phpamqplib\\exception\\amqpbasiccancelexception' => '/Exception/AMQPBasicCancelException.php',
'phpamqplib\\exception\\amqpchannelexception' => '/Exception/AMQPChannelException.php',
'phpamqplib\\exception\\amqpconnectionexception' => '/Exception/AMQPConnectionException.php',
'phpamqplib\\exception\\amqpexception' => '/Exception/AMQPException.php',
'phpamqplib\\exception\\amqpexceptioninterface' => '/Exception/AMQPExceptionInterface.php',
'phpamqplib\\exception\\amqpinvalidargumentexception' => '/Exception/AMQPInvalidArgumentException.php',
'phpamqplib\\exception\\amqpioexception' => '/Exception/AMQPIOException.php',
'phpamqplib\\exception\\amqplogicexception' => '/Exception/AMQPLogicException.php',
'phpamqplib\\exception\\amqpoutofboundsexception' => '/Exception/AMQPOutOfBoundsException.php',
'phpamqplib\\exception\\amqpoutofrangeexception' => '/Exception/AMQPOutOfRangeException.php',
'phpamqplib\\exception\\amqpprotocolchannelexception' => '/Exception/AMQPProtocolChannelException.php',
'phpamqplib\\exception\\amqpprotocolconnectionexception' => '/Exception/AMQPProtocolConnectionException.php',
'phpamqplib\\exception\\amqpprotocolexception' => '/Exception/AMQPProtocolException.php',
'phpamqplib\\exception\\amqpruntimeexception' => '/Exception/AMQPRuntimeException.php',
'phpamqplib\\exception\\amqptimeoutexception' => '/Exception/AMQPTimeoutException.php',
'phpamqplib\\helper\\mischelper' => '/Helper/MiscHelper.php',
'phpamqplib\\helper\\protocol\\methodmap080' => '/Helper/Protocol/MethodMap080.php',
'phpamqplib\\helper\\protocol\\methodmap091' => '/Helper/Protocol/MethodMap091.php',
'phpamqplib\\helper\\protocol\\protocol080' => '/Helper/Protocol/Protocol080.php',
'phpamqplib\\helper\\protocol\\protocol091' => '/Helper/Protocol/Protocol091.php',
'phpamqplib\\helper\\protocol\\wait080' => '/Helper/Protocol/Wait080.php',
'phpamqplib\\helper\\protocol\\wait091' => '/Helper/Protocol/Wait091.php',
'phpamqplib\\message\\amqpmessage' => '/Message/AMQPMessage.php',
'phpamqplib\\wire\\abstractclient' => '/Wire/AbstractClient.php',
'phpamqplib\\wire\\amqpabstractcollection' => '/Wire/AMQPAbstractCollection.php',
'phpamqplib\\wire\\amqparray' => '/Wire/AMQPArray.php',
'phpamqplib\\wire\\amqpdecimal' => '/Wire/AMQPDecimal.php',
'phpamqplib\\wire\\amqpreader' => '/Wire/AMQPReader.php',
'phpamqplib\\wire\\amqptable' => '/Wire/AMQPTable.php',
'phpamqplib\\wire\\amqpwriter' => '/Wire/AMQPWriter.php',
'phpamqplib\\wire\\constants080' => '/Wire/Constants080.php',
'phpamqplib\\wire\\constants091' => '/Wire/Constants091.php',
'phpamqplib\\wire\\genericcontent' => '/Wire/GenericContent.php',
'phpamqplib\\wire\\io\\abstractio' => '/Wire/IO/AbstractIO.php',
'phpamqplib\\wire\\io\\socketio' => '/Wire/IO/SocketIO.php',
'phpamqplib\\wire\\io\\streamio' => '/Wire/IO/StreamIO.php'
);
}
$cn = strtolower($class);
if (isset($classes[$cn])) {
require __DIR__ . $classes[$cn];
}
},
true,
false
);
// @codeCoverageIgnoreEnd