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:
@@ -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).
|
||||
Part of the [Qbix Platform](https://github.com/Qbix/Platform).
|
||||
|
||||
+15
-15
@@ -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 ──────────────────────────
|
||||
|
||||
+13
-13
@@ -1,13 +1,13 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* Build qbix-server.phar — single-file distributable.
|
||||
* Build qbixserver.phar — single-file distributable.
|
||||
*
|
||||
* 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.
|
||||
* 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)
|
||||
*/
|
||||
@@ -18,16 +18,16 @@ if (ini_get('phar.readonly')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$pharFile = __DIR__ . '/bin/qbix-server.phar';
|
||||
$pharFile = __DIR__ . '/bin/qbixserver.phar';
|
||||
if (file_exists($pharFile)) {
|
||||
unlink($pharFile);
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
// Add source files
|
||||
@@ -43,12 +43,12 @@ $phar->buildFromIterator(
|
||||
$stub = <<<'STUB'
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
Phar::mapPhar('qbix-server.phar');
|
||||
Phar::mapPhar('qbixserver.phar');
|
||||
|
||||
// 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(
|
||||
'root' => 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";
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* 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(
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+76
-19
@@ -1,30 +1,87 @@
|
||||
<?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
|
||||
{
|
||||
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