Weave Code
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

Event Laravel Package

sabre/event

Lightweight PHP 8.2+ library for event-driven development: EventEmitter, promises, an event loop, and coroutines. Used to build reactive, non-blocking apps and services. Full docs at sabre.io/event.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require sabre/event "^6.1"
    

    Requires PHP 8.2+ (check composer.json for project compatibility).

  2. First Use Case: Basic Event Emitter

    use Sabre\Event\Emitter;
    
    $emitter = new Emitter();
    $emitter->on('greet', function ($name) {
        echo "Hello, $name!\n";
    });
    $emitter->emit('greet', 'Laravel');
    

    Output: Hello, Laravel!

  3. Where to Look First:

    • Official Docs (API reference, examples).
    • src/Emitter.php (core event handling).
    • src/Promise.php (async workflows).
    • src/EventLoop.php (non-blocking execution).

Implementation Patterns

1. Event-Driven Workflows

  • Pattern: Decouple components using events (e.g., user actions, system states).
    $emitter = new Emitter();
    $emitter->on('user.created', fn($user) => UserMailer::sendWelcome($user));
    $emitter->emit('user.created', new User());
    
  • Laravel Integration: Use alongside Laravel’s Event facade for hybrid systems:
    $emitter->on('order.processed', fn($order) => event(new OrderProcessed($order)));
    

2. Promises for Async Operations

  • Pattern: Chain async tasks (e.g., API calls, DB operations) with then()/otherwise().
    use Sabre\Event\Promise;
    
    $promise = new Promise(function ($resolve, $reject) {
        if (rand(0, 1)) {
            $resolve('Success!');
        } else {
            $reject(new Exception('Failed!'));
        }
    });
    
    $promise->then(function ($result) {
        echo "Resolved: $result\n";
    })->otherwise(function ($error) {
        echo "Rejected: {$error->getMessage()}\n";
    });
    
  • Laravel Tip: Use Promise\all() to parallelize multiple async tasks:
    $promises = [
        new Promise(fn($resolve) => $resolve('Task 1')),
        new Promise(fn($resolve) => $resolve('Task 2')),
    ];
    Promise\all($promises)->then(fn($results) => dd($results)); // ['Task 1', 'Task 2']
    

3. Event Loop for Non-Blocking CLI

  • Pattern: Run async tasks in a loop (e.g., WebSocket server, CLI workers).
    use Sabre\Event\EventLoop;
    
    $loop = new EventLoop();
    $emitter = new Emitter();
    
    $emitter->on('tick', function () {
        echo "Looping...\n";
        $this->emit('tick'); // Re-trigger after delay
    });
    
    $loop->futureTick(1000, $emitter, 'tick'); // Emit 'tick' every 1s
    $loop->run(); // Blocks until loop stops
    
  • Laravel Use Case: Replace queue:work for custom async CLI logic (e.g., real-time data processing).

4. Coroutines for Async Control Flow

  • Pattern: Use coroutine() to yield control (e.g., pause/resume async tasks).
    function* asyncTask() {
        yield; // Pause execution
        echo "Resumed!\n";
        return "Done";
    }
    
    $coroutine = coroutine(asyncTask());
    $loop = new EventLoop();
    $loop->futureTick(1000, $coroutine, 'resume'); // Resume after 1s
    $loop->run();
    
  • Laravel Tip: Combine with Promises for complex async flows:
    $promise = new Promise(fn($resolve) => $resolve('Data'));
    $promise->then(fn($data) => yield); // Yield while waiting
    

5. Wildcard Listeners for Dynamic Events

  • Pattern: Listen to all events with * (e.g., logging, analytics).
    $emitter = new Emitter();
    $emitter->on('*', function ($event, $data) {
        logger()->info("Event $event triggered with: " . json_encode($data));
    });
    $emitter->emit('user.login', ['id' => 1]);
    

Gotchas and Tips

Pitfalls

  1. Event Loop Blocks HTTP Requests

    • Issue: Running $loop->run() in a Laravel HTTP route freezes the request.
    • Fix: Use only in Artisan commands, CLI scripts, or queue workers.
      // ❌ Avoid in routes
      // $loop->run();
      
      // ✅ Safe in commands
      $loop->run();
      
  2. Promises Require Event Loop for then()

    • Issue: then()/otherwise() callbacks won’t execute without an active loop.
    • Fix: Use Promise::wait() for synchronous waits or run in a loop:
      $promise->wait(); // Blocks until resolved/rejected
      // OR
      $loop->futureTick(0, $promise); // Schedule promise in loop
      
  3. Coroutines Need Explicit Resume

    • Issue: yield pauses indefinitely without a resume trigger.
    • Fix: Always pair yield with futureTick or manual resume:
      $coroutine = coroutine(function* () {
          yield; // Must resume later!
      });
      $loop->futureTick(1000, $coroutine, 'resume');
      
  4. PHP 8.2+ Strict Typing

    • Issue: Older PHP versions (e.g., 7.4) may throw type errors in v6.1+.
    • Fix: Use v6.0.x for PHP 7.4–8.1 or upgrade to PHP 8.2+.
  5. Memory Leaks with Listeners

    • Issue: Forgetting to removeListener() can accumulate callbacks.
    • Fix: Clean up listeners in destructors or use once() for one-time events:
      $emitter->once('event', $callback); // Auto-removes after first emit
      

Debugging Tips

  • Log Events: Attach a wildcard listener for debugging:
    $emitter->on('*', fn($event, $data) => logger()->debug("Event $event: $data"));
    
  • Promise Inspection: Use Promise::wait() to debug async flows:
    try {
        $result = $promise->wait();
    } catch (\Throwable $e) {
        logger()->error($e);
    }
    
  • Event Loop Control: Stop the loop gracefully with EventLoop::stop():
    $loop->futureTick(5000, fn() => $loop->stop()); // Stop after 5s
    

Extension Points

  1. Custom Emitters: Extend Emitter for domain-specific logic:
    class UserEmitter extends Emitter {
        public function emitLogin(User $user) {
            $this->emit('user.login', $user);
        }
    }
    
  2. Promise Decorators: Wrap Promises to add middleware:
    function loggingPromise(Promise $promise) {
        return new class($promise) extends Promise {
            public function __construct(private Promise $promise) {}
            public function then(callable $onFulfilled) {
                return $this->promise->then(function ($result) use ($onFulfilled) {
                    logger()->info("Promise resolved: $result");
                    return $onFulfilled($result);
                });
            }
        };
    }
    
  3. Event Loop Extensions: Add custom timers or I/O:
    $loop->futureTick(1000, fn() => echo "Timer fired!\n");
    

Laravel-Specific Quirks

  • Avoid in HTTP Context: The Event Loop blocks Laravel’s request lifecycle. Use only in:
    • Artisan commands (php artisan my:command).
    • Queue workers (php artisan queue:work).
    • CLI scripts (e.g., scheduler:run).
  • Hybrid with Laravel Events: Bridge sabre/events with Laravel’s Event facade:
    $emitter->on('user.created', fn($user) => event(new UserCreated($user)));
    
  • Service Container: Bind the emitter for dependency injection:
    $app->singleton(Emitter::class, fn() => new Emitter
    
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.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai