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

Partial Laravel Package

react/partial

Lightweight partial function application for PHP. Pre-fill (bind) some arguments of any callable and get back a new callable with fewer required params—handy for async callbacks and event handlers where you need to pass extra context without verbose closures.

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer:

composer require react/partial

First Use Case: Replace a verbose closure in an async callback (e.g., ReactPHP file download):

use function React\Partial\bind;

// Before
$download->on('complete', function ($contents) use ($filename) {
    $this->processFile($filename, $contents);
});

// After
$download->on('complete', bind([$this, 'processFile'], $filename));

Where to Look First:

  • bind(): Core function for left-side partial application.
  • bind_right(): Right-side partial application (less common).
  • placeholder (…): Skip arguments in partial application.

Implementation Patterns

1. Async Callback Simplification

Pattern: Replace closures in async handlers (ReactPHP, Guzzle, etc.) with bind().

// ReactPHP HTTP server route
$server->on('request', bind([$this, 'handleRequest'], $userId));

// Guzzle HTTP client
$client->requestAsync('GET', '/api', bind([$this, 'processResponse'], $requestId));

Workflow:

  1. Identify callbacks with repetitive arguments (e.g., $userId, $requestId).
  2. Replace closures with bind([$this, 'method'], $arg1, $arg2).
  3. Use bind_right() if the last argument is static (e.g., logging middleware).

2. Middleware Pipelines

Pattern: Pre-bind middleware context (e.g., user, request) to handlers.

// Laravel middleware (if using ReactPHP)
public function handle($request, callable $next) {
    return $next(bind([$this, 'processRequest'], $request->user()));
}

Integration Tip:

  • Combine with ReactPHP’s middleware stack for async pipelines.
  • Use placeholder to defer dynamic arguments (e.g., route parameters).

3. Data Transformation Pipelines

Pattern: Use bind() with array_map, array_filter, etc.

// Extract first character from strings
$firstChar = bind('substr', ..., 0, 1);
$results = array_map($firstChar, ['foo', 'bar']); // ['f', 'b']

Tip:

  • Prefer bind() over closures for one-off transformations.
  • For complex logic, compose functions:
    $transform = bind('strtoupper', bind('trim', ...));
    

4. Dependency Injection for Functions

Pattern: Replace service containers with partial application for lightweight DI.

// Instead of Laravel's container
$processOrder = bind([OrderService::class, 'process'], $orderId);

// In tests
$mockProcessOrder = bind([$mockService, 'process'], $testOrderId);

Workflow:

  • Useful for testing or simple services without Laravel’s container.
  • Avoid overusing for large-scale DI (stick to Laravel’s app()->make()).

5. Event Listeners

Pattern: Bind event context to listeners.

// ReactPHP event emitter
$emitter->on('user.created', bind([$this, 'notifyUser'], $userId));

Tip:

  • Use bind_right() if the event payload is the last argument:
    $emitter->on('user.created', bind_right([$this, 'logEvent'], 'user.created'));
    

Gotchas and Tips

Pitfalls

  1. Argument Order Confusion:

    • bind($fn, $arg1, $arg2) binds $arg1 to the first parameter of $fn.
    • Fix: Use bind_right() for right-side binding or placeholder for flexibility.
    // Wrong: Binds $userId to the 3rd parameter
    $fn = bind([User::class, 'find'], $userId);
    
    // Correct: Use placeholder
    $fn = bind([User::class, 'find'], ..., $userId);
    
  2. Closure Scope Issues:

    • Partially applied functions do not capture $this automatically.
    • Fix: Use [$this, 'method'] syntax or Closure::bindTo() if needed.
    // Wrong: $this is lost
    $fn = bind(function($x) { return $this->helper($x); }, $arg);
    
    // Correct
    $fn = bind([$this, 'helper'], $arg);
    
  3. Placeholder (…) Accessibility:

    • The character (U+2026) is platform-dependent (see README).
    • Fix: Use the placeholder alias:
      use function React\Partial\placeholder;
      $fn = bind('substr', placeholder(), 0, 1);
      
  4. Debugging Partially Applied Functions:

    • Stack traces may show generic Closure names.
    • Fix: Name your bound functions clearly:
      $processOrderWithUser = bind([OrderService::class, 'process'], $userId);
      
  5. Async Context Leaks:

    • Partially applied functions may retain async context (e.g., ReactPHP loop).
    • Fix: Avoid binding async-specific objects (e.g., $loop) unless intentional.

Debugging Tips

  1. Log Intermediate Functions:
    $boundFn = bind([$this, 'method'], $arg);
    \Log::debug('Bound function:', [$boundFn]);
    
  2. Use get_closure_this(): Check if $this is preserved in closures:
    $closure = bind([$this, 'method'], $arg);
    \Log::debug('Closure this:', [get_closure_this($closure)]);
    
  3. Test with Variadics: Verify placeholder works as expected:
    $fn = bind('array_map', bind('strtoupper', ...), ['foo', 'bar']);
    

Configuration Quirks

  • No Config File: react/partial is a zero-config package. All logic is runtime.
  • IDE Support:
    • Modern IDEs (PHPStorm, VSCode) may not auto-complete (use placeholder alias).
    • Add PHPDoc to bound functions:
      /** @var callable(string): string */
      $uppercase = bind('strtoupper', ...);
      

Extension Points

  1. Custom Binders: Create wrapper functions for domain-specific partials:
    function bindOrderProcessor(callable $processor, $orderId) {
        return bind($processor, $orderId);
    }
    
  2. ReactPHP Integration: Extend with ReactPHP’s Promise or Future for async partials:
    use function React\Partial\bind;
    use function React\Async\async;
    
    $promise = async(function() {
        return bind([$this, 'process'], $data);
    });
    
  3. Laravel Facade: Add a facade for convenience (if using Laravel):
    // app/Partial.php
    namespace App;
    use function React\Partial\bind;
    class Partial {
        public static function bind(callable $fn, ...$args) {
            return bind($fn, ...$args);
        }
    }
    
    Usage:
    $fn = \App\Partial::bind([User::class, 'find'], $id);
    

Performance Considerations

  • Micro-Optimization: Partial application adds negligible overhead (~0–5% for binding).
  • Async Bottlenecks: Focus on ReactPHP’s event loop efficiency, not react/partial.
  • Memory: Partially applied functions are closures; avoid leaking them in long-running async contexts.

Anti-Patterns

  1. Overusing bind_right: Prefer bind() for left-side arguments (more intuitive for most cases).
  2. Partial Application in Loops: Re-binding the same function in loops creates duplicate closures. Fix: Bind once outside the loop.
    // Anti-pattern
    foreach ($items as $item) {
        $fn = bind([$this, 'process'], $item); // New closure each time
    }
    
    // Pattern
    $processItem = bind([$this, 'process'], ...);
    foreach ($items as $item) {
        $processItem($item); // Reuses bound function
    }
    
  3. Ignoring placeholder: Overcomplicating partials with when simple bind() suffices.
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.
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
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