league/construct-finder
Locate PHP code constructs (classes, interfaces, traits, enums) within one or more directories. Returns construct objects or just names, with type-specific finders and support for excluding files via simple wildcard patterns.
Installation:
composer require league/construct-finder
Add to composer.json under require-dev if only needed for testing/analysis.
First Use Case:
Locate all classes implementing a specific interface (e.g., Arrayable):
use League\Construct\Finder\Finder;
$finder = new Finder();
$classes = $finder->findClassesImplementing('Arrayable');
foreach ($classes as $class) {
echo $class->getName() . "\n";
}
Where to Look First:
src/Finder.php for core methods (e.g., findClassesExtending(), findTraitsUsedBy()).tests/ for real-world usage examples.Dependency Analysis:
// Find all classes that depend on a specific trait
$finder = new Finder();
$dependents = $finder->findClassesUsingTrait('Illuminate\Support\Traits\ForwardsCalls');
Refactoring Helper:
// Replace a base class in a codebase
$oldBase = 'App\\BaseModel';
$newBase = 'App\\NewBaseModel';
$classes = $finder->findClassesExtending($oldBase);
foreach ($classes as $class) {
// Use reflection or AST to update extends clause
}
Testing Utilities:
// Verify all services in a container implement a contract
$services = config('app.services');
$validServices = $finder->findClassesImplementing('Psr\Container\ContainerInterface');
foreach ($services as $service) {
assert(in_array($service, $validServices, true));
}
Composer Autoload:
Ensure autoload-dev is configured if scanning non-public classes:
{
"autoload-dev": {
"psr-4": {
"App\\": "src/"
}
}
}
Caching Results:
Cache results in app/ConstructFinderCache.php to avoid repeated scans:
$finder = new Finder();
$cacheKey = 'classes_implementing_Arrayable';
$classes = cache()->remember($cacheKey, now()->addHours(1), fn() => $finder->findClassesImplementing('Arrayable'));
Laravel Service Provider:
Bind Finder as a singleton for global access:
$this->app->singleton(Finder::class, fn() => new Finder());
Performance:
vendor/) is slow. Exclude directories:
$finder = new Finder();
$finder->excludeDirectories(['vendor', 'node_modules']);
False Positives:
findClassesImplementing() may include abstract classes. Filter with:
$concreteClasses = array_filter($classes, fn($class) => !$class->isAbstract());
Namespace Conflicts:
Illuminate\Support\Collection) to avoid ambiguity.PHP 8 Enums:
$finder->findEnums('`App\\Models\\Status`');
Verbose Output: Enable debug mode to log scanned files:
$finder->setDebug(true);
Check Autoload:
Ensure classes are autoloaded. Use composer dump-autoload if needed.
Custom Filters:
Extend Finder to add logic (e.g., exclude test classes):
class CustomFinder extends Finder {
public function findClassesImplementing(string $interface): array {
$classes = parent::findClassesImplementing($interface);
return array_filter($classes, fn($class) => !str_contains($class->getName(), 'Test'));
}
}
Integration with PHPStan/Nikita: Use results to generate static analysis rules or PHPDoc annotations.
Event Dispatching:
Trigger events (e.g., ConstructFound) when classes are discovered:
$finder->on('construct.found', fn($class) => event(new ClassDiscovered($class)));
How can I help you explore Laravel packages today?