Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Pheanstalk Bundle Laravel Package

php-programmist/pheanstalk-bundle

A Symfony bundle that integrates Pheanstalk, the PHP client for the beanstalkd work queue, wiring it into the service container so you can configure connections and inject a ready-to-use job queue client into your application services.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle (LeezyPheanstalkBundle) that wires the pda/pheanstalk client into a Symfony container. It is not a Laravel package — there is no service provider, facade, or queue driver. If you are working in Laravel, the honest first step is usually to install pda/pheanstalk directly and write a small binding yourself; use this bundle only if you are in a Symfony app or deliberately reusing its config/service conventions.

Symfony setup (the intended path):

composer require php-programmist/pheanstalk-bundle

Register the bundle in config/bundles.php:

Leezy\PheanstalkBundle\LeezyPheanstalkBundle::class => ['all' => true],

Minimal config in config/packages/leezy_pheanstalk.yaml:

leezy_pheanstalk:
    connections:
        default:
            servers:
                - 127.0.0.1:11300
    default_connection: default

Where to look first:

  1. vendor/php-programmist/pheanstalk-bundle/ — inspect DependencyInjection/Configuration.php for the exact config tree (servers, connection aliases, timeouts).
  2. The container services: leezy.pheanstalk (default connection) and leezy.pheanstalk.<connection_name>.
  3. The underlying pda/pheanstalk README — the bundle is a thin wrapper, so most day-to-day API knowledge comes from there.

First use case — put and reserve a job:

use Pheanstalk\PheanstalkInterface;

public function dispatch(PheanstalkInterface $pheanstalk): void
{
    $pheanstalk
        ->useTube('emails')
        ->put(json_encode(['to' => '[email protected]', 'subject' => 'Hi']), PheanstalkInterface::DEFAULT_PRIORITY, 0, 60);

    $job = $pheanstalk->watch('emails')->reserve(5); // 5s timeout
    if ($job) {
        // handle payload
        $pheanstalk->delete($job);
    }
}

Start beanstalkd locally (beanstalkd -p 11300) so you can exercise the flow before wiring workers.

Implementation Patterns

Inject the connection service, not the bundle. Constructor-inject PheanstalkInterface (or the concrete Pheanstalk) and keep queue code in a dedicated producer/consumer class. Reserve the Pheanstalk client for one class per tube to keep lifecycle calls obvious.

Producer pattern — always set tube, priority, delay, TTR explicitly:

$pheanstalk->useTube('reports')->put(
    $payload,
    PheanstalkInterface::DEFAULT_PRIORITY, // lower = higher priority
    $delaySeconds,                         // 0 = ready now
    $ttrSeconds                            // time reserved job may run before reappearing
);

Consumer/worker pattern: a long-running console command that loops reserve(), dispatches to a handler, then calls exactly one of delete() (success), release() (retry), or bury() (dead-letter). Handle signals (SIGTERM/SIGINT) so supervisor can restart workers cleanly.

while (!$this->shouldStop) {
    $job = $pheanstalk->watch('reports')->ignore('default')->reserve(5);
    if (!$job) { continue; }
    try {
        ($this->handler)($job->getData());
        $pheanstalk->delete($job);
    } catch (\Throwable $e) {
        $pheanstalk->release($job, PheanstalkInterface::DEFAULT_PRIORITY, 10); // retry in 10s
    }
}

Laravel bridge pattern: register a singleton in AppServiceProvider and alias the interface:

$this->app->singleton(PheanstalkInterface::class, fn () => new \Pheanstalk\Pheanstalk('127.0.0.1', 11300));

Then inject PheanstalkInterface into jobs/controllers. Mirror the bundle's per-connection idea by creating named bindings (pheanstalk.reports, pheanstalk.emails) if you use multiple tubes/servers.

Payload convention: serialize a small envelope ({class, method, args, attempts}) rather than raw arrays so handlers stay versionable; store attempts in the payload and enforce max retries in the worker before burying. The bundle does not serialize for you.

