Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Swoole Launchpad Laravel Package

upscale/swoole-launchpad

Swoole Launchpad integrates the Swoole PHP extension with Laravel, giving applications a high-performance, coroutine-based server runtime. It provides the glue and Artisan tooling needed to boot Laravel on Swoole with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Verify the package before you commit to it. With 1 star and 0 dependents, treat this as "read the source first, not the docs." Clone the repo and check:

git clone https://github.com/upscalesoftware/swoole-launchpad /tmp/slp
ls /tmp/slp/src /tmp/slp/config
cat /tmp/slp/composer.json   # php + ext-swoole/openswoole constraints

Look specifically for: the service provider class, any publishable config file, the console command(s) it registers, and which Swoole callbacks it wires up (onStart, onWorkerStart, onRequest, onTask, onWorkerStop). That list is the API surface — everything else is convention.

2. Install and confirm the extension. Swoole/OpenSwoole is a PECL extension, not a Composer package, so a clean composer require upscale/swoole-launchpad proves nothing until you can boot a worker:

pecl install swoole          # or: pecl install openswoole
php -m | grep -i swoole
php -r "var_dump(SWOOLE_VERSION);"
composer require upscale/swoole-launchpad

If the package's composer.json pins ext-swoole but your host ships OpenSwoole (or vice versa), you'll get a resolution failure that has nothing to do with your code. Decide early which fork you're on and stick to it.

3. Publish config and boot something trivial. The first real use case is almost always: run an HTTP server from an Artisan command instead of php-fpm, usually behind nginx as a reverse proxy.

php artisan vendor:publish --provider="Upscale\SwooleLaunchpad\...ServiceProvider" --tag=config
php artisan swoole:launch        # name will differ — take it from the command's $signature

Then point nginx at the port and confirm you get a Laravel response through the Swoole worker, not through php-fpm:

location / {
    proxy_pass http://127.0.0.1:9501;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Upgrade $http_upgrade;      # only if you enable WebSocket
    proxy_set_header Connection "upgrade";
}

4. Where to look when it doesn't work. In order: (a) the config file you just published, (b) the command's handle() to see what it actually starts, (c) config/logging.php — Swoole workers write to stderr/stdout, and php-fpm-era log paths often swallow them. Run the command in the foreground with the logger set to stderr before you ever touch Supervisor.

Implementation Patterns

Bootstrap Laravel once per worker, not once per request. This is the core mental shift. The framework container is created in onWorkerStart and reused for every request the worker handles. Whatever pattern the package gives you for that boot step (a callback, a service provider, an abstract "server" class) is the single most important thing to get right. A typical shape:

'onWorkerStart' => function ($server, $workerId) {
    // boot app, warm container, resolve singletons you want shared
    // DO NOT resolve anything request-scoped here
}

Define servers declaratively and keep them in config. If the package supports multiple server definitions (HTTP, WebSocket, TCP), keep each in the config file keyed by name so you can boot one with --server=ws in dev and all of them in production. Hard-coded ports in handle() are the thing you'll regret.

Separate HTTP workers from task workers. Swoole's task_worker_num gives you a second process pool for blocking work. Two viable patterns:

  • Swoole tasks — dispatch via $server->task($payload), handle in onTask, return via finish(). Low latency, but the payload must be serializable and the pool size is fixed at boot.
  • Queue bridge — HTTP worker pushes a Laravel job to Redis/SQS and returns immediately. Better durability, better tooling, worse latency.

Use tasks for short fire-and-forget work (email send, log flush); use the queue for anything that needs retries or visibility.

Wire graceful shutdown and reload properly. Whatever command starts the server should also stop it cleanly:

php artisan swoole:launch            # foreground, for dev
kill -TERM <master-pid>              # graceful stop, drains in-flight requests
kill -USR1 <master-pid>              # typically reload workers (verify in source)
kill -USR2 <master-pid>              # typically reload task workers

Under Supervisor, use stopsignal=TERM and stopwaitsecs long enough for a max request to finish. Under Docker/Kubernetes use exec-form CMD ["php","artisan","swoole:launch"] so the master is PID 1 and receives signals directly — a shell wrapper will eat your SIGTERM and you'll get hard kills on every deploy.

Treat the worker as long-lived and the request as disposable. The pattern that survives production:

// per-request: reset anything cached on the container
app()->forgetInstance(SomeRequestScopedThing::class);
// per-request: clear the auth guard, the request, the session

If the package exposes a request lifecycle hook (a middleware, a onRequest wrapper), put your resets there and nowhere else. Consistency beats cleverness here.

