SkillAgentSearch skills...

Bunny

Performant pure-PHP AMQP (RabbitMQ) sync/async (ReactPHP) library

Install / Use

/learn @jakubkulhan/Bunny
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

BunnyPHP

Continuous Integration Downloads this Month Latest stable

Performant pure-PHP AMQP (RabbitMQ) non-blocking ReactPHP library

Requirements

BunnyPHP requires PHP 8.1 and newer.

Installation

Add as Composer dependency:

$ composer require bunny/bunny:@^0.6dev

Comparison

You might ask if there isn't a library/extension to connect to AMQP broker (e.g. RabbitMQ) already. Yes, there are multiple options:

Why should you want to choose BunnyPHP instead?

  • You want nice idiomatic PHP API to work with (I'm looking at you, php-amqplib). BunnyPHP interface follows PHP's common coding standards and naming conventions. See tutorial.

  • You can't (don't want to) install PECL extension that has latest stable version in 2014. BunnyPHP isn't as such marked as stable yet. But it is already being used in production.

  • You have both classic CLI/FPM and ReactPHP applications and need to connect to RabbitMQ. BunnyPHP comes with an asynchronous client with a synchronous API using Fibers.

Apart from that BunnyPHP is more performant than main competing library, php-amqplib. See benchmark/ directory and php-amqplib's benchmark/. (For ext-amp https://gist.github.com/WyriHaximus/65fd98e099820aded1b79e9111e02916 is used.)

Benchmarks were run as:

$ php benchmark/producer.php N & php benchmark/consumer.php

| Library | N (# messages) | Produce sec | Produce msg/sec | Consume sec | Consume msg/sec | |-------------|---------------:|------------:|----------------:|------------:|----------------:| | php-amqplib | 100 | 0.000671 | 148998 | 0.001714 | 58343 | | ext-amqp | 100 | 0.000302 | 331042 | 0.008915 | 11217 | | bunnyphp | 100 | 0.000194 | 515271 | 0.000939 | 106508 | | bunnyphp +/-| | | +345.8%/+155.6% | | +182.5%/+949.5% | | php-amqplib | 1000 | 0.004827 | 207167 | 0.015166 | 65937 | | ext-amqp | 1000 | 0.002399 | 416846 | 0.078373 | 12760 | | bunnyphp | 1000 | 0.001597 | 626202 | 0.011139 | 89773 | | bunnyphp +/-| | | +302.2%/+150.2% | | +136.1%/+703.5% | | php-amqplib | 10000 | 0.060204 | 166102 | 0.147772 | 67672 | | ext-amqp | 10000 | 0.022735 | 439853 | 0.754800 | 13249 | | bunnyphp | 10000 | 0.016441 | 608232 | 0.106685 | 93734 | | bunnyphp +/-| | | +366.1%/+138.2% | | +138.5%/+707.4% | | php-amqplib | 100000 | 1.158033 | 90276 | 1.477762 | 67670 | | ext-amqp | 100000 | 0.952319 | 105007 | 7.494665 | 13343 | | bunnyphp | 100000 | 0.812430 | 123088 | 1.073454 | 93157 | | bunnyphp +/-| | | +136.3%/+117.2% | | +137.6%/+698.1% | | php-amqplib | 1000000 | 18.64132 | 53644 | 18.992902 | 52651 | | ext-amqp | 1000000 | 12.86827 | 77710 | 89.432139 | 11182 | | bunnyphp | 1000000 | 11.63421 | 85953 | 11.947426 | 83700 | | bunnyphp +/-| | | +160.2%/+110.6% | | +158.9%/+748.5% |

Quick Start

Producing

use Bunny\Client;
use Bunny\Configuration;
use React\EventLoop\Loop;

use function React\Async\async;

$configuration = new Configuration(
    host:     'HOSTNAME',
    vhost:    'VHOST',    // The default vhost is '/'
    user:     'USERNAME', // The default user is 'guest'
    password: 'PASSWORD', // The default password is 'guest'
);

$bunny = new Client($configuration);
Loop::futureTick(async(static function (): void {
  $bunny->channel()->publish(
    body:       $message,     // The message you're publishing as a string
    routingKey: 'queue_name', // Routing key, in this example the queue's name
  );
  $bunny->disconnect();
}));

// Unlike the consumer example, we're not setting signal handlers before this a very quick operation

Consuming

use Bunny\Client;
use Bunny\Configuration;
use React\EventLoop\Loop;

use function React\Async\async;

$configuration = new Configuration(
    host:     'HOSTNAME',
    vhost:    'VHOST',    // The default vhost is '/'
    user:     'USERNAME', // The default user is 'guest'
    password: 'PASSWORD', // The default password is 'guest'
);

$consumerCleanUp = static function () {};
$bunny = new Client($configuration);
Loop::futureTick(async(static function () use (&$consumerCleanUp): void {
    $channel = $bunny->channel();
    $response = $channel->consume(
        async(static function (Message $message, Channel $channel, Client $bunny) {
            $success = handleMessage($message); // Handle your message here
    
            if ($success) {
                $channel->ack($message); // Acknowledge message
                return;
            }
    
            $channel->nack($message); // Mark message fail, message will be redelivered
        }),
        'queue_name',
    );
    $consumerCleanUp = static fn () => $channel->cancel($response->consumerTag);
}));

// Unlike the producer example, we do need signal handlers because this process can run for weeks and we want to shut down cleanly
$signals = [SIGINT, SIGTERM, SIGHUP];
$signalHandler = async(static function () use ($signals, &$signalHandler, &$consumerCleanUp, $bunny): void {
  foreach ($signals as $signal) {
      Loop::removeSignal($signal, $signalHandler);
  }
  
  $consumerCleanUp();
  $bunny->disconnect();
}))
foreach ($signals as $signal) {
    Loop::addSignal($signal, $signalHandler);
}

