How to Run Yii2 with Swoole HTTP Server for Realtime Asynchronous Task Processing

  Add to Bookmark

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.


Benefits of This Architecture

  • No more waiting for long tasks to complete during user requests.
  • Real-time background processing without relying on cron.
  • Event-driven and fast thanks to Swoole.
  • Clean and extendable codebase using handlers and task routing.

Project Structure

yii2-app/
├── swoole/
│   ├── runserver.php
│   ├── TaskHandlerInterface.php
│   ├── TaskRouter.php
│   ├── task-log.txt
│   └── handlers/
│       ├── SendEmailTask.php
│       └── GenerateReportTask.php
├── controllers/
│   └── SiteController.php
├── web/
│   └── index.php
...

Step 1: Setup the Swoole HTTP Server

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();

Step 2: Task Routing and Interface

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;
    }
}

Step 3: Define Task Handlers

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);
    }
}

Step 4: Send Tasks from Yii2

<?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;
    }
}

Step 5: Run the Swoole Server

Run the following in a separate terminal:

php swoole/runserver.php

You’re Done!

Now 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.

  • You can add more task types by creating new classes in handlers/.
  • Consider moving to Redis or queues later for larger scale.
  • Use this architecture to process webhooks, notifications, or database syncs instantly.

  Download Swoole Source Ccode