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

Pokio Laravel Package

nunomaduro/pokio

Pokio is a simple async API for PHP 8.3+ using pcntl forks and FFI shared memory to run closures concurrently and await results. Falls back to sequential execution if extensions aren’t available. Experimental/unsafe; intended for internal use, not production.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require nunomaduro/pokio
    

    Requires PHP 8.3+ and optionally pcntl/ffi extensions for true parallelism (falls back to sequential execution if unavailable).

  2. First Use Case: Run two blocking tasks concurrently (e.g., API calls or file operations) without waiting sequentially:

    use function Pokio\async;
    use function Pokio\await;
    
    $promise1 = async(function () {
        sleep(2); // Simulate I/O delay
        return 'Task 1 result';
    });
    
    $promise2 = async(function () {
        sleep(1); // Faster task
        return 'Task 2 result';
    });
    
    [$result1, $result2] = await([$promise1, $promise2]);
    // Outputs after ~2 seconds (not 3)
    
  3. Where to Look First:

    • Core Functions: async() (creates promises) and await() (blocks until resolution).
    • Promise Methods: then(), catch(), finally() for chaining (similar to JavaScript Promises).
    • Fallback Behavior: Check Pokio\supportsConcurrency() to verify if pcntl/ffi are available.

Implementation Patterns

Usage Patterns

  1. Parallelizing I/O-Bound Tasks:

    $promises = collect(range(1, 5))->map(fn ($i) =>
        async(fn () => fetchDataFromApi($i))
    )->toArray();
    
    $results = await($promises);
    
  2. Chaining Promises (Like JavaScript):

    $promise = async(fn () => fetchUser(1))
        ->then(fn ($user) => async(fn () => fetchPosts($user->id)))
        ->then(fn ($posts) => formatPosts($posts))
        ->catch(fn (Throwable $e) => logError($e));
    
  3. Non-Blocking User Prompts:

    $promise = async(fn () => prompt('Enter name:'));
    $name = await($promise);
    
  4. Artisan Command Integration:

    use Pokio\{async, await};
    
    protected function handle(): void
    {
        $tasks = collect($this->arguments['files'])->map(fn ($file) =>
            async(fn () => processFile($file))
        );
    
        await($tasks);
        $this->info('All files processed concurrently!');
    }
    

Workflows

  • Batch Processing: Use async + await to parallelize file operations, database queries, or external API calls in Laravel migrations or console commands.

    $files = glob('storage/*.log');
    $promises = array_map(fn ($file) => async(fn () => compressFile($file)), $files);
    await($promises);
    
  • Error Handling: Leverage catch() or try/catch with await to handle failures gracefully:

    try {
        $result = await(async(fn () => riskyOperation()));
    } catch (Throwable $e) {
        $this->error("Failed: {$e->getMessage()}");
    }
    
  • Resource Cleanup: Use finally() to ensure cleanup (e.g., closing files, releasing locks):

    $promise = async(fn () => processLargeFile())
        ->finally(fn () => cleanupTempFiles());
    await($promise);
    

Integration Tips

  1. Laravel Service Providers: Bind Pokio\Pokio to the container for dependency injection:

    $this->app->singleton(Pokio::class, fn () => new Pokio());
    
  2. Testing: Mock async/await in unit tests by replacing global functions:

    // In tests/CreatesApplication.php
    $this->app->singleton('Pokio\async', fn () => fn ($closure) => new MockPromise($closure));
    
  3. Performance Tuning:

    • Limit concurrent tasks to avoid process overhead:
      $batchSize = 10;
      $batches = array_chunk($tasks, $batchSize);
      foreach ($batches as $batch) {
          await($batch);
      }
      
    • Use Pokio\supportsConcurrency() to dynamically adjust logic based on environment.
  4. Shared State: Avoid sharing mutable state between processes (use FFI shared memory sparingly; prefer returning data via promises).


Gotchas and Tips

Pitfalls

  1. Process Isolation:

    • State Leaks: Child processes inherit the parent’s memory state. Avoid global variables or static properties in async closures.
      // ❌ Risky: Static state leaks
      async(fn () => static::$counter++);
      
      // ✅ Safe: Local state
      async(fn () => $localVar = 1);
      
    • Signal Handling: Child processes may ignore signals (e.g., SIGINT). Use pcntl_signal() in parent if needed.
  2. FFI/PCNTL Dependencies:

    • Fallback Behavior: If extensions are missing, Pokio runs sequentially. Test with:
      if (!Pokio\supportsConcurrency()) {
          $this->warn('Pokio running sequentially (no PCNTL/FFI).');
      }
      
    • Shared Hosting: Likely lacks pcntl/ffi. Use only for local/CI environments or ensure sequential fallback is acceptable.
  3. Resource Limits:

    • Memory: Each process has its own memory. Large data structures may bloat RAM.
    • File Descriptors: Open files in child processes aren’t shared. Use tmpfile() or explicit paths.
  4. Debugging:

    • Zombie Processes: If child processes hang, check for infinite loops or unhandled exceptions in async closures.
    • Logging: Use stderr or dedicated log files for child process output:
      async(fn () => file_put_contents('storage/logs/child.log', 'Debug info'));
      
  5. Serialization:

    • Closure Scope: Pokio serializes closures to child processes. Avoid serializing non-serializable objects (e.g., resources, closures with unbound variables).
    • Error: Pokio\Exceptions\SerializationException if data can’t be serialized.

Tips

  1. Error Handling:

    • Centralize error handling with a catch chain or wrapper:
      function safeAsync(callable $closure): mixed {
          return async($closure)->catch(fn (Throwable $e) => reportError($e));
      }
      
  2. Progress Tracking:

    • Use then() to track progress:
      $promise = async(fn () => longRunningTask())
          ->then(fn () => $this->info('Task started'))
          ->finally(fn () => $this->info('Task completed'));
      
  3. Configuration:

    • Set POKIO_MAX_PROCESSES environment variable to limit concurrency:
      export POKIO_MAX_PROCESSES=4
      
  4. Testing:

    • Mock async/await in tests to avoid actual process spawning:
      $mockPokio = new class {
          public function async(callable $closure) {
              return new class($closure) implements \Pokio\Promise {
                  public function __invoke() { return $this->closure(); }
                  // Implement other Promise methods...
              };
          }
      };
      
  5. Extension Points:

    • Custom Promise Classes: Extend Pokio\Promise for domain-specific behavior.
    • Shared Memory: Use Pokio\FFI\SharedMemory directly for advanced IPC (e.g., large data sharing).
  6. Performance:

    • Warm-Up: Pre-fork processes for repeated tasks (e.g., in Laravel’s booted event).
    • Avoid Overhead: For tiny tasks, sequential execution may be faster due to process creation costs.
  7. Security:

    • Sandboxing: Child processes run with the same permissions as the parent. Avoid executing untrusted code in async closures.
    • Cleanup: Ensure child processes exit gracefully (e.g., handle exceptions in closures).
  8. Laravel-Specific:

    • Queue Integration: Use Pokio for short-lived parallel tasks; offload long-running jobs to Laravel Queues.
    • Artisan Commands: Combine with Symfony’s Process component for hybrid async/sync workflows.

Debugging Quirks

  • Silent Failures: Child process exceptions may not propagate to the parent. Always use catch() or try/catch.
  • FFI Issues: On some systems, FFI shared memory may fail with EINVAL. Fallback to sequential execution or use sysvshm.
  • PHP Version: Ensure PHP 8.3+ (e.g., for named arguments in closures). Older versions may break serialization.

Extension Points

  1. Custom Promise Implementations:
    class CustomPromise implements
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport