Search by

dvaknheo / workermanhttpd

dvaknheo

workerman http server andplugin for duckphp

Package info

github.com/dvaknheo/workermanhttpd

Homepage

pkg:composer/dvaknheo/workermanhttpd

Statistics

Installs: 25

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.6 2024-05-20 06:29 UTC

This package is auto-updated.

Last update: 2026-09-28 01:18:40 UTC


README

English | 中文

*** v1.0.7 (development) ***

What is WorkermanHttpd

WorkermanHttpd lets the same PHP code run on Workerman and php-fpm with almost no changes. It is a thin wrapper around Workerman\Worker, built around two ideas:

  • echo directly — output is captured with an output buffer and becomes the response body
  • use superglobals directly — $_GET, $_POST, $_REQUEST, $_COOKIE, $_FILES, $_SESSION, $_SERVER

A few system functions have to be routed through WorkermanHttpd static wrappers (same parameters as the native functions):

function use instead
header() WorkermanHttpd::header()
setcookie() WorkermanHttpd::setcookie()
exit() WorkermanHttpd::exit()
session_start() WorkermanHttpd::session_start()
session_id() WorkermanHttpd::session_id()
session_destroy() WorkermanHttpd::session_destroy()
session_set_save_handler() WorkermanHttpd::session_set_save_handler()
register_shutdown_function() WorkermanHttpd::register_shutdown_function()

Call WorkermanHttpd::system_wrapper_get_providers() to list them.

Install

composer require dvaknheo/workermanhttpd

Requires PHP >= 7.2 and workerman/workerman ^4.0.4.

Basic usage

<?php
require(__DIR__.'/vendor/autoload.php');

function hello()
{
    \WorkermanHttpd\WorkermanHttpd::header('X-Test: '.DATE(DATE_ATOM));
    echo "<h1>hello ,have a good start.</h1>\n";
    return true;   // true = handled; false = 404
}

\WorkermanHttpd\WorkermanHttpd::RunQuickly([
    'host' => '127.0.0.1',
    'port' => 8080,
    'http_handler' => 'hello',
]);

Browse http://127.0.0.1:8080/.

Using it with DuckPhp

This library implements DuckPhp's official HTTP-server plug-in slot, so point --http_server at it:

php bin/cli.php run --http_server=WorkermanHttpd/HttpServerForDuckPhp --port=8080

DuckPhp\Component\Command::command_run() resolves http_server to a class name, swaps the HttpServer singleton for it, and then calls init($options)->run() on your class. This library therefore ships:

  • WorkermanHttpd\WorkermanHttpd — the main class; option http_app_class names your DuckPhp application class;
  • WorkermanHttpd\HttpServerForDuckPhp — the same class plus a declaration of DuckPhp\HttpServer\HttpServerInterface.

Note: HttpServerForDuckPhp requires DuckPhp at compile time (implements is resolved then), which is exactly why it lives in its own file — users without DuckPhp installed never autoload it.

You can also start the server yourself, without DuckPhp's CLI:

\WorkermanHttpd\WorkermanHttpd::RunQuickly([
    'port' => 8080,
    'http_app_class' => \YourProject\System\App::class,
    'path' => __DIR__,
]);

Options

$options = [
    // listen
    'host' => '127.0.0.1',
    'port' => 8080,

    // Workerman
    'worker_name' => 'WorkermanHttpd',
    'worker_count' => -1,          // -1 = CPU cores x 2
    'worker_properties' => [],     // assigned straight onto Workerman\Worker
    'pid_file' => null,            // becomes Worker::$pidFile; used by getPid()/close()
    'stdout_file' => null,         // becomes Worker::$stdoutFile
    'command' => 'start',          // start / stop / reload / restart
    'background' => false,         // daemon mode (not supported on Windows)
    'gracefull' => false,          // -g graceful restart

    // request / paths
    'path' => '',                  // project root; defaults to the startup cwd
    'doc_root' => null,            // $_SERVER['DOCUMENT_ROOT']; auto-detects path/public
    'request_class' => '',         // defaults to WorkermanHttpd\Request
    'http_enable_cache' => false,  // Workerman's request-object cache; must stay off in a worker

    // DuckPhp
    'http_app_class' => null,      // FQCN of the DuckPhp application class
    'http_app_options' => [],      // extra options merged into the application
    // Components renewed for each request (clone + re-init). See below.
    // Set to false to disable the per-request container entirely.
    'http_app_renew_classes' => [
        'DuckPhp\\Core\\Route', 'DuckPhp\\Core\\View', 'DuckPhp\\Component\\Lang',
    ],

    // http_handler mode
    'http_handler' => null,
    'http_exception_handler' => null,
    'http_404_handler' => null,

    // file modes
    'http_handler_basepath' => '',      // prefix for the two paths below
    'http_handler_root' => null,        // document root: resolve a file per URL path
    'http_handler_file' => null,        // single entry file: every URL goes here
    'with_http_handler_root' => false,  // let a false http_handler fall through
    'with_http_handler_file' => false,  // let a root miss fall through to the entry file
    'enable_resource_file' => true,     // send non-PHP files found under the root
];

