Installation:
composer require drift/react-functions
Requires PHP 8.1+ and ReactPHP (installed via react/event-loop).
First Use Case:
Convert a blocking function (e.g., file_get_contents) to non-blocking:
use Drift\ReactFunctions\File;
$loop = React\EventLoop\Factory::create();
$file = new File($loop);
$file->getContents('https://example.com')
->then(function ($contents) {
echo $contents;
})
->done();
Where to Look First:
Replacing Blocking Calls:
Replace synchronous functions (e.g., file_get_contents, curl_exec) with their non-blocking counterparts:
use Drift\ReactFunctions\Http;
$http = new Http($loop);
$http->get('https://api.example.com/data')
->then(function ($response) {
return json_decode($response, true);
})
->then(function ($data) {
// Process data asynchronously
});
Error Handling:
Use .otherwise() for error cases:
$file->getContents('invalid-url')
->otherwise(function ($error) {
error_log("Failed to fetch: " . $error->getMessage());
});
Chaining Promises: Chain multiple async operations:
$http->get('https://api.example.com/users')
->then(function ($users) {
return $http->post('https://api.example.com/process', json_encode($users));
})
->then(function ($result) {
// Handle result
});
Integration with Laravel:
Use React\EventLoop\LoopInterface via Laravel's service container:
$loop = app(React\EventLoop\LoopInterface::class);
$file = new File($loop);
Custom ReactPHP Components: Extend existing functions (e.g., wrap a custom HTTP client):
class CustomHttp extends Http {
public function customGet($url) {
return $this->get($url)->then(function ($response) {
return strtoupper($response); // Example transformation
});
}
}
Blocking the Event Loop:
Avoid mixing blocking calls (e.g., sleep(), file_get_contents without ReactPHP wrapper) in async code. Use the package's non-blocking methods instead.
Promise Leaks:
Always call .done() or .then() on promises to prevent memory leaks:
// Bad: Unattached promise
$file->getContents('url');
// Good: Attach handlers
$file->getContents('url')->done();
PHP 8.1+ Required:
Ensure your project uses PHP 8.1+ (or later) for compatibility with ReactPHP v3.
Error Propagation:
Unhandled promise rejections crash the event loop. Always use .otherwise() or .catch():
$file->getContents('url')
->catch(function ($error) {
// Handle error gracefully
});
Logging Promises:
Use ->then() for debugging:
$file->getContents('url')
->then(function ($contents) {
logger()->debug("Contents:", ['data' => $contents]);
});
Timeouts: Add timeouts to prevent hanging:
$promise = $file->getContents('url');
$loop->addTimer(5, function () use ($promise) {
$promise->cancel(); // Cancel after 5 seconds
});
Event Loop Inspection: Check for stuck loops with:
$loop->futureTick(function () {
logger()->debug("Loop is still running...");
});
Custom Functions:
Extend the package by creating new classes (e.g., Drift\ReactFunctions\CustomFunction):
namespace Drift\ReactFunctions;
use React\Promise\PromiseInterface;
class CustomFunction {
public function __construct(private LoopInterface $loop) {}
public function asyncTask($input): PromiseInterface {
return new Promise(function ($resolve) {
$this->loop->addTimer(1, function () use ($resolve, $input) {
$resolve($input * 2); // Example async task
});
});
}
}
Middleware: Add middleware to promises for cross-cutting concerns (e.g., logging, retries):
$promise->then(function ($result) {
logger()->info("Operation succeeded", ['result' => $result]);
return $result;
});
Configuration: Override defaults (e.g., timeout values) via dependency injection:
$http = new Http($loop, [
'timeout' => 10.0, // Default: 5.0 seconds
]);
How can I help you explore Laravel packages today?