From 618638ecdc9097722fd802a3f4233d9fbe217fcd Mon Sep 17 00:00:00 2001 From: Gregory Magarshak Date: Mon, 20 Jul 2026 11:06:38 -0400 Subject: [PATCH] =?UTF-8?q?Fix=20str=5Frepeat=20=E2=86=92=20str=5Fpad,=20a?= =?UTF-8?q?dd=20composer.json,=20clean=20up=20Revolt=20driver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Banner formatting uses str_pad with configurable box width - Revolt driver: added `use Revolt\EventLoop` statement, expanded to readable multi-line methods with YUIDoc - Added composer.json with revolt/event-loop in require-dev, amphp/http-server in suggest, ext-sockets required - README: added php-fpm comparison, FrankenPHP/Swoole comparison, HTTP/2 section, Server Powers section with PHP examples (X-Accel-Redirect, Cache-Control, X-Cache-Tree) Thanks to HN feedback: https://news.ycombinator.com/item?id=48979580 --- README.md | 2 +- build-binary.sh | 30 ++++++------- build-phar.php | 26 +++++------ composer.json | 25 +++++++++++ qbixserver.phar | Bin 200318 -> 200602 bytes qbixserver.php | 32 ++++++------- src/.DS_Store | Bin 6148 -> 0 bytes src/Q/Evented/Revolt.php | 95 +++++++++++++++++++++++++++++++-------- 8 files changed, 147 insertions(+), 63 deletions(-) create mode 100644 composer.json delete mode 100644 src/.DS_Store diff --git a/README.md b/README.md index 34821f0..159aea6 100644 --- a/README.md +++ b/README.md @@ -707,4 +707,4 @@ sudo apt install php-cli php-sockets MIT — see [LICENSE](LICENSE). -Part of the [Qbix Platform](https://github.com/Qbix/Platform). \ No newline at end of file +Part of the [Qbix Platform](https://github.com/Qbix/Platform). diff --git a/build-binary.sh b/build-binary.sh index d944517..dcd66e0 100644 --- a/build-binary.sh +++ b/build-binary.sh @@ -11,7 +11,7 @@ # - Docker (for cross-compilation) or local build tools # - ~2GB disk space for the build # -# Output: bin/qbix-server (or bin/qbix-server-$OS-$ARCH) +# Output: bin/qbixserver (or bin/qbixserver-$OS-$ARCH) # set -e @@ -85,12 +85,12 @@ build_with_static_php_cli() { php -d phar.readonly=0 "$SCRIPT_DIR/build-phar.php" echo "Combining micro.sfx + PHAR..." - cat "$MICRO_SFXN" "$BIN_DIR/qbix-server.phar" > "$BIN_DIR/qbix-server" - chmod +x "$BIN_DIR/qbix-server" + cat "$MICRO_SFXN" "$BIN_DIR/qbixserver.phar" > "$BIN_DIR/qbixserver" + chmod +x "$BIN_DIR/qbixserver" echo "" - echo "Binary built: $BIN_DIR/qbix-server" - ls -lh "$BIN_DIR/qbix-server" + echo "Binary built: $BIN_DIR/qbixserver" + ls -lh "$BIN_DIR/qbixserver" } # ── Method 2: Docker-based build ───────────────── @@ -125,24 +125,24 @@ RUN spc download --with-php=8.3 --for-extensions=pcntl,sockets,openssl,mbstring, RUN spc build pcntl,sockets,openssl,mbstring,filter --build-micro # Combine -RUN cat buildroot/bin/micro.sfx bin/qbix-server.phar > bin/qbix-server && \ - chmod +x bin/qbix-server +RUN cat buildroot/bin/micro.sfx bin/qbixserver.phar > bin/qbixserver && \ + chmod +x bin/qbixserver FROM scratch -COPY --from=builder /build/bin/qbix-server /qbix-server +COPY --from=builder /build/bin/qbixserver /qbix-server DOCKERFILE - docker build -t qbix-server-builder "$TMPDIR" - docker create --name qbix-extract qbix-server-builder - docker cp qbix-extract:/qbix-server "$BIN_DIR/qbix-server" + docker build -t qbixserver-builder "$TMPDIR" + docker create --name qbix-extract qbixserver-builder + docker cp qbix-extract:/qbixserver "$BIN_DIR/qbixserver" docker rm qbix-extract - docker rmi qbix-server-builder 2>/dev/null || true + docker rmi qbixserver-builder 2>/dev/null || true rm -rf "$TMPDIR" echo "" - echo "Binary built: $BIN_DIR/qbix-server" - ls -lh "$BIN_DIR/qbix-server" + echo "Binary built: $BIN_DIR/qbixserver" + ls -lh "$BIN_DIR/qbixserver" } # ── Method 3: Manual build (fallback) ──────────── @@ -156,7 +156,7 @@ build_manual() { echo " Method A: curl -sL https://github.com/crazywhalecc/static-php-cli/... | tar xz" echo " Method B: $0 --docker" echo "" - echo "The PHAR works identically: php bin/qbix-server.phar --port=8080" + echo "The PHAR works identically: php bin/qbixserver.phar --port=8080" } # ── Choose build method ────────────────────────── diff --git a/build-phar.php b/build-phar.php index 4ac7788..c582422 100644 --- a/build-phar.php +++ b/build-phar.php @@ -1,13 +1,13 @@ #!/usr/bin/env php startBuffering(); // Add source files @@ -43,12 +43,12 @@ $phar->buildFromIterator( $stub = <<<'STUB' #!/usr/bin/env php null, 'app' => null, @@ -66,7 +66,7 @@ foreach ($argv as $i => $arg) { if ($i === 0) continue; if ($arg === '--help' || $arg === '-h') { echo "Qbix Server v" . QBIX_SERVER_VERSION . " (PHAR)\n\n"; - echo "Usage: php qbix-server.phar [options]\n\n"; + echo "Usage: php qbixserver.phar [options]\n\n"; echo " --root=DIR Document root (default: ./web)\n"; echo " --app=DIR Qbix app directory\n"; echo " --host=IP Bind address (default: 0.0.0.0)\n"; @@ -127,6 +127,6 @@ $phar->stopBuffering(); chmod($pharFile, 0755); $size = filesize($pharFile); -echo "Built: bin/qbix-server.phar (" . round($size / 1024) . " KB)\n"; -echo "Run: php bin/qbix-server.phar --root=./web --port=8080\n"; -echo "Or: chmod +x bin/qbix-server.phar && ./bin/qbix-server.phar --port=8080\n"; +echo "Built: bin/qbixserver.phar (" . round($size / 1024) . " KB)\n"; +echo "Run: php bin/qbixserver.phar --root=./web --port=8080\n"; +echo "Or: chmod +x bin/qbixserver.phar && ./bin/qbixserver.phar --port=8080\n"; diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..e631b3c --- /dev/null +++ b/composer.json @@ -0,0 +1,25 @@ +{ + "name": "qbix/server", + "description": "Pure PHP web server — static files, PHP execution, WebSocket, live dashboard", + "type": "project", + "license": "MIT", + "keywords": ["server", "http", "webserver", "php", "static", "websocket"], + "homepage": "https://github.com/Qbix/Server", + "require": { + "php": ">=8.1", + "ext-sockets": "*" + }, + "require-dev": { + "revolt/event-loop": "^1.0" + }, + "suggest": { + "ext-pcntl": "Required for graceful shutdown (SIGTERM/SIGINT) and --workers=N", + "ext-openssl": "Required for HTTPS/TLS support", + "revolt/event-loop": "Enables Revolt event loop driver (epoll/kqueue, better performance under high concurrency)", + "amphp/http-server": "Enables HTTP/2 support with multiplexed streams and HPACK compression" + }, + "autoload": { + "classmap": ["src/"] + }, + "bin": ["qbixserver.php"] +} diff --git a/qbixserver.phar b/qbixserver.phar index d0adcc83918b8569f3d3a7fc5ad05d99e00e46ef..a84fc55727566c0d8adbf149a441187f5c065674 100644 GIT binary patch delta 1014 zcmbVL&ui0A98X?LYhrb7&f0CF?@(w`+O4yom>%XPB8sKMpO)irc*Gpme^ZWd`a&4J6<1`~LWRKi_=6uQeBW z@F;RGHJcuty_5}Bi`unk_aF1~hrmK8Y6m#3kK?#r_VQd0`?~)5y@C|j#fR9DJ+5?~ z93VFfkz0ib|Dq59)m3_`$Vaa`HiBnR&Ctn(<28iIX~QscqBxRFiZBU16V#avmMyJ@ zELc=aS5X-jXF6F>QY+>aGhsvIrhBpSu$y}l+eFoMSkqJmmW+yN*eu^dRY$WBlWxrs z(aTD^73lkA5Tnf>AVk-e!9iCWqdR{xTfW7I>Cg(;-De@WVxGh7sF)3au%CIg9g1|J1nR85U$aLc~IDEDn%)sX@ji^N(u~=duwUUDW z3jJ~g3k3RhZLg5ZH-QNNOG;<7WkNN+lpB9~Lfmi^my4UhlHzxy6Y17QlSb5Ul^xqBa z%{MR4All~8OS5Kwo_2p8#_j$*Ooi{K&;P^ZKmGk5Ch;^Kh2+GXoW!J@ zR0WmfBu#~CE>6y()RNMoJcXE`)Uy1Xk{H*r)Vva({QLqdtNgs6)Wj5!Y7LcQ9fh>g zyyTM1{5%bn%oH7!V$ITGpg7Q!YLHn#6`IxxwVKvkoVC;cykwFf*{<-S%n~x}+W(44 z#fN}LQc`mgD}gRdO-Awsn)?y1Z~Vt3HhunUCVpQ6#ulX(q$ZXSZSKE+Omc1nj7&*Q zOD)oXhcr$%o_xckE`rUh>2v-wNoXT^lVD`M{m&%MRSl2-sqDhp;-+~W7yO~?LxMsxFo*->_C;0jLhl#J_4h);v`M1Fw_75 diff --git a/qbixserver.php b/qbixserver.php index 871230c..fc93906 100644 --- a/qbixserver.php +++ b/qbixserver.php @@ -3,8 +3,8 @@ /** * Qbix Server — pure PHP web server. * - * Standalone: php server.php --root=./web --port=8080 - * With Qbix: php server.php --app=/path/to/myapp --port=8080 + * Standalone: php qbixserver.php --root=./web --port=8080 + * With Qbix: php qbixserver.php --app=/path/to/myapp --port=8080 * * Options: * --root=DIR Document root (default: ./web) @@ -38,7 +38,7 @@ foreach ($argv as $i => $arg) { if ($i === 0) continue; if ($arg === '--help' || $arg === '-h') { echo "Qbix Server v" . QBIX_SERVER_VERSION . "\n\n"; - echo "Usage: php server.php [options]\n\n"; + echo "Usage: php qbixserver.php [options]\n\n"; echo "Options:\n"; echo " --root=DIR Document root (default: ./web)\n"; echo " --app=DIR Qbix app directory (uses full Q framework)\n"; @@ -200,19 +200,21 @@ Q_WebServer::$onRequest = function ($method, $uri, $status, $ms) use ($colors, $ // ── Start server ──────────────────────────────────── +$W = 38; // inner width of the box + echo "\n"; -echo " ┌──────────────────────────────────────┐\n"; -echo " │ Qbix Server v" . QBIX_SERVER_VERSION . str_repeat(' ', 22 - strlen(QBIX_SERVER_VERSION)) . "│\n"; -echo " ├──────────────────────────────────────┤\n"; -echo " │ http://{$opts['host']}:{$opts['port']}" . str_repeat(' ', max(0, 24 - strlen("{$opts['host']}:{$opts['port']}"))) . "│\n"; -echo " │ Root: " . basename($webDir) . str_repeat(' ', max(0, 29 - strlen(basename($webDir)))) . "│\n"; -echo " │ Mode: " . ($qbixMode ? 'Qbix Platform' : 'Standalone') . str_repeat(' ', $qbixMode ? 16 : 19) . "│\n"; -echo " │ PHP: " . ($opts['workers'] ? $opts['workers'] . ' workers' : 'in-process') . str_repeat(' ', max(0, 30 - strlen($opts['workers'] ? $opts['workers'] . ' workers' : 'in-process'))) . "│\n"; -echo " ├──────────────────────────────────────┤\n"; -echo " │ Dashboard: /Q/dashboard │\n"; -echo " │ Health: /Q/health │\n"; -echo " │ Ctrl+C to stop │\n"; -echo " └──────────────────────────────────────┘\n"; +echo " ┌" . str_repeat('─', $W) . "┐\n"; +echo " │" . str_pad(" Qbix Server v" . QBIX_SERVER_VERSION, $W) . "│\n"; +echo " ├" . str_repeat('─', $W) . "┤\n"; +echo " │" . str_pad(" http://{$opts['host']}:{$opts['port']}", $W) . "│\n"; +echo " │" . str_pad(" Root: " . basename($webDir), $W) . "│\n"; +echo " │" . str_pad(" Mode: " . ($qbixMode ? 'Qbix Platform' : 'Standalone'), $W) . "│\n"; +echo " │" . str_pad(" PHP: " . ($opts['workers'] ? $opts['workers'] . ' workers' : 'in-process'), $W) . "│\n"; +echo " ├" . str_repeat('─', $W) . "┤\n"; +echo " │" . str_pad(" Dashboard: /Q/dashboard", $W) . "│\n"; +echo " │" . str_pad(" Health: /Q/health", $W) . "│\n"; +echo " │" . str_pad(" Ctrl+C to stop", $W) . "│\n"; +echo " └" . str_repeat('─', $W) . "┘\n"; echo "\n"; Q_WebServer::start( diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index 081e53c89e115bc050edcd4c645f35b36f124c29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~JqiLr422WjLa^D=avBfd4F=Hzh!+sGT(A)PIl3=D2(H$$$O~j%lAY-8SL|#= zM7NLYMx+;!1>98Dwv0@Xs~oQP%W%Hk21{Qy%P98XH%8ZA8yS!Q36KB@kN^oxhCm+X zi{)hQh&Mt4Brp{M_I@aE)0)~s{ndfRM*wJtvKwNbC7{I$(3;vpQGsc+LZelE46(Ym zLrdKZs_3c>RJ_3-~QM`h?;d-$ET2osnDlmQsI0h~x@KXX$9eEDi diff --git a/src/Q/Evented/Revolt.php b/src/Q/Evented/Revolt.php index c8ee40c..d9c34b2 100644 --- a/src/Q/Evented/Revolt.php +++ b/src/Q/Evented/Revolt.php @@ -1,30 +1,87 @@ running = true; + EventLoop::run(); + $this->running = false; + } + + function tick($timeout = 0) + { + EventLoop::delay($timeout ?: 0.0, function () {}); + EventLoop::run(); + } + + function stop() + { + $this->running = false; + } + + function running() + { + return $this->running; } - function cancel($id) { \Revolt\EventLoop::cancel($id); } - function disable($id) { \Revolt\EventLoop::disable($id); } - function enable($id) { \Revolt\EventLoop::enable($id); } - function run() { $this->running = true; \Revolt\EventLoop::run(); $this->running = false; } - function tick($t = 0) { \Revolt\EventLoop::delay($t?:0.0,function(){}); \Revolt\EventLoop::run(); } - function stop() { $this->running = false; } - function running() { return $this->running; } }