Multiple connections: define several entries under connections (e.g. default, priority) and inject leezy.pheanstalk.priority where needed. Use separate tubes and, ideally, separate beanstalkd instances for workloads you do not want competing for memory.

Monitoring/stats: call $pheanstalk->stats(), statsTube('emails'), and peekReady('emails') from a health command or debug endpoint. For Symfony, inspect available bundle commands with php bin/console list | grep -i leezy rather than assuming command names.

Testing: the client talks to a real beanstalkd over TCP, so unit tests should mock PheanstalkInterface (it is injectable) and integration tests should point at a disposable beanstalkd container. There is no in-memory fake bundled.

Gotchas and Tips

  • Project status is the biggest gotcha. Zero stars, no known dependents, last release December 2020. Treat it as a thin convenience wrapper, pin pda/pheanstalk and Symfony versions, and be prepared to fork or replace it with direct pda/pheanstalk usage. Check PHP/Symfony constraint compatibility before upgrading either.
  • Not a Laravel queue driver. It will not satisfy Queue::push(), dispatch(), or Horizon. If you need Laravel-native queues, use the built-in Redis/database/SQS drivers; use beanstalkd only if you deliberately want its semantics.
  • API breakage between Pheanstalk 3 and 4. Pheanstalk 4 moved to namespaced classes (Pheanstalk\Pheanstalk, Pheanstalk\Job, Pheanstalk\PheanstalkInterface); older tutorials use the global Pheanstalk class. The bundle's service definitions are tied to whichever major version it requires — verify composer show pda/pheanstalk and match imports to it.
  • reserve() blocks. reserve(0) blocks forever; always pass a timeout in workers so shutdown signals are observed. Catch TimeoutException and continue rather than letting it crash the worker.
  • Every reserved job must be finished. If the worker crashes or forgets delete(), the job reappears after its TTR. Keep TTR comfortably above expected handler runtime; a too-short TTR causes duplicate processing.
  • Priority is inverted. Lower numbers run first; 0 is highest. Copying priority constants from another queue system will produce surprising ordering.
  • Job size limit. beanstalkd caps jobs (default 65,535 bytes). Do not put large payloads in the tube; store the blob elsewhere and queue an ID.
  • Tube naming. Tube names are case-sensitive and bounded (200 bytes); default is watched automatically, so call ignore('default') when you only want your tube.
  • Use release() for retries, bury() for poison messages. release() with a delay is your backoff mechanism; burying keeps failed jobs inspectable via peekBuried() instead of silently dropping them.
  • Connection string format matters. In the bundle config, servers are typically host:port entries; a malformed string or unreachable host throws ConnectionException at first use, not at container boot. Warm the connection in a health check if you want early failure.
  • beanstalkd is not durable by default. Jobs are in memory unless started with a binlog (-b); a restart clears the queue. Do not treat it as a system of record.
  • Config caching. In Symfony, run bin/console cache:clear after changing leezy_pheanstalk config; stale container definitions are a common "why is it still connecting to the old host" cause.
  • Extension points are limited. There are no events or middleware hooks. Customize by overriding the service definitions (leezy.pheanstalk.*), decorating the Pheanstalk service, or bypassing the bundle entirely and constructing the client in your own factory. The client itself is extensible via Pheanstalk's command API.
  • Debugging tip: use telnet 127.0.0.1 11300 and issue stats, stats-tube emails, peek-ready emails, and list-tubes to see exactly what the client sees; most "lost job" reports are TTR/release mistakes rather than bundle bugs.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
laravel2580/idehelpers
delatbabel/viewpages
anavel/gettext
muhamadrezaar/highcharts
delatbabel/keylists
frankkessler/salesforce-laravel-oauth2-rest
anavel/foundation
wdmtech/enviricons
eii/laravel-captcha
muka/shape-reader
tdt/json
refinery29/php-cs-fixer-config
hotmeteor/eco-env
mvaliolahi/hydrate
wearede/tbcpay-php
parsnick/steak
thibaud-dauce/mattermost-php
multilinguals/apollo-client
antaresbr/collection
kr0lik/phpstan-rules