Persistent PHP application server12 min read

Workerman: a persistent PHP application server

Learn what Workerman is, how its long-running worker model differs from classic PHP request lifetimes, and how to avoid state leakage in a small Composer-installed HTTP worker.

PHP compatibility and project lifecycle

Unknown from the supplied source. The manual states it covers Workerman 3.x to 5.x and supports Linux and Windows, but the excerpt gives no PHP version matrix, so PHP 8.4 support is unconfirmed. Check the manual's System Requirements page.

Official sources checked 2026-10-22. Browser lessons on this site run PHP 8.5.

What Workerman is

The official manual describes Workerman as an open-source, high-performance PHP application container developed in pure PHP. It is not an MVC framework. It is a lower-level, general service framework for building network servers. The manual lists TCP and UDP services, WebSocket and HTTP services, game and IoT back ends, proxies, and message-queue components.

What it is not

The classification matters, so it is worth being precise:

  • Workerman is not a PHP interpreter and not an alternative engine. Your code runs on a normal PHP installation, and Workerman is PHP code that runs on top of it.
  • It is not PHP core. PHP core provides the language and extensions. Workerman is a library and server loop you install with Composer.
  • It is not a CMS. Drupal is a CMS product built on PHP. Workerman has no content model, admin interface or content storage.
  • It is not a full-stack framework. The manual points developers who want HTTP sites to webman, a separate project. The manual also mentions GatewayWorker, a wrapper for long-connection applications. Those projects are outside the supplied source and are not described further here.
  • It is distinct from Swoole and Open Swoole. These are separate projects, and the supplied source does not discuss them. Do not assume that documentation or behavior for one carries over to another. The manual describes Workerman as pure PHP.

Architecture and execution model

The manual compares Workerman to a PHP version of Nginx. It uses multiple processes with epoll and non-blocking I/O. A master process starts worker processes, and each worker holds its own connections and its own memory.

You create a Worker object that listens on an address, set properties such as the process count and name, and attach callbacks. The manual's table of contents lists callbacks such as onWorkerStart, onConnect, onMessage and onClose, and an interface called runAll.

A classic PHP-FPM or Apache request lifetime starts clean and discards almost everything when the request ends. A persistent worker loads code once and then handles many requests inside the same process. This is why the manual says Workerman avoids per-request initialization and destruction. It also means PHP state can survive across requests and across different users.

State leakage and cleanup

In a persistent worker, anything stored in a static property, global variable, singleton, cache array or long-lived connection can outlive the request that created it. The risks include:

  • Data from one user's request appearing in another user's response.
  • Slow memory growth from arrays that are never cleared.
  • Stale or half-open database or socket resources.

Practical habits:

  • Keep per-request data in local variables or in the request object.
  • If you must use static or shared holders, reset them in a finally block so cleanup runs even on exceptions.
  • Create long-lived resources deliberately, for example in onWorkerStart, and plan how to detect and replace broken ones.
  • Treat each worker process as having separate memory. A counter in one process is not shared with another, and a separate component is needed for shared data.
  • Remember that code changes need a restart. The manual has FAQ entries on code changes not taking effect, on persistence of objects and resources, and on restarting after a number of requests. Their details are not in the supplied excerpt, so read them in the manual.

Practical use cases

From the manual: instant messaging and push, Internet of Things communication, game servers, HTTP services, SOA-style service wrappers, proxies and other custom servers. For a conventional website, the manual recommends webman instead of building directly on Workerman.

Installation approach

The manual has Installation, System Requirements, and Download and Install sections, plus a Start and Stop page. The supplied excerpt shows only the table of contents and the preface, so exact requirements and current versions are unknown here. The manual says it covers Workerman 3.x to 5.x and that Linux and Windows are supported. On Windows, PHP must be correctly configured in the environment variables.

The example below uses Composer. The package name and the class namespaces in it are not shown in the supplied excerpt, so confirm them against the manual's Download and Install and Worker pages during review. Do not assume PHP 8.4 support until the manual's System Requirements page confirms it.

