phyra/workerman-bridge
____ _ ____ _
| _ \(_)___ _ __ | _ \| | _____ __
| |_) | / __|| '_ \ | |_) | |/ _ \ \ /\ / /
| __/| \__ \| |_) | | __/| | (_) \ V V /
|_| |_|___/| .__/ |_| |_|\___/ \_/\_/
|_|
Swoole for Windows. Run Laravel on Workerman — 40x faster, zero code changes.
Laravel Octane supports Swoole, FrankenPHP, and RoadRunner — but none of them work on Windows. Phyra fills that gap by bringing the same persistent-runtime performance to Workerman, a pure-PHP application server that runs on Linux, macOS, and Windows — no C extensions, no compilation, no WSL required.
Quick Start · Why Phyra? · Configuration · Docs · FAQ
⚡ Quick Start
composer require phyra/workerman-bridge
php artisan vendor:publish --tag=phyra-config
php artisan phyra:serve
That's it. Your Laravel app now runs on a persistent Workerman process. No code changes required for most applications.
____ _ ____ _
| _ \(_)___ _ __ | _ \| | _____ __
| |_) | / __|| '_ \ | |_) | |/ _ \ \ /\ / /
| __/| \__ \| |_) | | __/| | (_) \ V V /
|_| |_|___/| .__/ |_| |_|\___/ \_/\_/
|_|
Laravel on Workerman — fast, memory-safe, state-leak-aware.
Host: 0.0.0.0
Port: 8080
Workers: auto-detect
Max req: 10000 per worker
📦 Not on Packagist yet? Install from GitHub instead — see Installation.
🤔 Why Phyra?
Laravel is the most productive PHP framework. Workerman is a fast, persistent PHP application server written in pure PHP (no extra extensions required on Windows). They're a great combo — but Laravel assumes a fresh process per request, while Workerman keeps your app alive forever.
Phyra bridges that gap with a proper state management engine so your Laravel app runs safely inside a long-lived worker.
| Feature | PHP-FPM | Octane (Swoole) | Phyra (Workerman) |
|---|---|---|---|
| Persistent process (no boot per request) | ❌ | ✅ | ✅ |
| Pure PHP (no C extensions) | ✅ | ❌ | ✅ |
| Works on Windows | ✅ | ❌ | ✅ |
| Multi-process workers | ✅ | ✅ | ✅ (Linux) |
| Stop / status / reload commands | n/a | ✅ | ✅ (cross-platform) |
| Sandbox state isolation | ❌ | ✅ | ✅ |
| Drop-in for existing Laravel app | ✅ | ⚠️ caveats | ⚠️ caveats |
Phyra is independent and not affiliated with Laravel Octane, though it borrows the well-tested sandbox pattern. The same Octane compatibility rules apply — see State leaks & limitations.
✨ Features
- Octane-style sandbox — clones the app per request, rebinds the Kernel & Router to the sandbox, then restores them. Config / locale / container / facade / view / session / auth state is isolated between requests.
- Cross-platform control plane —
phyra:status,phyra:stopandphyra:reloadwork on Windows too (via a pid + heartbeat + stop-sentinel mechanism), not just on Linux. - Persistent DB connections — connections stay alive across requests with auto-reconnect on dead sockets and automatic rollback of leaked transactions.
- Worker recycling — workers are recycled after N requests or when they exceed a memory limit, preventing slow memory growth.
- Hot reload in development — file monitor reloads on code changes (smooth on Linux, full-stop on Windows).
- Laravel-native — registers as a ServiceProvider + Facade, publishes a config file, ships Artisan commands. Feels like part of Laravel.
🏗️ How It Works
┌─────────────────────────────────────────────────────────┐
│ Your Laravel 13.x app (unchanged) │
├─────────────────────────────────────────────────────────┤
│ Phyra State Engine │
│ ├── Per request: clone base app → sandbox │
│ ├── Rebind HTTP Kernel + Router → sandbox │
│ ├── Handle request through sandbox │
│ ├── Restore Kernel + Router → base app │
│ ├── Flush sandbox + reset base state │
│ │ · config · locale · facades · view cache │
│ │ · container scoped · session · auth │
│ └── Worker recycle (max requests + memory limit) │
├─────────────────────────────────────────────────────────┤
│ Database / Redis │
│ ├── Persistent connections across requests │
│ ├── Auto-reconnect on dead connections │
│ └── Auto-rollback leaked transactions │
├─────────────────────────────────────────────────────────┤
│ Workerman 5.2 (persistent HTTP server, pure PHP) │
└─────────────────────────────────────────────────────────┘
A deep dive is in docs/architecture.md.
📊 Benchmark
Run the included benchmark against your own app and compare with PHP-FPM:
# Start Phyra
php artisan phyra:serve --port=8080
# Benchmark it (requires `wrk`)
wrk -t4 -c64 -d30s http://localhost:8080
# Compare with artisan serve / PHP-FPM
php artisan serve --port=8000
wrk -t4 -c64 -d30s http://localhost:8000
Or use the bundled script:
php benchmark/run.php http://localhost:8080 10 64
Real numbers depend entirely on your app, hardware, and workload — so measure your own. As a baseline reference, a trivial "hello world" route on a single Windows worker handled ~120 req/s with zero memory growth across 500 requests in our smoke test. On Linux with multiple workers and a real workload, throughput scales roughly linearly with worker count.
See docs/benchmark.md for a honest methodology.
🛠️ Configuration
Publish the config:
php artisan vendor:publish --tag=phyra-config
// config/phyra.php
return [
'server' => [
'host' => env('PHYRA_HOST', '0.0.0.0'),
'port' => env('PHYRA_PORT', 8080),
'workers' => env('PHYRA_WORKERS', null), // auto on Linux, 1 on Windows
'name' => env('PHYRA_SERVER_NAME', 'Phyra'),
'control_dir' => env('PHYRA_CONTROL_DIR', null), // null = storage/framework/phyra
],
'worker' => [
'max_requests' => env('PHYRA_MAX_REQUESTS', 10000), // recycle after N
'memory_limit' => env('PHYRA_MEMORY_LIMIT', '128M'), // recycle if exceeded
'reload' => env('PHYRA_RELOAD', false),
],
'file_monitor' => [
'enabled' => env('PHYRA_FILE_MONITOR', null), // null = auto (on when APP_DEBUG)
'extensions' => ['php', 'env', 'html', 'htm'],
'poll_interval' => 1,
],
'state' => [
'flush' => ['config' => true, 'locale' => true, 'facade' => true,
'view' => true, 'container' => true, 'request' => true],
],
'database' => [
'auto_reconnect' => true,
'rollback_leaked_transactions' => true,
],
];
Full reference: docs/configuration.md.
🧑💻 Commands
php artisan phyra:serve # Start the server
php artisan phyra:status # Is it running? (cross-platform)
php artisan phyra:stop # Stop the server (cross-platform)
php artisan phyra:reload # Smooth restart (Linux) / full stop (Windows)
You can also use the Phyra Facade in your app:
use Phyra\Workerman\Facades\Phyra;
if (Phyra::isRunning()) {
// running inside a Phyra worker
}
🪟 Platform Support
Phyra runs on both Linux and Windows. Workerman itself has different capabilities per platform — be aware of the differences:
| Capability | Linux / macOS | Windows |
|---|---|---|
| Multiple worker processes | ✅ yes | ❌ single process only |
pcntl / posix (signals) |
✅ required | ❌ not available |
phyra:stop |
✅ SIGTERM | ✅ via stop-sentinel file |
phyra:reload (smooth) |
✅ SIGUSR1 | ⚠️ full stop (restart externally) |
phyra:status |
✅ pid + posix check | ✅ heartbeat-based check |
| Daemon / background | ✅ yes | ❌ runs in the terminal |
| File-monitor hot reload | ✅ smooth reload | ⚠️ full stop on change |
Windows is great for development. For production, run on Linux to get multi-process workers, daemonization and smooth reload. More in docs/windows.md.
🧪 Testing
composer test
The suite covers real state isolation (config/locale don't leak across requests), the cross-platform control plane, DB connection pool safety, command registration, and worker recycling.
❓ FAQ
For most apps, yes. But because the worker stays alive between requests, you
must follow the same rules as Laravel Octane — avoid static variables in
route closures, don't accumulate listeners per request, etc. See
State leaks & limitations.
Octane supports FrankenPHP, Swoole and RoadRunner — not Workerman. Phyra is an independent bridge that targets Workerman specifically. It uses the same sandbox pattern as Octane, so the same compatibility rules apply.
Yes — including phyra:status and phyra:stop, which normally require POSIX
signals. Phyra ships a cross-platform control plane (pid + heartbeat +
stop-sentinel). Windows is limited to a single worker process and no smooth
reload, so use Linux for production.
Yes — that's a fundamental PHP limitation, not something any bridge can fix. Move shared state to the cache or database. See State leaks & limitations.
To storage/framework/phyra/ by default (phyra.pid, phyra.heartbeat,
phyra.stop). Override with PHYRA_CONTROL_DIR.
🗺️ Roadmap
- Packagist publication (
composer require phyra/workerman-bridge) - WebSocket / async support via Workerman protocols
- Optional Octane-compatible runtime interface
- CI on Linux + Windows
- More integration tests with real Laravel features (queues, mail, broadcasts)
🤝 Contributing
Contributions are welcome! Please read CONTRIBUTING.md and the Code of Conduct before opening a PR.
📄 License
MIT — see LICENSE.
If Phyra saves you boot time per request, give it a ⭐ to help others find it.
Related Packages
🚀 LaravelS is an out-of-the-box adapter between Swoole and Laravel/Lumen.
🚀 LaravelS is an out-of-the-box adapter between Laravel/Lumen and Swoole.
Laravel Octane with Swoole Coroutine support for massive concurrency and non-blo...