In this tutorial, you'll learn how to implement real-time asynchronous task processing in a Yii2 application using PHP Swoole as a microservice. This architecture lets you offload time-consuming tasks (like sending emails or generating reports) to a separate process, without waiting for them to complete in your web request.
This is not about running Yii2 on Swoole. Instead, we embed a Swoole HTTP server alongside our Yii2 project, acting as a background worker that receives and processes tasks sent from Yii2 controllers.
cron.yii2-app/
├── swoole/
│ ├── runserver.php
│ ├── TaskHandlerInterface.php
│ ├── TaskRouter.php
│ ├── task-log.txt
│ └── handlers/
│ ├── SendEmailTask.php
│ └── GenerateReportTask.php
├── controllers/
│ └── SiteController.php
├── web/
│ └── index.php
...File: swoole/runserver.php
<?php
declare(strict_types=1);
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';
$config = require __DIR__ . '/../config/console.php';
$application = new yii\console\Application($config);
$server = new \Swoole\Http\Server("127.0.0.1", 9501);
$server->on("start", function (\Swoole\Http\Server $server) {
echo "Swoole task server started at http://127.0.0.1:9501\n";
});
$server->on("request", function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) {
$data = json_decode($request->rawContent(), true);
if (!$data || !isset($data['task'])) {
$response->status(400);
$response->end(json_encode(['error' => 'No task']));
return;
}
// Immediately respond before processing
$response->end(json_encode(['status' => 'Task queued']));
// Run task in coroutine asynchronously
go(function () use ($data) {
$payload = $data['payload'] ?? [];
\app\swoole\TaskRouter::dispatch($data['task'], $payload);
});
});
$server->start();File: swoole/TaskHandlerInterface.php
<?php
namespace app\swoole;
interface TaskHandlerInterface
{
public function handle(array $payload): void;
}File: swoole/TaskRouter.php
<?php
namespace app\swoole;
class TaskRouter
{
public static function dispatch(string $taskName, array $payload): bool
{
$class = '\\app\\swoole\\handlers\\' . ucfirst($taskName) . 'Task';
file_put_contents(__DIR__ . '/task-log.txt', "Trying to dispatch: $class\n", FILE_APPEND);
if (!class_exists($class)) {
file_put_contents(__DIR__ . '/task-log.txt', "Class not found: $class\n", FILE_APPEND);
return false;
}
$handler = new $class();
if (!$handler instanceof TaskHandlerInterface) {
file_put_contents(__DIR__ . '/task-log.txt', "Handler does not implement TaskHandlerInterface: $class\n", FILE_APPEND);
return false;
}
$handler->handle($payload);
return true;
}
}File: swoole/handlers/SendEmailTask.php
<?php
namespace app\swoole\handlers;
use app\swoole\TaskHandlerInterface;
class SendEmailTask implements TaskHandlerInterface
{
public function handle(array $payload): void
{
// Simulate sending an email
$to = $payload['to'] ?? 'unknown';
$subject = $payload['subject'] ?? '(no subject)';
$users = json_encode([]);
$i = 1;
while ($i <= 100) {
\Swoole\Coroutine::sleep(1);
$i++;
file_put_contents(__DIR__ . '/../task-log.txt', "$i\n", FILE_APPEND);
}
file_put_contents(__DIR__ . '/../task-log.txt', "Email Send , [EMAIL] To: $to, Subject: $subject and total users id $users\n", FILE_APPEND);
}
}File: swoole/handlers/GenerateReportTask.php
<?php
namespace app\swoole\handlers;
use app\swoole\TaskHandlerInterface;
class GenerateReportTask implements TaskHandlerInterface
{
public function handle(array $payload): void
{
// Generate Report
file_put_contents(__DIR__ . '/../task-log.txt', "Report Generated for : " . json_encode($payload) . "\n", FILE_APPEND);
}
}<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
class SiteController extends Controller
{
public function actionCheck()
{
$client = new \yii\httpclient\Client([
'baseUrl' => 'http://127.0.0.1:9501',
]);
$response = $client->createRequest()
->setMethod('POST')
->setUrl('/')
->setFormat(\yii\httpclient\Client::FORMAT_JSON)
->setData([
'task' => 'SendEmail',
'payload' => [
'to' => 'user@example.com',
'subject' => 'Welcome from Yii2',
],
])
->send();
if ($response->isOk) {
echo 'Task sent successfully: ' . $response->content;
}
$response = $client->createRequest()
->setMethod('POST')
->setUrl('/')
->setFormat(\yii\httpclient\Client::FORMAT_JSON)
->setData([
'task' => 'GenerateReport',
'payload' => [
'report_date' => date('Y-m-d'),
'report_title' => 'UP State Report',
],
])
->send();
if ($response->isOk) {
echo 'Task sent successfully: ' . $response->content;
exit;
}
return 'Error: ' . $response->statusCode;
}
}Run the following in a separate terminal:
php swoole/runserver.phpNow when you visit http://localhost:8080/site/check, Yii2 sends two background tasks to the Swoole server. Swoole handles them immediately, logs the results, and your controller returns without delay.
You’ve just upgraded your Yii2 app to support real-time microservice task delegation with PHP Swoole — no cron jobs, no blocking.
handlers/.Sign in to join the discussion and post comments.
Sign in