Strengths

  • A small core with components for timers, async clients, Redis, HTTP and queues, per the manual.
  • A single PHP language for both web-style and non-HTTP protocols.
  • No separate web server or FastCGI layer is required to speak HTTP, because Workerman handles the protocol itself.

The manual makes performance claims. This guide does not repeat them, because they are unmeasured here.

Limitations

  • You manage a long-running process, including restarts, logging and memory growth.
  • State leakage is your responsibility.
  • Blocking calls can stall every connection handled by a worker process.
  • Support status, license, release dates and PHP version compatibility are unknown from the supplied source. Lifecycle is recorded as unknown.
  • The manual lists an Unsupported Functions/Features page that is not shown in the excerpt.

Deployment notes

For local work, bind to 127.0.0.1 as the example does. Do not expose a development port on public interfaces. Workerman is not FastCGI, so there is no FastCGI socket to protect. The manual has FAQ entries on running behind Nginx or Apache, on SSL/TLS and HTTPS, on getting the real client IP through a proxy, on starting with the system, and on smooth restarts. Read those before planning production deployment. Their contents are not in the supplied excerpt, so no production recommendations are made here.

The example

The example is a small HTTP worker with two processes. It keeps one deliberate per-process counter to show that memory persists between requests. It also stores per-request data in a static holder and clears it in a finally block. Run it natively with Composer. No Docker image is used, because the source does not specify one.

Requests to the same worker should show the counter increasing. With two processes the pid and counter may vary between requests, which shows that each process keeps separate memory.

Source

The only official source used is the Workerman Official Manual at https://manual.workerman.net/doc/en/.

Practical examples

These examples have not been run in a live environment yet. Commands and versions follow the official documentation linked on this page; check them before relying on them, and report a problem if one does not work.

Minimal two-process HTTP worker with per-request cleanup

A native Composer example, not a browser exercise. It starts a Workerman HTTP worker bound to 127.0.0.1:8080. The worker keeps a per-process counter, which persists by design, and a static per-request holder that is reset after every request. The package name and namespaces must be verified against the manual before use.

composer.json

{
    "name": "learnphponline/workerman-demo",
    "description": "Minimal Workerman HTTP worker demo",
    "type": "project"
}

server.php

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
use Workerman\Worker;

final class RequestState
{
    /** @var array<string, string> */
    public static array $items = [];

    public static function reset(): void
    {
        self::$items = [];
    }
}

// Bound to loopback only for local development.
$worker = new Worker('http://127.0.0.1:8080');
$worker->count = 2;
$worker->name = 'learn-http';

// Deliberately persistent: lives as long as this worker process.
$processHits = 0;

$worker->onMessage = function (TcpConnection $connection, Request $request) use (&$processHits): void {
    try {
        RequestState::$items['path'] = $request->path();
        $processHits++;

        $body = 'path=' . RequestState::$items['path'] . "\n"
            . 'pid=' . getmypid() . "\n"
            . 'process_hits=' . $processHits . "\n";

        $connection->send(new Response(200, ['Content-Type' => 'text/plain; charset=utf-8'], $body));
    } finally {
        // Cleanup: nothing request-specific survives into the next request.
        RequestState::reset();
    }
};

Worker::runAll();

Install and run

mkdir workerman-demo
cd workerman-demo
composer require workerman/workerman
php server.php start
curl http://127.0.0.1:8080/hello
curl http://127.0.0.1:8080/hello
php server.php stop

Expected behavior

After start, the server listens on 127.0.0.1:8080. Each curl request returns plain text with the path, the worker pid and a process_hits value. Repeated requests handled by the same process show a higher process_hits, and the other process keeps its own count. The static request holder is empty at the start of each request.

The package name, namespaces and Response and Request APIs must be checked against the manual. PHP 8.4 compatibility is unconfirmed by the supplied source. Windows start and stop behavior may differ, so see the manual's Windows notes. The process counter is not shared between processes and resets on restart. This is a development setup, not a production deployment.