clue/buzz-react
Async HTTP client for ReactPHP built on Buzz: send concurrent requests, stream responses, and integrate with event-loop apps. Lightweight, promise-based API for non-blocking web calls in long-running CLI/daemon services.
Installation
composer require clue/buzz-react
Ensure your project uses ReactPHP (v1.0+) as the event loop.
Basic Request
use Buzz\Client\React\Client;
use React\EventLoop\Factory;
$loop = Factory::create();
$client = new Client($loop);
$promise = $client->get('https://api.example.com/data');
$promise->then(
function ($response) {
echo $response->getBody();
},
function ($error) {
echo "Error: " . $error->getMessage();
}
);
$loop->run();
First Use Case Fetch multiple endpoints concurrently:
$promises = [
$client->get('https://api1.example.com'),
$client->get('https://api2.example.com'),
];
\React\Promise\all($promises)->then(
function ($responses) {
foreach ($responses as $response) {
echo $response->getBody();
}
}
);
Chaining Requests
Use then() to chain dependent requests:
$client->get('https://api.example.com/token')
->then(function ($response) {
$token = json_decode($response->getBody(), true)['token'];
return $client->get('https://api.example.com/protected', [
'headers' => ['Authorization' => "Bearer $token"]
]);
});
Retry Logic
Implement exponential backoff with React\Promise\retry:
use React\Promise\Timer\TimeoutException;
$retry = \React\Promise\retry(
3,
function ($attempt) use ($client) {
return $client->get('https://api.example.com')
->otherwise(function ($error) {
throw new TimeoutException("Retry $attempt failed", 0, $error);
});
}
);
Service Provider
Bind Client to Laravel’s container:
public function register()
{
$this->app->singleton(Client::class, function ($app) {
$loop = Factory::create();
return new Client($loop);
});
}
Queue Workers
Use ReactPHP with Laravel Queues (via spatie/react-queue):
$loop = Factory::create();
$client = new Client($loop);
$loop->addPeriodicTimer(1, function () use ($client) {
$client->get('https://api.example.com/queue')->then(...);
});
Middleware Attach middleware for logging/headers:
$client = new Client($loop);
$client->setMiddleware(function ($request, $next) {
$request = $request->withHeader('X-Custom-Header', 'value');
return $next($request);
});
Event Loop Management
$loop->run() blocks execution.ReactPHP in CLI commands or Laravel’s Artisan with a custom loop:
$loop = Factory::create();
$loop->run();
Memory Leaks
.done() or use React\Promise\Timer for cleanup:
$promise->done(); // Ensures promise is settled
Deprecation Warning
react/http (modern ReactPHP client).guzzlehttp/guzzle with async adapters.$loop->addPeriodicTimer(0.1, function () {
echo "Loop running...\n";
});
React\Promise\Timer\TimeoutException to catch hangs:
$promise->otherwise(function ($error) {
\Log::error("Promise failed: " . $error->getMessage());
});
Custom Transports
Override Client to use a proxy or custom transport:
class CustomClient extends Client {
protected function createTransport() {
return new CustomTransport($this->loop);
}
}
Response Transformers Decorate responses for consistency:
$client->setMiddleware(function ($request, $next) {
return $next($request)->then(function ($response) {
return new class($response) implements \Psr\Http\Message\ResponseInterface {
// Custom logic
};
});
});
Rate Limiting
Use React\Promise\Timer to throttle requests:
$lastRequest = 0;
$client->get('https://api.example.com')->then(function () use (&$lastRequest) {
$lastRequest = time();
});
// Throttle to 1 request per second
$loop->addPeriodicTimer(1, function () use ($client, &$lastRequest) {
if (time() - $lastRequest > 1) {
$client->get('https://api.example.com');
}
});
How can I help you explore Laravel packages today?