Health checks and readiness. Expose /health from inside the Swoole server that returns 200 only after onWorkerStart completed successfully. Your load balancer should hit that, not /. Bonus: report $server->stats() (worker count, active connections, queued tasks) on an internal-only endpoint.

Smoke-test the server in CI. Start it on a free port, curl two endpoints, assert both, kill it. Long-lived-process code breaks in ways unit tests never see:

php artisan swoole:launch --port=9599 & SRV=$!
sleep 2
curl -fsS localhost:9599/health
kill -TERM $SRV && wait $SRV

Gotchas and Tips

The bus factor is the headline risk. One star, zero dependents means: no Stack Overflow answers, no third-party blog posts, no upstream patches for your PHP version. Pin the exact version in composer.json, commit the lockfile, and budget a day for reading every file in src/ before you put it in front of traffic. If it's fewer than ~1,000 lines, vendor it into app/Support/ and own it — you'll be maintaining it either way, and at least then you can patch it.

State leaks between requests are the #1 production bug in every Swoole-Laravel bridge. Symptoms: user A sees user B's data, config changes persist, request() returns the previous request. Audit for:

  • Singletons holding request data (repositories, "current user" services)
  • Static properties and class-level caches
  • config()->set() at runtime — mutate once at boot, never per request
  • Auth::user() cached on the guard
  • Event listeners, middleware, or routes registered inside a request handler

Grep your app for static $, app()->instance(, and config([ before you deploy.

Blocking I/O silently destroys your concurrency gains. By default, coroutine hooks may be off. If curl_exec, PDO::query, file_get_contents, or sleep() run un-hooked, they block the entire worker and you've built a slower php-fpm. Enable runtime hooks in the server config:

'hook_flags' => SWOOLE_HOOK_ALL,   // or at minimum SWOOLE_HOOK_TCP | SWOOLE_HOOK_FILE

Then verify: Swoole\Runtime::getHookFlags(). And for anything not hookable, use Swoole-native clients (Swoole\Coroutine\Http\Client, Coroutine\Redis) or a pooled connection.

Database connections go stale. MySQL drops idle connections after wait_timeout; a worker idle overnight wakes up with a dead PDO and throws MySQL server has gone away. Mitigations: set DB::reconnect on failure, keep the pool small (workers × pool_size < max_connections), or use a coroutine-aware pool. Same story for Redis if you're wrapping Predis\Client in a singleton.

Opcache and preloading will lie to you in development. With opcache.validate_timestamps=0 or opcache.preload, editing a file does nothing until you restart workers. Set validate_timestamps=1 and revalidate_freq=0 in dev, and add a --watch/auto-reload path if the package offers one. In production, the opposite: validate_timestamps=0 plus a full restart on deploy, because a SIGUSR1 worker reload picks up new code but not new preloaded symbols.

Memory grows monotonically if you let it. Long-lived workers accumulate: log context, event dispatcher entries, Doctrine/Clock caches, anything appended to an array. Run with max_request set (Swoole recycles workers after N requests) as a safety net, and monitor RSS per worker via $server->stats() or ps -o rss on child PIDs. A slow leak shows up as RSS climbing over 24h with flat traffic.

Signal handling under orchestration is the deployment landmine. In Docker: exec form, no shell, stop_grace_period > your slowest request, and STOPSIGNAL SIGTERM. In Kubernetes, add a preStop sleep so the readiness probe fails before the process starts draining, otherwise you'll serve 502s on every rollout.

Debugging technique that actually works. When a request hangs, find the worker PID ($server->stats() or ps --ppid <master>), then strace -p <pid> — you'll immediately see whether it's blocked on a socket, a lock, or a busy loop. Enable log_level => SWOOLE_LOG_DEBUG only while diagnosing; at debug level the log volume alone will impact throughput. If the package has its own logger, prefer stderr over a file so docker logs / kubectl logs just works.

Extension points to look for in src/. Even at this size, the useful hooks are usually: (1) a config-driven callback map for raw Swoole events, (2) a container binding for the server instance, (3) an overridable base class for your own server types. If those three exist, you can extend without forking. If the command is a single 400-line handle() with everything inline, fork it now.

Have an exit plan. Keep the Swoole server behind the same nginx proxy_pass that could point at php-fpm. If the package dies on a PHP upgrade, flipping the upstream back is a one-line change instead of an incident. Given the dependency profile, that's not pessimism — it's the correct reading of "0 dependents."

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource