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 Watcher Laravel Package

huang-yi/swoole-watcher

A Laravel package that watches application files and automatically reloads the Swoole server when changes are detected, making local development with Swoole smoother and more efficient.

Deep Wiki
Context7

Getting Started

Before touching the package, install its system dependency — fswatch is not a PHP library and won't be pulled in by Composer:

# macOS
brew install fswatch
# Debian/Ubuntu
apt-get install fswatch

Then require the package as a dev dependency (it belongs in require-dev, never require):

composer require --dev huang-yi/swoole-watcher

You also need the Swoole extension enabled (php --ri swoole should report a 4.x/5.x build).

Look first at vendor/huang-yi/swoole-watcher/src/ to confirm the current constructor/static API — the public surface changed across the short release history and the README is thin. The typical shape is instantiate-or-call with a list of paths plus a callback that fires on filesystem events, then start the underlying fswatch process from a Swoole child process.

The first real use case is hot-reloading a Swoole HTTP/TCP server in development so you don't manually Ctrl-C after every edit:

<?php
require __DIR__ . '/vendor/autoload.php';

use HuangYi\Watcher\Watcher;

$server = new Swoole\Http\Server('127.0.0.1', 9501);
$server->on('request', fn ($req, $res) => $res->end('ok'));

// watcher runs in its own context; reload workers on change
$process = new Swoole\Process(function () {
    Watcher::watch([__DIR__ . '/app', __DIR__ . '/config'], function () use ($server) {
        $server->reload();
    });
});
$process->start();

$server->start();

Run that file with php dev-server.php, edit a file under app/, and the workers cycle automatically. Treat this as a scaffolding example — verify the exact argument order in the source before copying.

Implementation Patterns

Gate it behind an environment check. The single most important pattern is refusing to start the watcher unless you're in a dev context:

if (getenv('APP_ENV') !== 'local') {
    return;
}

Ship this check inside whatever bootstrap/console command you wrap the watcher in, so CI and production images can include the package without risk of a stray watcher process.

Wrap it as an artisan/console command. Rather than a loose script, expose php bin/console dev:watch that (a) starts the Swoole server in one process, (b) starts the watcher in a sibling process, and (c) coordinates shutdown. This gives you one thing to run locally and a natural place to log events.

Debounce aggressively. Editors (and git operations) emit bursts of fswatch events — a single save can produce 3–10 lines. Collect events into a short buffer and only trigger one reload every 200–500 ms. Doing the debounce in PHP avoids fighting fswatch's -l/--latency flag semantics across versions:

$pending = false;
$timer = Swoole\Timer::tick(300, function () use (&$pending, $server) {
    if ($pending) {
        $pending = false;
        $server->reload();
    }
});

Separate "reload" from "restart". $server->reload() recycles workers — perfect for controllers, services, and route files. It does not pick up changes to bootstrap files, composer.json, DI container configuration, or anything loaded by the master process. For those, either trigger a full process restart or accept that the developer must restart manually. Map event paths to the right action:

if (str_starts_with($path, __DIR__ . '/config/')) {
    // restart required
} else {
    $server->reload();
}

Filter noise at the watcher, not downstream. Pass exclusion patterns or prune the path list so vendor/, runtime/, storage/logs/, .git/, and IDE swap files (.swp, ~, .tmp) never cause reloads. Watching vendor/ alone can make reload loops fire continuously during composer install.

Reuse one child process per server. Start the watcher once via Swoole\Process with $process->start(), and register a signal/reap handler so the child is killed when the parent exits. Otherwise an orphaned fswatch process lingers and keeps firing against a dead server.

Pair with a framework watcher when one exists. If you're on Swoft or Hyperf, those ecosystems ship their own reload tooling; use this package only when you need a standalone, dependency-light watcher outside those frameworks, or when you want to script custom reload semantics (e.g., only reload on PHP changes but restart on config changes).

Gotchas and Tips

fswatch must be on the PATH of the PHP process, not your shell. This is the most common failure: it works in your terminal but fails under a daemon, container, or IDE run configuration. Resolve the binary explicitly or set the path in config, and fail with a clear error message if exec('which fswatch') returns empty.

No Windows support. Both fswatch and the underlying inotify/FSEvents mechanisms are Unix-only. On Windows developers you'll need a different strategy (polling, or WSL). Document this up front or your team will file bugs.

Hitting the inotify watch limit on Linux. Large repos (especially with node_modules or a fat vendor/) exhaust fs.inotify.max_user_watches and fswatch silently stops receiving events. Raise it via sysctl fs.inotify.max_user_watches=524288, and watch for a watcher that "runs but never reloads" — it's usually this, not a code bug.

fswatch flag drift between versions. Recursive (-r), exclude (-e), batch/latency (-l), and format (--format) flags behave differently across 1.x and 1.14+. If you're building on top of the package rather than using it as-is, pin or probe the fswatch --version output and branch accordingly. Debug by dumping the raw command the package constructs (temporarily echo it before exec/Process).

Release age and Swoole compatibility. Last tagged release is April 2020, which predates Swoole 5. If you're on Swoole 5, run the watcher in isolation first before wiring it to a real server — API changes around Process, Timer, and coroutine contexts can surface as silent exits. Pin the package version in composer.json so a future (unlikely) release doesn't surprise you.

Signals don't always do what you expect. $server->reload() only reloads workers; the master keeps running with the old bootstrap. On some Swoole builds, SIGUSR1 reload and a hard restart have different startup cost — for a slow app, prefer reload for code and reserve restarts for config.

Watch event ordering isn't guaranteed. You may see Updated before Created for new files, or missed events during rapid git checkout. Don't build correctness-critical logic on event types; treat "something changed under these paths" as the contract.

Extension points. The cleanest customization is usually to subclass the watcher (or wrap it) and override the command-building method so you can inject extra fswatch flags and paths, rather than editing vendor code. If the class is final or methods are private, copy the ~100 lines into your own DevWatcher class — it's small enough, and it frees you from the 2020 dependency.

Debugging checklist when reloads stop working: (1) is fswatch running (ps aux | grep fswatch)? (2) does the watcher see events if you touch a watched file? (3) has the inotify limit been hit? (4) is your exclusion list accidentally excluding the whole tree? (5) is the reload callback actually invoking $server->reload() on a server instance in the same process? Each of these fails independently, so test them in that order.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
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
cgv/localized-resource