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.
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.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:
$userId, $requestId).bind([$this, 'method'], $arg1, $arg2).bind_right() if the last argument is static (e.g., logging middleware).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:
placeholder to defer dynamic arguments (e.g., route parameters).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:
bind() over closures for one-off transformations.$transform = bind('strtoupper', bind('trim', ...));
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:
app()->make()).Pattern: Bind event context to listeners.
// ReactPHP event emitter
$emitter->on('user.created', bind([$this, 'notifyUser'], $userId));
Tip:
bind_right() if the event payload is the last argument:
$emitter->on('user.created', bind_right([$this, 'logEvent'], 'user.created'));
Argument Order Confusion:
bind($fn, $arg1, $arg2) binds $arg1 to the first parameter of $fn.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);
Closure Scope Issues:
$this automatically.[$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);
Placeholder (…) Accessibility:
… character (U+2026) is platform-dependent (see README).placeholder alias:
use function React\Partial\placeholder;
$fn = bind('substr', placeholder(), 0, 1);
Debugging Partially Applied Functions:
Closure names.$processOrderWithUser = bind([OrderService::class, 'process'], $userId);
Async Context Leaks:
$loop) unless intentional.$boundFn = bind([$this, 'method'], $arg);
\Log::debug('Bound function:', [$boundFn]);
get_closure_this():
Check if $this is preserved in closures:
$closure = bind([$this, 'method'], $arg);
\Log::debug('Closure this:', [get_closure_this($closure)]);
Variadics:
Verify placeholder works as expected:
$fn = bind('array_map', bind('strtoupper', ...), ['foo', 'bar']);
react/partial is a zero-config package. All logic is runtime.… (use placeholder alias)./** @var callable(string): string */
$uppercase = bind('strtoupper', ...);
function bindOrderProcessor(callable $processor, $orderId) {
return bind($processor, $orderId);
}
Promise or Future for async partials:
use function React\Partial\bind;
use function React\Async\async;
$promise = async(function() {
return bind([$this, 'process'], $data);
});
// 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);
react/partial.bind_right:
Prefer bind() for left-side arguments (more intuitive for most cases).// 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
}
placeholder:
Overcomplicating partials with … when simple bind() suffices.How can I help you explore Laravel packages today?