lorisleiva/lody
Lody loads files or PHP classes from one or more paths as a Laravel LazyCollection. Discover classes via PSR-4 resolution, then filter (e.g., non-abstract, instance of) and iterate to register or process them. Configurable path and classname resolving.
LazyCollection design minimizes memory overhead during class/file discovery, critical for large codebases (e.g., monorepos, plugin systems).composer.json), reducing friction in namespace-aware applications. Avoids reinventing the wheel for class-to-path resolution.isInstanceOf(), hasTrait()) enable declarative discovery, mirroring Laravel’s query builder or collection APIs. Ideal for domain-specific logic (e.g., "register all queued commands in app/Jobs/").resolvePathUsing, resolveClassnameUsing) allow adaptation to non-standard paths or custom autoloaders, supporting edge cases like vendor-agnostic plugins or multi-root projects.base_path(), app()->getNamespace(), and Finder components ensures seamless adoption in existing projects. Minimal abstraction overhead.composer require and basic usage (e.g., Lody::classes('app/Plugins')->each(...)).setBasePath()) enables use in Lumen, Symfony, or plain PHP, though Laravel-specific features (e.g., app()->getNamespace()) are lost.DataFactory classes) and mocking (e.g., filter classes with hasMethod('mock')).| Risk Area | Severity | Mitigation |
|---|---|---|
| Performance Overhead | Low | Lazy loading ensures minimal memory impact. Benchmark with ->count() before ->each(). |
| PSR-4 Dependency | Medium | Custom resolvers can adapt to non-PSR-4 setups, but require upfront effort. |
| Laravel-Specific Logic | Low | Standalone mode exists, but loses Laravel conveniences (e.g., base_path()). |
| Class Validation | Medium | classExists() filters invalid classes, but runtime errors may occur if filters are too permissive. |
| Directory Traversal | Low | Uses Finder with recursive defaults; restrict paths explicitly (e.g., app/) to avoid vendor/ scans. |
| Trait/Method Reflection | Low | Relies on PHP’s ReflectionClass, which is stable but may miss edge cases (e.g., dynamic methods). |
| Future Laravel Breaking Changes | Low | Package maintains compatibility with latest Laravel versions (e.g., v0.7.0 for Laravel 13). |
app/ or include vendor/ (e.g., for third-party plugins)? If the latter, how will we handle namespace collisions?ShouldAutoRegister, PluginContract.hasTrait('DebugOnly') in production)?->remember() or ->cache().app/Modules/ with 1000+ classes), should we add parallel scanning or chunking?Lody integrate with Laravel’s exception handling (e.g., App\Exceptions\Handler)?->forTesting() method to exclude production-only classes.Lody in unit tests? Use a test double for Lody::classes() in critical paths.base_path() or Finder behavior?app/) to prevent vendor/ or node_modules/ scans?boot() via Lody::classes()->each().PluginManager::bind()).ShouldRegisterCommand.Lody::classes()->hasTrait('HandlesEvents').| Phase | Action | Tools/Lody Methods |
|---|---|---|
| Assessment | Audit existing manual registrations (e.g., register() in providers). |
Lody::classes('app/')->dump() |
| Pilot | Replace one service provider with Lody (e.g., event listeners). | Lody::classes('app/Listeners')->each(...) |
| Incremental Rollout | Migrate plugins, commands, and policies in batches. | Lody::files('app/Plugins')->getClassnames() |
| Full Adoption | Replace all manual registrations with Lody. | Lody::resolvePathUsing(fn($path) => ...) |
| Optimization | Add caching (e.g., ->remember(60)) for performance-critical paths. |
Lody::classes()->cache() |
resolveClassnameUsing for non-PSR-4 setups (e.g., App\ namespace mapped to src/).lorisleiva/lody to composer.json.AppServiceProvider (e.g., custom base path or classname logic).EventServiceProvider) with Lody.Lody::classes('app/Listeners')->count().Lody::classes('app/Plugins')->hasTrait('PluginContract')->each(...).Lody::classes('app/Commands')->remember(3600)).->when(fn() => app()->isLocal(), ...)).How can I help you explore Laravel packages today?