File modes

// Equivalent to nginx's `root`: run the PHP file a URL resolves to, send static files.
\WorkermanHttpd\WorkermanHttpd::RunQuickly([
    'port' => 8080,
    'http_handler_root' => __DIR__.'/public',
]);

// Single entry file: every URL is handled by one file (REQUEST_URI/PATH_INFO preserved).
\WorkermanHttpd\WorkermanHttpd::RunQuickly([
    'port' => 8080,
    'http_handler_file' => __DIR__.'/public/index.php',
]);

http_handler_root supports / → index.php, an exact file, a directory's index.php, xxx.php/extra (the remainder becomes PATH_INFO), and walking up the path looking for the first index.php. Static files are streamed by Workerman (Response::withFile()), so large resources are not read into memory.

Security: the path is split on / and any . or .. segment is a 404; then a realpath() containment check (which also resolves symlinks) confirms the final file is still inside the document root. Both are needed.

Per-request isolation in a resident worker

A Workerman worker is a resident process, while DuckPhp keeps every component singleton in a process-level static (PhaseContainer::$instance). Left alone, state a request writes into a component outlives that request — the measured symptom is that a single request calling Route::_()->addRouteHook(...) permanently swallows every later request of that worker.

So the server installs WorkermanHttpd\PerRequestPhaseContainer by default: the container as it stands right after application init is kept untouched as a template, each request gets a copy of it, and the classes listed in http_app_renew_classes are replaced by a clone of the template instance, re-init()ed. Cloning (rather than constructing) is the important part: Route receives its route hooks from the RouteHook components during application init, and a freshly built Route would have none of them.

Everything not listed is shared by reference — database connections, loggers, the application object — because requests within a worker are sequential; only per-request state needs renewing. If your own extension keeps per-request state, add its class name to http_app_renew_classes.

Classes

Everything except the main class and SingletonExTrait is stateless.

WorkermanHttpd

The main class. Uses SingletonExTrait (a replaceable singleton).

Static: RunQuickly($options), _($object = null), G($object = null), Request(), Response(), Worker(), OnWorkerStart($worker), OnMessage($connection, $request), OnMasterReload(), plus the system wrappers listed above.

Instance: init(array $options, ?object $context = null), run(), getPid(), close().

HttpServerForDuckPhp

A WorkermanHttpd subclass that additionally declares DuckPhp\HttpServer\HttpServerInterface (RunQuickly / run / getPid / close), and understands the -H/-P/-t/-b shorthand switches. This is what --http_server=WorkermanHttpd/HttpServerForDuckPhp loads.

Request / Response

Extend Workerman\Protocols\Http\Request / Response and use SingletonExTrait. Replace them with Request::G(MyRequest::G()).

ExitException

The interruption exception. Do not throw it directly — call WorkermanHttpd::exit().

When __EXIT_EXCEPTION is already defined (DuckPhp defines it as DuckPhp\Core\ExitException), exit() throws that class, which is what makes DuckPhp's exception manager swallow it silently.

SingletonExTrait

A replaceable-singleton trait with the same effect as DuckPhp's; it honours the __SINGLETONEX_REPALACER macro.

Testing

The repository ships an end-to-end acceptance suite: it really starts a Workerman server, drives it over real HTTP, then kills it.

composer install
php dev/e2e.php              # everything
php dev/e2e.php --list       # list cases
php dev/e2e.php --filter=duckphp --dump

It needs a local DuckPhp checkout (default ../DNMVCS, override with DUCKPHP_PATH). dev/server.php is the test server entry point and dev/project/ is a minimal DuckPhp demo project.

Notes

  • This library does not depend on DuckPhp; DuckPhp appears only in require-dev and in HttpServerForDuckPhp.
  • In a resident worker $_SERVER is rebuilt from a startup snapshot for every request, so nothing leaks from one request into the next.
  • Workerman does not support multiple processes or --background on Windows; use worker_count = 1 there.