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

Watcher Laravel Package

hyperf/watcher

Hyperf Watcher monitors your application files and automatically restarts the Hyperf server when changes are detected. It simplifies local development by eliminating manual restarts, with configurable watch paths and ignore rules for a smoother, faster coding workflow.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a development-only companion: it watches your source tree, kills the running Hyperf server, and boots a fresh one — so you stop typing Ctrl+C + php bin/hyperf.php start on every edit.

Minimal setup:

composer require --dev hyperf/watcher
php bin/hyperf.php vendor:publish hyperf/watcher

This drops config/autoload/watcher.php. Open it first — it is the only file you will normally touch:

return [
    'driver' => Hyperf\Watcher\Driver\ScanFileDriver::class,
    'bin'    => 'php',
    'watch'  => [
        'dir'     => ['app', 'config'],
        'file'    => ['.env'],
        'exclude' => ['runtime', 'vendor', 'storage'],
    ],
];

Then replace your normal start command with the watch command:

php bin/hyperf.php server:watch

First use case to validate the setup: add a route in config/routes.php, hit it with curl, change the response string in the controller, and watch the console print a reload followed by a new request serving the new value. If that loop works, everything else is tuning.

Prerequisites: a working Hyperf skeleton, the Swoole (or Swow) extension loaded, and a PHP CLI binary that actually has the extension compiled in — this is the usual first failure in containers.

Implementation Patterns

Per-environment config, same file. Keep one watcher.php but drive paths from env() so the Docker image and the host machine disagree only where they must:

'dir' => array_filter(explode(',', env('WATCH_DIRS', 'app,config'))),

Pick the driver to match the filesystem, not the OS. ScanFileDriver is pure PHP and polls the tree — portable, works on bind mounts and Windows, but CPU cost scales with directory size. Shell-based drivers (FindDriver, and an fswatch-backed driver where available) offload the diffing to the OS but depend on the binary existing in the container and on real filesystem events being delivered. Default to ScanFileDriver; only switch when your watch set is large and the host is Linux with native events.

Wire it into the team's dev entrypoint so nobody has to remember it:

"scripts": {
  "dev": "php bin/hyperf.php server:watch"
}
# docker-compose.yml
services:
  app:
    command: php bin/hyperf.php server:watch
    volumes:
      - .:/opt/www

Set 'bin' explicitly in the container if php resolves to a different build than the one running PHP-FPM/CLI-with-Swoole.

Compose the watch set deliberately. Watch app, config, and .env; never watch runtime. Add feature directories (app/Domain, modules/*/src) as they appear — the watcher does not discover new roots on its own.

Treat a reload as a cold boot in your workflow. Anything you'd normally do after start (clear container cache, re-run di:init-proxy, migrate, seed) has to keep happening after an automatic reload. If a change needs a cache rebuild, either automate it in a dev script the reload invokes, or accept a manual restart for that class of change and don't fight the watcher.

Custom driver as the extension point. When the bundled drivers don't fit (e.g. you want to trigger reloads from a message queue, a git hook, or a .trigger sentinel file), implement the driver contract with a check/fetch method returning the changed paths and register the class in watcher.php's driver key. Everything else in the package — spawning, restarting, output — stays unchanged.

One watcher per service. In a multi-app monorepo, run a watcher process per Hyperf app with non-overlapping dir sets and distinct ports. A single watcher over the repo root restarts every service on every edit.

Gotchas and Tips

Reloads are hard restarts, not graceful reloads. In-flight requests are dropped, coroutine-local state and singletons are rebuilt, connection pools are recreated. Don't use the watcher to reproduce stateful bugs that depend on a warm process; use a manual restart plus a scripted request sequence for those.

Polling cost is the #1 complaint. ScanFileDriver walks every watched directory on an interval. If idle CPU is high, you almost certainly have vendor or runtime inside the watched set, or a monorepo root being scanned. Shrink watch.dir first, then tune the polling interval, then consider a shell-based driver.

Docker Desktop on macOS/Windows delivers unreliable file events over bind mounts. This is why the shell drivers misbehave there while they're fine on Linux CI-less dev boxes. Use the polling driver and accept a small latency (a second or two) rather than debugging phantom missed reloads.

Reload loops from self-written files. If your app generates files into a watched path — logs, compiled views, Swagger output, proxy classes, uploaded fixtures — every request writes a file, the watcher reloads, the next request writes again. Exclude the output directories. If reloads fire with no edits at all, this is the first thing to check.

Orphaned child servers hold the port. The watcher spawns the actual server process; if the watcher is SIGKILLed rather than interrupted, the child can survive and the next start fails with "address already in use". Get in the habit of Ctrl+C (SIGINT), and when it does happen: ps aux | grep hyperf, lsof -i :9501, kill the leftover.

Autoload changes need a manual step. Editing composer.json or adding a new namespace doesn't take effect from the watcher's restart alone. Run composer dump-autoload (and clear the DI/config container cache if the framework cached it) before expecting the new classes to resolve.

New top-level directories aren't picked up live. Some drivers cache their scan root set at startup. Create app/NewModule and your first edit there may not trigger anything — restart the watcher once after creating structural directories.

Stale container/proxy caches look like watcher bugs. If reloads happen but behavior doesn't change, clear runtime/container (and regenerate DI proxies) before blaming the watcher. Symptom is a new class that "doesn't exist" after a reload.

bin is not decorative. In images with several PHP versions, the framework's php on PATH may lack Swoole while your watcher's bin points at one that has it — or vice versa. Mismatch shows up as the child immediately dying while the watcher keeps running, which reads like "the watcher is broken."

Never ship it. It's require-dev for a reason: it spawns and kills processes, writes to the console, and is inappropriate in production. Keep it out of the production image's composer install --no-dev set and out of any supervisor config.

Verbose output is your debugging tool. When you can't tell what triggered a reload, run the command with its debug/verbose flag and correlate logged paths against your excludes. If the paths are all in runtime or a generated folder, you've found the culprit without touching code.

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.
vento/laravel-airbrake
wfeller/laravel-batch
maco-studios/mage-os-installer
busarm/armie
siberfx/laravel-logger
kfteam1991/dcat-admin
dashed/dashed-laposta
dashed/dashed-ecommerce-montaportal
dashed/dashed-ecommerce-etsy
neohq/string-blade-compiler
log1x/acf-composer
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager