pdezwart/php-amqp — pull request #133 by pinepain (2014-11-29 21:13:17+00:00)
Title: [WIP] Add multy basic.consume support

As mentioned in issue https://github.com/pdezwart/php-amqp/issues/132, we don't have support for multiple `basic.consume` calls support, which limit AMQP protocol power significantly.

There was attempt to add support for multiple queues consumer (I guess PR https://github.com/pdezwart/php-amqp/pull/71 was about it in some way), but it adds too much code to solve such problem.

This PR allow us to omit specifying callback for `AMQPQueue::consume()` method and just send `basic.consume` AMQP method to broker (and assign consumer tag if call succeed).

Potentials cons may comes when multiple `AMQPQueue::consume()` called with different flags, like one with autoack enabled, and the other no, then acking message which delivered already auto-acked will lead to error.
This should not be a mission-critical for usage, but let's explain it:

Here is demonstration:

```
connect();

$ch = new AMQPChannel($cnn);

$ex = new AMQPExchange($ch);
$ex->setName('exchange');
$ex->setType(AMQP_EX_TYPE_TOPIC);
$ex->declareExchange();

$q1 = new AMQPQueue($ch);
$q1->setName('queue-one');
$q1->declareQueue();
$q1->bind($ex->getName(), 'routing.one');

$q2 = new AMQPQueue($ch);
$q2->setName('queue-two');
$q2->declareQueue();
$q2->bind($ex->getName(), 'routing.two');

$ex->publish('message1', 'routing.one');
$ex->publish('message2', 'routing.two');

$counter = 2;

$q1->consume(null, AMQP_AUTOACK);
$q2->consume(function (AMQPEnvelope $envelope, AMQPQueue $queue) use ($counter) {
echo $envelope->getDeliveryTag(), ' ', $envelope->getBody(), " ", $queue->getName(), PHP_EOL;
$queue->ack($envelope->getDeliveryTag());
return (--$counter > 0);
}, AMQP_NOPARAM);
```

Will gives us:
```
1 message1 queue-two
2 message2 queue-two
```
and will hang, and we'll get in rabbitmq log

```
{amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'}
```

Also, it may be a bit ambiguous to have second argument passed to consumer callback (`AMQPQueue $queue`) always be a `AMQPQueue` object consumer callback was called from.

Here is demonstration:

```
connect();

$ch = new AMQPChannel($cnn);
$ex = new AMQPExchange($ch);
$ex->setName('exchange');
$ex->setType(AMQP_EX_TYPE_TOPIC);
$ex->declareExchange();

$q1 = new AMQPQueue($ch);
$q1->setName('queue-one');
$q1->declareQueue();
$q1->bind($ex->getName(), 'routing.one');

$q2 = new AMQPQueue($ch);
$q2->setName('queue-two');
$q2->declareQueue();
$q2->bind($ex->getName(), 'routing.two');

$ex->publish('message1', 'routing.one');
$ex->publish('message2', 'routing.two');

$q1->consume();
$q2->consume(function (AMQPEnvelope $envelope, AMQPQueue $queue) {
echo $envelope->getBody(), " ", $queue->getName(), PHP_EOL;
});
```

Which show us (meta-code,

```
message1 queue-two
message2 queue-two
```

Literally, it is not a problem, while under the hood both queues should operates on the same channel, so acking or nacking will works as expected.

Also, there are a lot similar potential pitfalls with this extension, so until pros weights more than cons we can allow ourselves such fixes.