spatie/php-structure-discoverer
Quickly discover classes, interfaces, enums, and traits in your PHP application that match conditions like implementing an interface, with extra metadata per structure and built-in caching to keep things fast in production.
Architecture fit. spatie/php-structure-discoverer is a read-only, stateless discovery layer that scans directories for classes, interfaces, enums, and traits and returns FQCNs or rich DiscoveredStructure objects. It does not participate in request lifecycle, DI container mutation, or runtime interception, which makes it low-coupling and safe to drop into a codebase. Its natural home is replacing hand-maintained registration arrays: service provider bindings, plugin/extension registries, attribute-driven listener/command/cast registration, and modular-monolith boundaries. For a TPM, the value proposition is "convention over configuration" — you trade explicit lists for filesystem-derived truth.
Integration feasibility. Very low effort at the surface: one composer require, one config publish (structure-discoverer-config), and three artisan commands (structure-scouts:cache, structure-scouts:clear, plus the published config). The condition DSL (classes(), implementing(), extending(), withAttribute(), any(), exact(), custom()) is expressive enough to cover most registry use cases without writing bespoke reflection code. Adoption is incremental by design — a single StructureScout subclass can be introduced without touching the rest of the app.
Technical risk. The dominant risk is not correctness of discovery but cache lifecycle: discovery results are cached, and a stale cache silently returns the wrong set. Secondary risks: composer dependency conflicts (PHP parser and AST tooling are common conflict surfaces in larger Laravel apps; amphp/parallel is an optional extra that must be justified separately), environment constraints on parallel workers, and divergence between "files on disk" and "classes actually autoloadable." Signals of quality hygiene are strong (PHPStan, CS fixer, test workflow badges, changelog, releases, MIT). Weak signals are real-world hardening: 179 stars and 0 recorded dependents means limited production exposure, and a release dated 2026-06-15 warrants verification against the actual Packagist metadata before we commit.
Key questions.
parallel() given our file count, or is that premature?Stack fit. Laravel-first and framework-agnostic underneath. The Laravel integration is idiomatic: package auto-discovery, a tagged config file, a LaravelDiscoverCacheDriver bound to any configured cache store with optional key prefix, and structure-scouts:cache / structure-scouts:clear commands that slot into existing php artisan optimize-style deploy steps. Plain-PHP usage works via FileDiscoverCacheDriver or a custom DiscoverCacheDriver implementation, so a non-Laravel service in the same estate is not blocked.
Migration path. Treat this as a strangler-fig replacement of explicit lists, not a rewrite:
structure-scouts:cache to the deploy pipeline before traffic shift.Compatibility. Verify against the composer lockfile before committing: PHP version constraint, AST/parser dependency version, and — if parallel is desired — amphp/parallel and its process requirements. Check that structure scout directories in config align with actual app/ and package paths; packages should register scouts explicitly via StructureScoutManager::add() in their service provider, since they cannot rely on the app's configured scan directories.
Sequencing. Install → publish config → define first scout → wire into provider → parity test in CI → add cache warm + clear to deploy → enable attribute-driven discovery → evaluate parallel only if cold discovery exceeds an agreed threshold (e.g., >2s in CI). Parallel amphp adoption is a separate, later decision with its own risk review.
Maintenance. Small, stable surface area. Upstream is Spatie-maintained under MIT with a visible changelog and release cadence; upgrade friction should be low given the read-only nature of the library. Our own maintenance burden is the scout definitions and the discipline of keeping scan directories and cache-warm steps in sync with the codebase's evolving structure.
Support. Community-only via GitHub issues; no vendor SLA unless a paid Spatie arrangement is procured. With 179 stars and no recorded dependents, expect a thin knowledge base — when something breaks, we will be reading source, not Stack Overflow. Budget for one engineer to be the internal owner.
Scaling. Scaling dimension is file count and disk I/O, not request volume — and caching neutralizes it. With a warmed cache, runtime cost is a single cache read. Cold discovery is O(files); parallel() (default 50 files per process) helps on large repos but adds a process-spawning dependency and is incompatible with some restricted container/hosting environments. Recommendation: ship without parallel, measure, add only if measured.
Failure modes. Ranked by blast radius:
structure-scouts:clear runs on rollback.vendor/ or test fixtures via misconfigured directories or ignored_files.Ramp-up. Low. The DSL reads like English and a mid-level Laravel developer should be productive within a day. The real onboarding cost is not the API but the operational contract: understanding when caches are built and invalidated, and why condition semantics default to AND (any()/exact() for OR composition). Plan a short internal write-up covering the deploy ordering and the parity-test pattern so the second team to adopt it doesn't rediscover these constraints.
How can I help you explore Laravel packages today?