Always run moving parts in a fiber

Since v0.6 Bunny has been rebuilt using Fibers. This means that every method on Client and Channel must be called inside a fiber. In the examples this will be called using a Loop::futureTick call. In your applications this might be some other trigger like a HTTP request. As long as somewhere in the direct call stack this call is inside a fiber you'll be good.

All the examples from this point on will include a Full Example on how to use that specific example using a full connection cycle inside a fiber the example. For the above that would be the following, even tho it's not doing anything:

use Bunny\Client;
use Bunny\Configuration;
use React\EventLoop\Loop;

use function React\Async\async;

$configuration = new Configuration(
    host:     'HOSTNAME',
    vhost:    'VHOST',    // The default vhost is '/'
    user:     'USERNAME', // The default user is 'guest'
    password: 'PASSWORD', // The default password is 'guest'
);

$bunny = new Client($configuration);
Loop::futureTick(async(static function (): void {
  $bunny->connect();
}));

Tutorial

Connecting

When instantiating the BunnyPHP Client accepts an array with connection options:

use Bunny\Client;
use Bunny\Configuration;

$configuration = new Configuration(
    host:     'HOSTNAME',
    vhost:    'VHOST',    // The default vhost is '/'
    user:     'USERNAME', // The default user is 'guest'
    password: 'PASSWORD', // The default password is 'guest'
);

$bunny = new Client($configuration);
$bunny->connect();
<details> <summary>Using DSN</summary>
use Bunny\Client;
use Bunny\Configuration;

$configuration = Configuration::fromDSN('amqp://USERNAME:PASSWORD@HOSTNAME/VHOST');

$bunny = new Client($configuration);
$bunny->connect();
</details>

Connecting securely using TLS(/SSL)

Options for TLS-connections should be specified as array tls:

use Bunny\Client;
use Bunny\Configuration;

$configuration = new Configuration(
    host:     'HOSTNAME',
    vhost:    'VHOST',    // The default vhost is '/'
    user:     'USERNAME', // The default user is 'guest'
    password: 'PASSWORD', // The default password is 'guest'
    tls:      [
        'cafile'      => 'ca.pem',
        'local_cert'  => 'client.cert',
        'local_pk'    => 'client.key',
    ],
);

$bunny = new Client($configuration);
$bunny->connect();
<details> <summary>Using DSN</summary>
use Bunny\Client;
use Bunny\Configuration;

$configuration = Configuration::fromDSN(
    'amqp://USERNAME:PASSWORD@HOSTNAME/VHOST?tls[cafile]=ca.pem&tls[local_cert]=client.cert&tls[local_pk]=client.key',
);

$bunny = new Client($configuration);
$bunny->connect();
</details>

For options description - please see SSL context options.

Note: invalid TLS configuration will cause connection failure.

See also common configuration variants.

Providing client properties

Client Connections can present their capabilities to a server by presenting an optional client_properties table when establishing a connection.

For example, a connection name may be provided by setting the [connection_name property](https://www

Related Skills

View on GitHub
GitHub Stars741
CategoryDevelopment
Updated6d ago
Forks107

Languages

PHP

Security Score

100/100

Audited on Mar 14, 2026

No findings