webflo/drupal-finder
A small PHP utility that locates the root of a Drupal installation from any directory, supporting both Drupal 7 and 8+. It detects the Drupal version, core path, and site directory, making it easy for tools like Drush, Composer plugins, and custom scripts to work reliably across project layouts.
Architecture fit. webflo/drupal-finder is a single-purpose, dependency-free path-resolution utility: it walks up from a given directory looking for Drupal's core/lib/Drupal.php (and Composer's vendor/autoload.php) and exposes getDrupalRoot(), getComposerRoot(), and getVendorDir(). There is no framework coupling, no service provider, no config surface, and no runtime state. It is a build-time/CLI-time convenience, not a request-path library. For a Laravel codebase it fits only in one of three roles: (a) developer/CI tooling that must find a colocated Drupal docroot, (b) an integration adapter in a Laravel app that shells into or bootstraps a Drupal site (SSO bridge, content sync, migration scripts), or (c) a transitive dependency pulled in by Drupal-oriented Composer plugins. If none of those describe the estate, the architectural fit is effectively zero and the correct answer is to not adopt.
Integration feasibility. Trivially installable — composer require webflo/drupal-finder, instantiate, call locateRoot(), read the root. A competent PHP engineer is productive in under an hour. The real work is not integration but containment: wrapping the call behind an interface so the rest of the Laravel application never depends on the class directly, and so an env-var override (DRUPAL_ROOT) can supersede heuristic discovery in environments where the heuristic is wrong.
Technical risk.
vendor hoisting, monorepos containing multiple Drupal checkouts, or a relocated core will produce a wrong-but-plausible root rather than an error — the failure is silent, which is worse than a crash.Key questions. Is Drupal root discovery on a runtime path or confined to build/CI? Do we distribute the consuming artifact? Is there an existing DRUPAL_ROOT convention we should prefer? What is our documented GPL policy for dev dependencies? Can we replace this with a ~60-line internal resolver and own it outright?
Stack fit. PHP 8.x, Composer 2, Laravel 10–12. The package is framework-agnostic, so it composes cleanly — but it also means Laravel gives us nothing for free. Plan on a thin DrupalLocator contract bound in a service provider, with the package as the implementation detail. Cache the resolved root for the process lifetime; do not re-walk the tree per job.
Migration path. Where teams currently hand-roll path arithmetic (__DIR__ . '/../../..') or carry a hardcoded DRUPAL_ROOT env value, introduce the finder as a fallback behind the existing override, ship it dark, and compare resolved values in CI logs for a release cycle before flipping the default. This makes the migration reversible and surfaces heuristic disagreements without production impact.
Compatibility. Drupal 8 through 11 all place Drupal.php at core/lib/, so detection holds across versions. It locates the root, not a site, so multisite installations are unaffected. Watch PHP version constraints and confirm Composer 2-only behavior if any legacy Composer 1 tooling remains. If the Laravel app and Drupal site ship in separate containers/images, the finder will simply fail to locate a root — confirm the two artifacts are ever colocated before committing.
Sequencing. (1) License and provenance clearance with legal/security — gate, not parallel track. (2) Spike on a branch against the actual Drupal layout, including the symlinked-docroot case. (3) Implement the wrapper interface and env override. (4) Add a CI assertion that the resolved root matches the expected path across all supported layouts. (5) Roll out to tooling and non-production jobs first. (6) Re-evaluate adoption for any distributed artifact.
Maintenance. Very low ongoing cost — the API is stable and the code is small enough to audit in an afternoon. The bus factor of one is mitigated by vendoring or, better, by forking the ~200 lines internally under the GPL's explicit permission to do so. A pinned version with a documented upgrade cadence is sufficient.
Support. No commercial backing. Upstream responsiveness is not something to plan around; our CI assertion is the real safety net. The GPL grant is a genuine upside here — if the project stalls, we can fork and patch without negotiation.
Scaling. Effectively free. Operations are stat/file_exists calls against parent directories, bounded by tree depth. The only scaling concern is accidental per-request invocation in a Laravel app under load — resolve once at boot and memoize. No network, no I/O beyond the local filesystem, no concurrency hazards.
Failure modes.
vendor/ in slim production images, which changes what getComposerRoot() returns even when the Drupal root is found correctly.Ramp-up. An hour for a mid-level engineer to use, half a day to integrate responsibly behind an interface with tests. The learning cost is negligible; the organizational cost — license review, provenance verification, and a decision on internal fork versus upstream dependency — is where the actual effort sits, and it should be scheduled as such rather than treated as a one-line Composer addition.
How can I help you explore Laravel packages today?