workerman/coroutine
Workerman coroutine library providing lightweight concurrency tools for PHP: Coroutine, Channel, Barrier, Parallel, and Pool. Designed to simplify async workflows and coordinated task execution in Workerman-based applications.
Installation:
composer require workerman/coroutine
Requires workerman/workerman (≥5.1) and PHP 8.1+ (PHP 8.4+ recommended for full compatibility).
First Use Case: Replace a blocking HTTP request with a coroutine:
use Workerman\Coroutine;
Coroutine::create(function () {
$client = new \Workerman\Coroutine\Http\Request();
$response = $client->get('https://api.example.com/data');
$data = json_decode($response, true);
// Process $data asynchronously
});
Where to Look First:
Coroutine, Channel, Pool, Parallel.Coroutine::create(): Spawn a coroutine.Channel::push()/pop(): Blocking-safe message passing.Pool::get()/release(): Connection pooling.Coroutine::create(function () {
// Async task (e.g., WebSocket handler, background job)
$result = processData();
logResult($result);
});
Coroutine::run(); // Blocks until all coroutines complete (use cautiously in Laravel).
// In a Laravel job
public function handle() {
Coroutine::create([$this, 'process']);
}
private function process() {
// Coroutine logic
}
$channel = new \Workerman\Coroutine\Channel(100);
// Producer (e.g., API consumer)
Coroutine::create(function () use ($channel) {
$channel->push(fetchData());
});
// Consumer (e.g., database writer)
Coroutine::create(function () use ($channel) {
while (true) {
$data = $channel->pop(); // Blocks until data is available
saveToDatabase($data);
}
});
if ($channel->hasConsumers()) {
$channel->push($data); // Safe to push
}
$pool = new \Workerman\Coroutine\Pool(5, function () {
return new \Workerman\Coroutine\Mysql\Connection('mysql:host=...');
});
Coroutine::create(function () use ($pool) {
$conn = $pool->get(); // Acquires a connection
$result = $conn->query('SELECT * FROM users');
$pool->release($conn); // Releases back to pool
});
$httpPool = new \Workerman\Coroutine\Pool(10, function () {
return new \Workerman\Coroutine\Http\Request();
});
$parallel = new \Workerman\Coroutine\Parallel();
$parallel->add(function () { /* Task 1 */ });
$parallel->add(function () { /* Task 2 */ });
$results = $parallel->wait(); // Array of results
Illuminate\Queue with a Pool-based worker:
$jobPool = new \Workerman\Coroutine\Pool(20, function () {
return new AsyncJobHandler();
});
Coroutine::create(function () use ($jobPool) {
while (true) {
$job = $jobPool->get()->handle();
$jobPool->release($job);
}
});
// In a separate Workerman process
$server = new \Workerman\Worker();
$server->onWorkerStart = function () {
\Workerman\Coroutine::runAll();
};
Blocking the Event Loop:
Coroutine::run() in Laravel’s HTTP middleware blocks the entire request pipeline.Memory Leaks:
Channel/Pool sizes or leaked coroutines.// Limit channel size
$channel = new \Workerman\Coroutine\Channel(1000);
// Close pools explicitly
$pool->closeConnections();
PHP 8.4+ Compatibility:
object array usage in older versions.v1.1.5+ and ensure workerman/workerman is updated.State Management:
Debugging Complexity:
Coroutine::create(function () {
$id = Coroutine::getId();
logger()->info("Coroutine $id started");
});
Coroutine::get() to inspect active coroutines.$coroutines = Coroutine::get();
foreach ($coroutines as $id => $coroutine) {
logger()->debug("Coroutine $id: " . $coroutine->getStatus());
}
if (!Coroutine::wait(1.0, function () use ($channel) {
return $channel->pop();
})) {
logger()->error("Timeout waiting for data");
}
try {
Coroutine::create(function () {
try {
// Risky operation
} catch (\Throwable $e) {
Coroutine::throwException($e); // Propagate to parent
}
});
} catch (\Throwable $e) {
logger()->error("Coroutine failed: " . $e->getMessage());
}
Swoole vs. Workerman:
event extension).Pool Sizing:
Pool size to max concurrent connections (e.g., DB connections).Pool::getConnectionCount() to monitor usage.Channel Backpressure:
Channel::hasConsumers() before pushing:
if ($channel->hasConsumers()) {
$channel->push($data);
}
class AsyncJob extends \Workerman\Coroutine\Coroutine {
public function run() {
// Override coroutine logic
}
}
$channel = new \Workerman\Coroutine\Channel();
$channel->onPush = function ($data) {
// Pre-process data
return $data;
};
$pool = new \Workerman\Coroutine\Pool(5, function () {
return new CustomConnection();
});
Service Container:
Coroutine::create(function () {
$repo = new \App\Repositories\UserRepository(); // No `app()->make()`
});
Eloquent ORM:
swoole-mysql) or raw PDO:
$pdo = new \PDO('mysql:host=...', 'user', 'pass');
$pdo->setAttribute(\PDO::ATTR_PERSISTENT, false);
Middleware:
class AsyncAuthMiddleware
How can I help you explore Laravel packages today?