mirror of
https://github.com/Qbix/webserver.git
synced 2026-07-22 07:57:23 +02:00
Fix str_repeat → str_pad, add composer.json, clean up Revolt driver
- 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
This commit is contained in:
+15
-15
@@ -11,7 +11,7 @@
|
|||||||
# - Docker (for cross-compilation) or local build tools
|
# - Docker (for cross-compilation) or local build tools
|
||||||
# - ~2GB disk space for the build
|
# - ~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
|
set -e
|
||||||
@@ -85,12 +85,12 @@ build_with_static_php_cli() {
|
|||||||
php -d phar.readonly=0 "$SCRIPT_DIR/build-phar.php"
|
php -d phar.readonly=0 "$SCRIPT_DIR/build-phar.php"
|
||||||
|
|
||||||
echo "Combining micro.sfx + PHAR..."
|
echo "Combining micro.sfx + PHAR..."
|
||||||
cat "$MICRO_SFXN" "$BIN_DIR/qbix-server.phar" > "$BIN_DIR/qbix-server"
|
cat "$MICRO_SFXN" "$BIN_DIR/qbixserver.phar" > "$BIN_DIR/qbixserver"
|
||||||
chmod +x "$BIN_DIR/qbix-server"
|
chmod +x "$BIN_DIR/qbixserver"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Binary built: $BIN_DIR/qbix-server"
|
echo "Binary built: $BIN_DIR/qbixserver"
|
||||||
ls -lh "$BIN_DIR/qbix-server"
|
ls -lh "$BIN_DIR/qbixserver"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Method 2: Docker-based build ─────────────────
|
# ── 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
|
RUN spc build pcntl,sockets,openssl,mbstring,filter --build-micro
|
||||||
|
|
||||||
# Combine
|
# Combine
|
||||||
RUN cat buildroot/bin/micro.sfx bin/qbix-server.phar > bin/qbix-server && \
|
RUN cat buildroot/bin/micro.sfx bin/qbixserver.phar > bin/qbixserver && \
|
||||||
chmod +x bin/qbix-server
|
chmod +x bin/qbixserver
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
COPY --from=builder /build/bin/qbix-server /qbix-server
|
COPY --from=builder /build/bin/qbixserver /qbix-server
|
||||||
DOCKERFILE
|
DOCKERFILE
|
||||||
|
|
||||||
docker build -t qbix-server-builder "$TMPDIR"
|
docker build -t qbixserver-builder "$TMPDIR"
|
||||||
docker create --name qbix-extract qbix-server-builder
|
docker create --name qbix-extract qbixserver-builder
|
||||||
docker cp qbix-extract:/qbix-server "$BIN_DIR/qbix-server"
|
docker cp qbix-extract:/qbixserver "$BIN_DIR/qbixserver"
|
||||||
docker rm qbix-extract
|
docker rm qbix-extract
|
||||||
docker rmi qbix-server-builder 2>/dev/null || true
|
docker rmi qbixserver-builder 2>/dev/null || true
|
||||||
|
|
||||||
rm -rf "$TMPDIR"
|
rm -rf "$TMPDIR"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Binary built: $BIN_DIR/qbix-server"
|
echo "Binary built: $BIN_DIR/qbixserver"
|
||||||
ls -lh "$BIN_DIR/qbix-server"
|
ls -lh "$BIN_DIR/qbixserver"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Method 3: Manual build (fallback) ────────────
|
# ── 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 A: curl -sL https://github.com/crazywhalecc/static-php-cli/... | tar xz"
|
||||||
echo " Method B: $0 --docker"
|
echo " Method B: $0 --docker"
|
||||||
echo ""
|
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 ──────────────────────────
|
# ── Choose build method ──────────────────────────
|
||||||
|
|||||||
+13
-13
@@ -1,13 +1,13 @@
|
|||||||
#!/usr/bin/env php
|
#!/usr/bin/env php
|
||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Build qbix-server.phar — single-file distributable.
|
* Build qbixserver.phar — single-file distributable.
|
||||||
*
|
*
|
||||||
* Usage: php build-phar.php
|
* Usage: php build-phar.php
|
||||||
* Output: bin/qbix-server.phar
|
* Output: bin/qbixserver.phar
|
||||||
*
|
*
|
||||||
* The PHAR includes all server classes and the minimal Q shim.
|
* The PHAR includes all server classes and the minimal Q shim.
|
||||||
* Run with: php bin/qbix-server.phar --root=./web --port=8080
|
* Run with: php bin/qbixserver.phar --root=./web --port=8080
|
||||||
*
|
*
|
||||||
* Requires: phar.readonly=0 in php.ini (or pass -d phar.readonly=0)
|
* Requires: phar.readonly=0 in php.ini (or pass -d phar.readonly=0)
|
||||||
*/
|
*/
|
||||||
@@ -18,16 +18,16 @@ if (ini_get('phar.readonly')) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$pharFile = __DIR__ . '/bin/qbix-server.phar';
|
$pharFile = __DIR__ . '/bin/qbixserver.phar';
|
||||||
if (file_exists($pharFile)) {
|
if (file_exists($pharFile)) {
|
||||||
unlink($pharFile);
|
unlink($pharFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mkdir(__DIR__ . '/bin', 0755, true);
|
@mkdir(__DIR__ . '/bin', 0755, true);
|
||||||
|
|
||||||
echo "Building qbix-server.phar...\n";
|
echo "Building qbixserver.phar...\n";
|
||||||
|
|
||||||
$phar = new Phar($pharFile, 0, 'qbix-server.phar');
|
$phar = new Phar($pharFile, 0, 'qbixserver.phar');
|
||||||
$phar->startBuffering();
|
$phar->startBuffering();
|
||||||
|
|
||||||
// Add source files
|
// Add source files
|
||||||
@@ -43,12 +43,12 @@ $phar->buildFromIterator(
|
|||||||
$stub = <<<'STUB'
|
$stub = <<<'STUB'
|
||||||
#!/usr/bin/env php
|
#!/usr/bin/env php
|
||||||
<?php
|
<?php
|
||||||
Phar::mapPhar('qbix-server.phar');
|
Phar::mapPhar('qbixserver.phar');
|
||||||
|
|
||||||
// Bootstrap
|
// Bootstrap
|
||||||
require 'phar://qbix-server.phar/Q.php';
|
require 'phar://qbixserver.phar/Q.php';
|
||||||
|
|
||||||
// Parse args the same way as server.php
|
// Parse args the same way as qbixserver.php
|
||||||
$opts = array(
|
$opts = array(
|
||||||
'root' => null,
|
'root' => null,
|
||||||
'app' => null,
|
'app' => null,
|
||||||
@@ -66,7 +66,7 @@ foreach ($argv as $i => $arg) {
|
|||||||
if ($i === 0) continue;
|
if ($i === 0) continue;
|
||||||
if ($arg === '--help' || $arg === '-h') {
|
if ($arg === '--help' || $arg === '-h') {
|
||||||
echo "Qbix Server v" . QBIX_SERVER_VERSION . " (PHAR)\n\n";
|
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 " --root=DIR Document root (default: ./web)\n";
|
||||||
echo " --app=DIR Qbix app directory\n";
|
echo " --app=DIR Qbix app directory\n";
|
||||||
echo " --host=IP Bind address (default: 0.0.0.0)\n";
|
echo " --host=IP Bind address (default: 0.0.0.0)\n";
|
||||||
@@ -127,6 +127,6 @@ $phar->stopBuffering();
|
|||||||
chmod($pharFile, 0755);
|
chmod($pharFile, 0755);
|
||||||
|
|
||||||
$size = filesize($pharFile);
|
$size = filesize($pharFile);
|
||||||
echo "Built: bin/qbix-server.phar (" . round($size / 1024) . " KB)\n";
|
echo "Built: bin/qbixserver.phar (" . round($size / 1024) . " KB)\n";
|
||||||
echo "Run: php bin/qbix-server.phar --root=./web --port=8080\n";
|
echo "Run: php bin/qbixserver.phar --root=./web --port=8080\n";
|
||||||
echo "Or: chmod +x bin/qbix-server.phar && ./bin/qbix-server.phar --port=8080\n";
|
echo "Or: chmod +x bin/qbixserver.phar && ./bin/qbixserver.phar --port=8080\n";
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
}
|
||||||
Binary file not shown.
+17
-15
@@ -3,8 +3,8 @@
|
|||||||
/**
|
/**
|
||||||
* Qbix Server — pure PHP web server.
|
* Qbix Server — pure PHP web server.
|
||||||
*
|
*
|
||||||
* Standalone: php server.php --root=./web --port=8080
|
* Standalone: php qbixserver.php --root=./web --port=8080
|
||||||
* With Qbix: php server.php --app=/path/to/myapp --port=8080
|
* With Qbix: php qbixserver.php --app=/path/to/myapp --port=8080
|
||||||
*
|
*
|
||||||
* Options:
|
* Options:
|
||||||
* --root=DIR Document root (default: ./web)
|
* --root=DIR Document root (default: ./web)
|
||||||
@@ -38,7 +38,7 @@ foreach ($argv as $i => $arg) {
|
|||||||
if ($i === 0) continue;
|
if ($i === 0) continue;
|
||||||
if ($arg === '--help' || $arg === '-h') {
|
if ($arg === '--help' || $arg === '-h') {
|
||||||
echo "Qbix Server v" . QBIX_SERVER_VERSION . "\n\n";
|
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 "Options:\n";
|
||||||
echo " --root=DIR Document root (default: ./web)\n";
|
echo " --root=DIR Document root (default: ./web)\n";
|
||||||
echo " --app=DIR Qbix app directory (uses full Q framework)\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 ────────────────────────────────────
|
// ── Start server ────────────────────────────────────
|
||||||
|
|
||||||
|
$W = 38; // inner width of the box
|
||||||
|
|
||||||
echo "\n";
|
echo "\n";
|
||||||
echo " ┌──────────────────────────────────────┐\n";
|
echo " ┌" . str_repeat('─', $W) . "┐\n";
|
||||||
echo " │ Qbix Server v" . QBIX_SERVER_VERSION . str_repeat(' ', 22 - strlen(QBIX_SERVER_VERSION)) . "│\n";
|
echo " │" . str_pad(" Qbix Server v" . QBIX_SERVER_VERSION, $W) . "│\n";
|
||||||
echo " ├──────────────────────────────────────┤\n";
|
echo " ├" . str_repeat('─', $W) . "┤\n";
|
||||||
echo " │ http://{$opts['host']}:{$opts['port']}" . str_repeat(' ', max(0, 24 - strlen("{$opts['host']}:{$opts['port']}"))) . "│\n";
|
echo " │" . str_pad(" http://{$opts['host']}:{$opts['port']}", $W) . "│\n";
|
||||||
echo " │ Root: " . basename($webDir) . str_repeat(' ', max(0, 29 - strlen(basename($webDir)))) . "│\n";
|
echo " │" . str_pad(" Root: " . basename($webDir), $W) . "│\n";
|
||||||
echo " │ Mode: " . ($qbixMode ? 'Qbix Platform' : 'Standalone') . str_repeat(' ', $qbixMode ? 16 : 19) . "│\n";
|
echo " │" . str_pad(" Mode: " . ($qbixMode ? 'Qbix Platform' : 'Standalone'), $W) . "│\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 " │" . str_pad(" PHP: " . ($opts['workers'] ? $opts['workers'] . ' workers' : 'in-process'), $W) . "│\n";
|
||||||
echo " ├──────────────────────────────────────┤\n";
|
echo " ├" . str_repeat('─', $W) . "┤\n";
|
||||||
echo " │ Dashboard: /Q/dashboard │\n";
|
echo " │" . str_pad(" Dashboard: /Q/dashboard", $W) . "│\n";
|
||||||
echo " │ Health: /Q/health │\n";
|
echo " │" . str_pad(" Health: /Q/health", $W) . "│\n";
|
||||||
echo " │ Ctrl+C to stop │\n";
|
echo " │" . str_pad(" Ctrl+C to stop", $W) . "│\n";
|
||||||
echo " └──────────────────────────────────────┘\n";
|
echo " └" . str_repeat('─', $W) . "┘\n";
|
||||||
echo "\n";
|
echo "\n";
|
||||||
|
|
||||||
Q_WebServer::start(
|
Q_WebServer::start(
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
+76
-19
@@ -1,30 +1,87 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* @module Q
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Revolt\EventLoop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event loop driver backed by Revolt (amphp's event loop).
|
||||||
|
* Install via: composer require revolt/event-loop
|
||||||
|
*
|
||||||
|
* @class Q_Evented_Revolt
|
||||||
|
* @extends Q_Evented_Driver
|
||||||
|
*/
|
||||||
class Q_Evented_Revolt extends Q_Evented_Driver
|
class Q_Evented_Revolt extends Q_Evented_Driver
|
||||||
{
|
{
|
||||||
protected $running = false;
|
protected $running = false;
|
||||||
function onReadable($s, callable $cb) {
|
|
||||||
return \Revolt\EventLoop::onReadable($s, function($id,$s)use($cb){ $cb($s); });
|
function onReadable($stream, callable $cb)
|
||||||
|
{
|
||||||
|
return EventLoop::onReadable($stream, function ($id, $s) use ($cb) {
|
||||||
|
$cb($s);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function onWritable($s, callable $cb) {
|
|
||||||
return \Revolt\EventLoop::onWritable($s, function($id,$s)use($cb){ $cb($s); });
|
function onWritable($stream, callable $cb)
|
||||||
|
{
|
||||||
|
return EventLoop::onWritable($stream, function ($id, $s) use ($cb) {
|
||||||
|
$cb($s);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function delay($sec, callable $cb) {
|
|
||||||
return \Revolt\EventLoop::delay($sec, function()use($cb){ $cb(); });
|
function delay($seconds, callable $cb)
|
||||||
|
{
|
||||||
|
return EventLoop::delay($seconds, function () use ($cb) {
|
||||||
|
$cb();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function repeat($sec, callable $cb) {
|
|
||||||
return \Revolt\EventLoop::repeat($sec, function()use($cb){ $cb(); });
|
function repeat($seconds, callable $cb)
|
||||||
|
{
|
||||||
|
return EventLoop::repeat($seconds, function () use ($cb) {
|
||||||
|
$cb();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function defer(callable $cb) {
|
|
||||||
return \Revolt\EventLoop::defer(function()use($cb){ $cb(); });
|
function defer(callable $cb)
|
||||||
|
{
|
||||||
|
return EventLoop::defer(function () use ($cb) {
|
||||||
|
$cb();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function onSignal($sig, callable $cb) {
|
|
||||||
return \Revolt\EventLoop::onSignal($sig, function($id,$s)use($cb){ $cb($s); });
|
function onSignal($signal, callable $cb)
|
||||||
|
{
|
||||||
|
return EventLoop::onSignal($signal, function ($id, $s) use ($cb) {
|
||||||
|
$cb($s);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel($id) { EventLoop::cancel($id); }
|
||||||
|
function disable($id) { EventLoop::disable($id); }
|
||||||
|
function enable($id) { EventLoop::enable($id); }
|
||||||
|
|
||||||
|
function run()
|
||||||
|
{
|
||||||
|
$this->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; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user