wyrihaximus/list-classes-in-directory
A small utility that scans a directory and returns the fully qualified class names found inside it. Handy for auto-discovery, registering implementations, building class maps, or wiring up plugin-style architectures without manually maintaining lists.
Architecture fit. wyrihaximus/list-classes-in-directory is a framework-agnostic PHP utility that sits at the code-introspection layer: given a path, it returns fully qualified class names extracted by tokenizing/parsing the files rather than by autoloading them. That distinction matters for a Laravel codebase. Because it does not trigger autoloading or instantiation, it is safe to run against classes that have constructor dependencies, side-effecting static initializers, or unresolved bindings — a real constraint if we tried to do the same thing with ReflectionClass over glob(). It has no Laravel coupling, no service provider, no config, and no facade, so it belongs in the tooling/infrastructure tier of our architecture, not the domain or HTTP layer. The realistic fit is one of: (a) build-time generation of a discovery manifest (commands, policies, observers, Livewire components, module registration), (b) an Artisan-based architecture/consistency audit, or (c) scaffolding and migration tooling. It is a poor fit as a per-request runtime dependency.
Integration feasibility. Trivially installable via Composer; the API surface is small and functional (directory and single-file entry points returning an iterable of FQCNs). The integration work is not in the wiring — it is in the caching and the abstraction we place around it. Discovery that walks the filesystem and parses files cannot run on every boot in a Laravel app of any size; it must be either a CI/build step that emits a manifest or a cached artisan command with the result persisted (config cache, generated PHP file, or cache store keyed on a hash of directory mtimes).
Technical risk. The dominant risk is not code quality — it is dependency posture. 24 stars, 0 dependents, and a single maintainer mean we are effectively self-supporting if the package stalls. Second-order risk is Composer resolution: if the package pins nikic/php-parser (or a tokenizer abstraction) at a major version that conflicts with PHPStan, Rector, Pint-adjacent tooling, or Pest plugins already in require-dev, we get a hard install failure that is annoying to unwind. Third, the package is small enough that "build vs. buy" is genuinely competitive: Symfony Finder plus token_get_all() is roughly 60 lines, and reading vendor/composer/autoload_classmap.php after composer dump-autoload -o is a zero-dependency alternative if PSR-4 compliance is guaranteed.
Key questions to resolve before adoption.
app/ tree?vendor/node_modules, and handle multiple class declarations per file and anonymous classes?Stack fit. Laravel 10/11/12 on PHP 8.2+. The package is a pure Composer addition with no publishable assets, so it fits at the lowest-friction tier. I would classify it as require-dev unless we deliberately ship runtime discovery, and even then the runtime path should consume a pre-generated manifest rather than the library directly.
Migration path. There is nothing to migrate; this is greenfield. The correct move is to introduce it behind an internal port — e.g. App\Support\Discovery\ClassDiscovery with a single iterate(string $path): iterable method — and bind the third-party implementation to that interface. That costs an hour and converts a supply-chain decision into a one-line swap if the package is abandoned, changes its API, or blocks a PHP upgrade.
Compatibility. Run composer require --dry-run and composer why-not against the current lockfile first; specifically check for transitive conflicts on any parser dependency. Confirm the package does not require an extension we don't guarantee in our base image (tokenizer is bundled, but confirm). Verify behavior under our autoloader configuration, including any non-standard PSR-4 mappings and classmap-authoritative production builds.
Sequencing.
app/, measure wall time and memory on the largest module, and diff the output against composer dump-autoload -o's classmap to quantify false negatives.ClassDiscovery port and a value object for results; write tests covering multiple classes per file, enums, anonymous classes, and a deliberately malformed file.Maintenance. Low surface area, MIT license, active-enough release history (most recent July 2024). But the bus factor is one. We should pin an exact version ("x.y.z" not "^x.y"), vendor-lock it, and record an internal owner. The realistic maintenance event is not a security patch or a feature request — it is a PHP minor-version bump where the maintainer is slow to widen the constraint, blocking our own upgrade. The adapter pattern is the insurance policy here.
Support. Community-only. No SLA, no commercial backing, issues answered on the maintainer's schedule. Internal documentation needs to cover the discovery contract, the caching invalidation rules, and the fallback plan, because we cannot escalate upstream.
Scaling. Cost is O(number of PHP files) with a parse per file. On a monorepo with several thousand files this is seconds, not milliseconds, which makes per-request or per-boot invocation unacceptable in production. Horizontal scaling is fine provided the manifest is baked into the deploy artifact; if discovery runs at boot, every pod pays the cost and behavior can diverge if filesystem ordering differs.
Failure modes. The dangerous ones:
Ramp-up. Hours for a mid-level engineer. Documentation is README-scale, so the internal adapter and its tests become the real onboarding artifact.
Recommendation. Adopt conditionally, not by default. The library is sound, small, and does one thing without side effects, which is exactly what you want from a utility. But with 24 stars, zero dependents, and a ~60-line equivalent reachable via Symfony Finder plus token_get_all(), this is a build-vs-buy call where buying saves little and imports an unowned dependency. Approve it only if (1) Composer resolution is clean against our existing dev toolchain, (2) it sits behind an internal interface, and (3) discovery output is generated at build time and cached. If any of those three fail, implement the scanner in-house — the maintenance delta is negligible and the risk transfer is zero.
How can I help you explore Laravel packages today?