{"id":13623547,"url":"https://github.com/jakubkulhan/bunny","last_synced_at":"2025-10-05T09:41:36.556Z","repository":{"id":28754301,"uuid":"32276372","full_name":"jakubkulhan/bunny","owner":"jakubkulhan","description":"Performant pure-PHP AMQP (RabbitMQ) sync/async (ReactPHP) library","archived":false,"fork":false,"pushed_at":"2025-09-18T22:40:30.000Z","size":488,"stargazers_count":737,"open_issues_count":42,"forks_count":107,"subscribers_count":36,"default_branch":"0.6.x","last_synced_at":"2025-10-04T04:36:26.898Z","etag":null,"topics":["amqp","amqp-client","amqp0-9-1","php","php-library","rabbitmq"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jakubkulhan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2015-03-15T18:20:50.000Z","updated_at":"2025-09-21T18:27:15.000Z","dependencies_parsed_at":"2024-02-16T20:30:24.127Z","dependency_job_id":"6b8d2eab-d32a-4b7b-be53-817efb0de87c","html_url":"https://github.com/jakubkulhan/bunny","commit_stats":{"total_commits":124,"total_committers":33,"mean_commits":3.757575757575758,"dds":0.75,"last_synced_commit":"1f6c8f2163efaf9455c3390435424d93649f1d83"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/jakubkulhan/bunny","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakubkulhan%2Fbunny","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakubkulhan%2Fbunny/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakubkulhan%2Fbunny/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakubkulhan%2Fbunny/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jakubkulhan","download_url":"https://codeload.github.com/jakubkulhan/bunny/tar.gz/refs/heads/0.6.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakubkulhan%2Fbunny/sbom","scorecard":{"id":503066,"data":{"date":"2025-08-11","repo":{"name":"github.com/jakubkulhan/bunny","commit":"5a37f769b5c8a1698742fc2394781b37279fe0f1"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":10,"reason":"30 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":0,"reason":"Found 1/14 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch '0.6.x'","Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T22:29:43.594Z","repository_id":28754301,"created_at":"2025-08-19T22:29:43.594Z","updated_at":"2025-08-19T22:29:43.594Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278420196,"owners_count":25983812,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amqp","amqp-client","amqp0-9-1","php","php-library","rabbitmq"],"created_at":"2024-08-01T21:01:32.993Z","updated_at":"2025-10-05T09:41:36.550Z","avatar_url":"https://github.com/jakubkulhan.png","language":"PHP","funding_links":[],"categories":["Table of Contents","PHP","目录","队列 Queue"],"sub_categories":["Queue","队列 Queue","Globalization"],"readme":"# BunnyPHP\n\n[![Continuous Integration](https://github.com/jakubkulhan/bunny/actions/workflows/ci.yml/badge.svg)](https://github.com/jakubkulhan/bunny/actions/workflows/ci.yml)\n[![Downloads this Month](https://img.shields.io/packagist/dm/bunny/bunny.svg)](https://packagist.org/packages/bunny/bunny)\n[![Latest stable](https://img.shields.io/packagist/v/bunny/bunny.svg)](https://packagist.org/packages/bunny/bunny)\n\n\n\u003e Performant pure-PHP AMQP (RabbitMQ) non-blocking ReactPHP library\n\n## Requirements\n\nBunnyPHP requires PHP 8.1 and newer.\n\n## Installation\n\nAdd as [Composer](https://getcomposer.org/) dependency:\n\n```sh\n$ composer require bunny/bunny:@^0.6dev\n```\n\n## Comparison\n\nYou might ask if there isn't a library/extension to connect to AMQP broker (e.g. RabbitMQ) already. Yes, there are\n multiple options:\n\n- [ext-amqp](http://pecl.php.net/package/amqp) - PHP extension\n- [php-amqplib](https://github.com/php-amqplib/php-amqplib) - pure-PHP AMQP protocol implementation\n\nWhy should you want to choose BunnyPHP instead?\n\n* You want **nice idiomatic PHP API** to work with (I'm looking at you, php-amqplib). BunnyPHP interface follows PHP's common\n  **coding standards** and **naming conventions**. See tutorial.\n\n* You **can't (don't want to) install PECL extension** that has latest stable version in 2014. BunnyPHP isn't as such marked\n  as stable yet. But it is already being used in production.\n\n* You have **both classic CLI/FPM and [ReactPHP](http://reactphp.org/)** applications and need to connect to RabbitMQ.\n  BunnyPHP comes with an **asynchronous** client with a **synchronous** API using [`Fibers`](https://reactphp.org/async/).\n\nApart from that BunnyPHP is more performant than main competing library, php-amqplib. See [`benchmark/` directory](https://github.com/jakubkulhan/bunny/tree/master/benchmark)\nand [php-amqplib's `benchmark/`](https://github.com/videlalvaro/php-amqplib/tree/master/benchmark). (For `ext-amp` https://gist.github.com/WyriHaximus/65fd98e099820aded1b79e9111e02916 is used.)\n\nBenchmarks were run as:\n\n```sh\n$ php benchmark/producer.php N \u0026 php benchmark/consumer.php\n```\n\n| Library     | N (# messages) | Produce sec | Produce msg/sec | Consume sec | Consume msg/sec |\n|-------------|---------------:|------------:|----------------:|------------:|----------------:|\n| php-amqplib | 100            | 0.000671    | 148998          | 0.001714    | 58343           |\n| ext-amqp    | 100            | 0.000302    | 331042          | 0.008915    | 11217           |\n| bunnyphp    | 100            | 0.000194    | 515271          | 0.000939    | 106508          |\n| bunnyphp +/-|                |             | +345.8%/+155.6% |             | +182.5%/+949.5% |\n| php-amqplib | 1000           | 0.004827    | 207167          | 0.015166    | 65937           |\n| ext-amqp    | 1000           | 0.002399    | 416846          | 0.078373    | 12760           |\n| bunnyphp    | 1000           | 0.001597    | 626202          | 0.011139    | 89773           |\n| bunnyphp +/-|                |             | +302.2%/+150.2% |             | +136.1%/+703.5% |\n| php-amqplib | 10000          | 0.060204    | 166102          | 0.147772    | 67672           |\n| ext-amqp    | 10000          | 0.022735    | 439853          | 0.754800    | 13249           |\n| bunnyphp    | 10000          | 0.016441    | 608232          | 0.106685    | 93734           |\n| bunnyphp +/-|                |             | +366.1%/+138.2% |             | +138.5%/+707.4% |\n| php-amqplib | 100000         | 1.158033    | 90276           | 1.477762    | 67670           |\n| ext-amqp    | 100000         | 0.952319    | 105007          | 7.494665    | 13343           |\n| bunnyphp    | 100000         | 0.812430    | 123088          | 1.073454    | 93157           |\n| bunnyphp +/-|                |             | +136.3%/+117.2% |             | +137.6%/+698.1% |\n| php-amqplib | 1000000        | 18.64132    | 53644           | 18.992902   | 52651           |\n| ext-amqp    | 1000000        | 12.86827    | 77710           | 89.432139   | 11182           |\n| bunnyphp    | 1000000        | 11.63421    | 85953           | 11.947426   | 83700           |\n| bunnyphp +/-|                |             | +160.2%/+110.6% |             | +158.9%/+748.5% |\n\n## Tutorial\n\n### Connecting\n\nWhen instantiating the BunnyPHP `Client` accepts an array with connection options:\n\n```php\nuse Bunny\\Client;\nuse Bunny\\Configuration;\n\n$configuration = new Configuration(\n    host:     'HOSTNAME',\n    vhost:    'VHOST',    // The default vhost is /\n    user:     'USERNAME', // The default user is guest\n    password: 'PASSWORD', // The default password is guest\n);\n\n$bunny = new Client($configuration);\n$bunny-\u003econnect();\n```\n\n\u003cdetails\u003e\n\n\u003csummary\u003eUsing DSN\u003c/summary\u003e\n\n```php\nuse Bunny\\Client;\nuse Bunny\\Configuration;\n\n$configuration = Configuration::fromDSN('amqp://USERNAME:PASSWORD@HOSTNAME/VHOST');\n\n$bunny = new Client($configuration);\n$bunny-\u003econnect();\n```\n\n\u003c/details\u003e\n\n### Connecting securely using TLS(/SSL)\n\nOptions for TLS-connections should be specified as array `tls`:  \n\n```php\nuse Bunny\\Client;\nuse Bunny\\Configuration;\n\n$configuration = new Configuration(\n    host:     'HOSTNAME',\n    vhost:    'VHOST',    // The default vhost is /\n    user:     'USERNAME', // The default user is guest\n    password: 'PASSWORD', // The default password is guest\n    tls:      [\n        'cafile'      =\u003e 'ca.pem',\n        'local_cert'  =\u003e 'client.cert',\n        'local_pk'    =\u003e 'client.key',\n    ],\n);\n\n$bunny = new Client($configuration);\n$bunny-\u003econnect();\n```\n\n\u003cdetails\u003e\n\n\u003csummary\u003eUsing DSN\u003c/summary\u003e\n\n```php\nuse Bunny\\Client;\nuse Bunny\\Configuration;\n\n$configuration = Configuration::fromDSN(\n    'amqp://USERNAME:PASSWORD@HOSTNAME/VHOST?tls[cafile]=ca.pem\u0026tls[local_cert]=client.cert\u0026tls[local_pk]=client.key',\n);\n\n$bunny = new Client($configuration);\n$bunny-\u003econnect();\n```\n\n\u003c/details\u003e\n\nFor options description - please see [SSL context options](https://www.php.net/manual/en/context.ssl.php).\n\nNote: invalid TLS configuration will cause connection failure.\n\nSee also [common configuration variants](examples/tls/).\n\n### Providing client properties\n\nClient Connections can [present their capabilities](https://www.rabbitmq.com/connections.html#capabilities) to\na server by presenting an optional `client_properties` table when establishing a connection.\n\nFor example, a connection name may be provided by setting the\n[`connection_name` property](https://www.rabbitmq.com/connections.html#client-provided-names):\n\n```php\nuse Bunny\\Client;\nuse Bunny\\Configuration;\n\n$configuration = new Configuration(\n    host:             'HOSTNAME',\n    vhost:            'VHOST',    // The default vhost is /\n    user:             'USERNAME', // The default user is guest\n    password:         'PASSWORD', // The default password is guest\n    clientProperties: [\n        'connection_name' =\u003e 'My connection',\n    ],\n);\n\n$bunny = new Client($configuration);\n$bunny-\u003econnect();\n```\n\nObviously this can be dynamic, for example, on Kubernetes you can include the pod, the namespace, and any other environment variable in it:\n\n```php\nuse Bunny\\Client;\nuse Bunny\\Configuration;\n\n$configuration = new Configuration(\n    host:             'HOSTNAME',\n    vhost:            'VHOST',    // The default vhost is /\n    user:             'USERNAME', // The default user is guest\n    password:         'PASSWORD', // The default password is guest\n    clientProperties: [\n        'connection_name' =\u003e 'Pod: ' . getenv('POD_NAME') . '; Release: ' . getenv('RELEASE_TAG') . '; Namespace: ' . getenv('POD_NAMESPACE'),\n    ],\n);\n\n$bunny = new Client($configuration);\n$bunny-\u003econnect();\n```\n\n### Publish a message\n\nNow that we have a connection with the server we need to create a channel and declare a queue to communicate over before we can publish a message, or subscribe to a queue for that matter.\n\n```php\n$channel = $bunny-\u003echannel();\n$channel-\u003equeueDeclare('queue_name'); // Queue name\n```\n\n#### Publishing a message on a virtual host with quorum queues as a default\n\nFrom RabbitMQ 4 queues will be standard defined as Quorum queues, those are by default durable, in order to connect to them you should use the queue declare method as follows. In the current version of RabbitMQ 3.11.15 this is already supported, if the virtual host is configured to have a default type of Quorum.\n\n```php\n$channel = $bunny-\u003echannel();\n$channel-\u003equeueDeclare('queue_name', false, true); // Queue name\n```\n\nWith a communication channel set up, we can now publish a message to the queue:\n\n```php\n$channel-\u003epublish(\n    $message,    // The message you're publishing as a string\n    [],          // Any headers you want to add to the message\n    '',          // Exchange name\n    'queue_name', // Routing key, in this example the queue's name\n);\n```\n\nAlternatively:\n\n```php\n$channel-\u003epublish(\n    body:       $message,     // The message you're publishing as a string\n    routingKey: 'queue_name', // Routing key, in this example the queue's name\n);\n```\n\n### Subscribing to a queue\n\nSubscribing to a queue can be done in two ways. The first way will run indefinitely:\n\n```php\n$channel-\u003erun(\n    static function (Message $message, Channel $channel, Client $bunny) {\n        $success = handleMessage($message); // Handle your message here\n\n        if ($success) {\n            $channel-\u003eack($message); // Acknowledge message\n            return;\n        }\n\n        $channel-\u003enack($message); // Mark message fail, message will be redelivered\n    },\n    'queue_name',\n);\n```\n\nThe other way lets you run the client for a specific amount of time consuming the queue before it stops:\n\n```php\n$channel-\u003econsume(\n    static function (Message $message, Channel $channel, Client $client) {\n        $channel-\u003eack($message); // Acknowledge message\n    },\n    'queue_name',\n);\n$bunny-\u003erun(12); // Client runs for 12 seconds and then stops\n```\n\n### Pop a single message from a queue\n\n```php\n$message = $channel-\u003eget('queue_name');\n\n// Handle message\n\n$channel-\u003eack($message); // Acknowledge message\n```\n\n### Prefetch count\n\nA way to control how many messages are prefetched by BunnyPHP when consuming a queue is by using the channel's QOS method. In the example below only 5 messages will be prefetched. Combined with acknowledging messages this turns into an effective flow control for your applications, especially asynchronous applications. No new messages will be fetched unless one has been acknowledged.\n\n```php\n$channel-\u003eqos(\n    0, // Prefetch size\n    5,  // Prefetch count\n);\n```\n\n### Asynchronous usage\n\n**Node: Up to version `v0.5.x` Bunny had two different clients, one sync, and one async. As of `v0.6` both clients have been folder into one: An async client with a sync API.**\n\n## AMQP interop\n\nThere is [amqp interop](https://github.com/queue-interop/amqp-interop) compatible wrapper(s) for the bunny library.\n\n## Testing\n\nTo fully test this package, TLS certificates are required and a local RabbitMQ. On top of that a Code Style fixer and Static Analysis are used in this project. To make it as simple as possible for anyone working on this project a `Makefile` is in place to take care of all of that.\n\n```shell\n$ make\n```\n\n\u003cdetails\u003e\n\n\u003csummary\u003eTesting details\u003c/summary\u003e\n\nCreate client/server TLS certificates by running:\n\n```shell\n$ cd test/tls \u0026\u0026 make all \u0026\u0026 cd -\n```\n\nYou need access to a RabbitMQ instance in order to run the test suite. The easiest way is to use the provided Docker Compose setup to create an isolated environment, including a RabbitMQ container, to run the test suite in.\n\n**Docker Compose**\n\n- Use Docker Compose to create a network with a RabbitMQ container and a PHP container to run the tests in. The project\n  directory will be mounted into the PHP container.\n  \n  ```shell\n  $ docker-compose up -d\n  ```\n\n  To test against different TLS configurations (as in CI builds), you can set environment variable `CONFIG_NAME=rabbitmq.tls.verify_none` before running `docker-compose up`.\n  \n- Optionally use `docker ps` to display the running containers.  \n\n  ```shell\n  $ docker ps --filter name=bunny\n  [...] bunny_rabbit_node_1_1\n  [...] bunny_bunny_1\n  ```\n\n- Enter the PHP container.\n\n  ```shell\n  $ docker exec -it bunny_bunny_1 bash\n  ```\n  \n- Within the container, run:\n\n  ```shell\n  $ vendor/bin/phpunit\n  ```\n\n\u003c/details\u003e\n\n## Contributing\n\n* Large part of the PHP code (almost everything in `Bunny\\Protocol` namespace) is generated from spec in file\n  [`spec/amqp-rabbitmq-0.9.1.json`](spec/amqp-rabbitmq-0.9.1.json). Look for `DO NOT EDIT!` in doc comments.\n\n  To change generated files change [`spec/generate.php`](spec/generate.php) and run:\n\n  ```sh\n  $ php ./spec/generate.php\n  ```\n\n## Broker compatibility\n\nWorks well with RabbitMQ\n\nDoes not work with ActiveMQ because it requires AMQP 1.0 which is a completely different protocol (Bunny is implementing AMQP 0.9.1)\n\n## License\n\nBunnyPHP is licensed under MIT license. See `LICENSE` file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakubkulhan%2Fbunny","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjakubkulhan%2Fbunny","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakubkulhan%2Fbunny/lists"}