innmind/file-watch
Watch files and directories for changes from PHP. innmind/file-watch surfaces filesystem events when files are created, modified, or deleted, making it useful for hot reloading, build watchers, and automation that must react to file changes.
Architecture fit. innmind/file-watch is a general-purpose PHP library that binds filesystem change events to callbacks. It originates in the Innmind ecosystem, which is functionally oriented — immutable collections, explicit Maybe/Either-style result types, and a dependency graph of sibling innmind/* packages (innmind/type, innmind/immutable, innmind/operating-system, innmind/filesystem). Laravel is the opposite idiom: mutable Eloquent models, a service container with facades, and synchronous request/command lifecycles. There is no first-party Laravel integration, no service provider, no config publishing, and no facade. Anything we build is an adapter we own. The library is therefore not a fit for HTTP request paths and only conditionally viable as a long-running CLI primitive (an Artisan command or a supervised worker).
Integration feasibility. Feasible but narrow. The realistic value is developer tooling — re-running tests, linters, or codegen on save — or, at the edge, an operator-facing "watch this drop folder and dispatch a job" command. It is not a substitute for Laravel's event/queue system, S3 notifications, database triggers, or Vite's existing dev-server watcher. The absence of dependents (0) and a single star means zero ecosystem validation: no blog posts, no Stack Overflow corpus, no known Laravel usage to copy. Integration cost is dominated by learning the library's functional API plus its transitive Innmind dependencies, not by the ~500 lines of glue code.
Technical risk. High, concentrated in four places:
| Risk | Severity | Note |
|---|---|---|
| Bus factor | High | Single-maintainer package; abandonment means we fork |
| Transitive surface | Medium–High | Innmind packages are opinionated and pull in their own abstractions |
| Mechanism portability | High | Linux inotify vs. macOS FSEvents vs. Windows; containers and network mounts frequently break inotify |
| Metadata integrity | Medium | Last release dated 2026-08-16 is anomalous against the current date — either a future-dated tag, a monorepo artifact, or unreliable registry data. Must be verified before pinning |
| Maturity score | Medium | Composite score of 16.08 signals low-confidence maturity; the opportunity sub-score (44.55) reflects headroom, not readiness |
MIT licensing is clean and removes legal friction; that is the single strongest signal here.
Key questions for the maintainer/PoC.
ext-inotify, inotifywait shelled out, or stat-based polling? Polling changes the CPU/IO profile entirely.vendor/ tree?$this->output and signal handling?Stack fit. Treat it as require-dev only. The Laravel-native equivalent of this capability is a combination of Vite's watcher (frontend), spatie/phpunit-watcher or Pest's watch mode (tests), and a queue worker (production side effects). Any production use should be justified by a specific operator workflow, not by convenience.
Migration path. Define our own narrow interface first and let the package be one implementation behind it:
interface FileWatcher {
/** @return iterable<FileChanged> */
public function watch(array $paths, array $options = []): iterable;
}
Ship a PollingFileWatcher (a filemtime/hash loop on a while + sleep) as the zero-dependency default, and a InnmindFileWatcher adapter that translates the library's functional return types into plain Laravel-friendly DTOs and Illuminate\Support\Collection. This inverts the dependency: if the package is abandoned or its API shifts, we delete one adapter class. Bind the chosen implementation in a small dedicated service provider, gated by an env flag so CI and production never load it.
Compatibility. Pin exact versions in composer.json ("innmind/file-watch": "X.Y.Z"), commit composer.lock, and add the package to the renovate/dependabot watchlist with auto-merge disabled. Verify PHP constraint against our minimum supported version and against the oldest PHP in the CI matrix. Confirm it does not require an extension we do not install in the container image; if inotify is required, add it explicitly to the Dockerfile and document the fs.inotify.max_user_watches prerequisite.
Sequencing.
FileWatcher and PollingFileWatcher with the Artisan command, no external dependency.InnmindFileWatcher as an opt-in require-dev binding, feature-flagged.php artisan watch:run in the onboarding guide.Maintenance. Low code volume, non-trivial dependency surface. Realistic ongoing cost is 1–2 days per quarter: version bumps, PHP minor compatibility, and periodic verification the upstream is alive. Budget for a fork-and-vendor contingency — with one maintainer and zero dependents, the probability of an unannounced breaking change or dormancy over a 24-month horizon is material. The adapter boundary keeps the fork cost to hours, not weeks.
Support. No commercial support, no vendor SLA, no community channel to escalate to. Internal support means one or two engineers who have read the Innmind docs. Document the mechanism and the failure signatures in the internal runbook so this is not tribal knowledge.
Scaling. Only matters in the dev/CI dimension. inotify watch descriptors are consumed per directory, so watching a project root recursively can hit max_user_watches on Linux, especially with a populated vendor/ and node_modules/. Default to an explicit, narrow path allowlist (app/, routes/, config/, tests/) and explicitly exclude storage/, vendor/, node_modules/, .git/. On CI, prefer git-diff-based change detection over filesystem watching — CI containers often do not propagate inotify events reliably.
Failure modes to instrument and document.
MODIFY is subscribed. Confirm the library subscribes to create/move events, or watch the directory rather than the file.composer install or git checkout can fire thousands of events. Requires debounce; without it, we re-run commands in a loop and peg a core.SIGINT leaves a zombie holding watch descriptors. Trap signals in the Artisan command and register a shutdown handler.Ramp-up. Expect 2–3 days for a mid-level Laravel engineer to become productive: half a day on the Innmind functional idioms (result types, immutable sequences), half a day on our adapter and command, and the remainder on the platform quirks (inotify limits, container mounts). The functional style is the main friction point for a team fluent in Laravel's imperative conventions — mitigate by never letting Innmind types escape the adapter class. Overall verdict: adopt as a low-risk developer-experience experiment behind an interface, with a polling fallback and a hard no-go on production request paths; the low maturity score and single-maintainer profile argue against anything deeper.
How can I help you explore Laravel packages today?