graham-campbell/analyzer
Analyzer is a PHP tool by Graham Campbell that scans your code/tests to verify referenced classes actually exist, catching typos and broken imports early. Requires PHP 8.1–8.5 and supports PHPUnit 10–13 (v5.1).
composer require --dev graham-campbell/analyzer:^5.1
phpunit.xml):
<phpunit>
<extensions>
<extension class="GrahamCampbell\Analyzer\Extension"/>
</extensions>
</phpunit>
./vendor/bin/phpunit
Create a test class extending GrahamCampbell\Analyzer\TestCase:
use GrahamCampbell\Analyzer\TestCase;
class ClassReferenceTest extends TestCase
{
protected function getPaths(): array
{
return [__DIR__.'/../src'];
}
}
Run the test to detect missing or invalid class references in your codebase.
register()/boot() methods for missing classes (e.g., App\Services\PaymentService).
use GrahamCampbell\Analyzer\TestCase;
class PaymentServiceProviderTest extends TestCase
{
protected function getPaths(): array
{
return [app_path('Providers')];
}
}
App\Facades\PaymentFacade) reference valid underlying classes.toArray()/with() methods for model/relationship references.Extend provideFilesToCheck() to include PHPDoc-heavy files (e.g., controllers, DTOs):
protected function provideFilesToCheck(): array
{
return [
__DIR__.'/../app/Http/Controllers',
__DIR__.'/../app/Dtos',
];
}
Add to .github/workflows/ci.yml:
- name: Analyze Class References
run: ./vendor/bin/phpunit --extension GrahamCampbell\Analyzer\Extension
Run before tests to fail fast on broken references.
Override getIgnored() to exclude known third-party or dynamic classes:
protected function getIgnored(): array
{
return [
'Illuminate\Support\Collection', // Dynamically extended
'Laravel\Nova\Nova', // Managed by Nova
];
}
Use shouldAnalyzeFile() to skip specific files (e.g., generated stubs):
protected function shouldAnalyzeFile(string $file): bool
{
return !Str::contains($file, 'generated/');
}
False Negatives with Dynamic Classes:
class_alias() or eval() won’t be detected. Exclude these from analysis or mock them in tests.getIgnored() to whitelist dynamic classes.PHPDoc Parsing Quirks:
@template-extends) may cause parsing errors. Simplify annotations or update the package.phpunit.xml for @php tags to set error_reporting:
<php>
<ini name="error_reporting" value="-1"/>
</php>
Performance in Large Codebases:
getPaths() to critical directories (e.g., app/, src/).phpunit --parallel.Namespace Conflicts:
use App; aliases, the package won’t resolve them. Stick to fully qualified names or mock the resolver.PHPUnit Version Mismatch:
phpunit.xml matches the package’s supported versions (v5.1 requires PHPUnit 10–13).phpunit.xml:
<phpunit>
<extensions>
<extension class="GrahamCampbell\Analyzer\Extension" debug="true"/>
</extensions>
</phpunit>
getPaths().storage/logs/laravel.log.Custom Analyzers:
Extend GrahamCampbell\Analyzer\Analyzer to add rules (e.g., validate interface implementations):
class InterfaceAnalyzer extends Analyzer
{
public function analyze(Node $node): void
{
if ($node instanceof ClassLike) {
$implements = $node->implements();
foreach ($implements as $interface) {
if (!$this->classExists($interface->toString())) {
$this->fail($node, "Interface {$interface} does not exist.");
}
}
}
}
}
Hook into Laravel Events:
Trigger analysis on booted or registered events in AppServiceProvider:
use GrahamCampbell\Analyzer\Analyzer;
public function boot()
{
$analyzer = new Analyzer();
$analyzer->analyzeFiles([app_path('Providers')]);
}
Artisan Command: Create a custom command for ad-hoc analysis:
use GrahamCampbell\Analyzer\Analyzer;
use Illuminate\Console\Command;
class AnalyzeCommand extends Command
{
protected $signature = 'analyze:classes';
protected $description = 'Analyze class references in the codebase';
public function handle()
{
$analyzer = new Analyzer();
$analyzer->analyzeFiles([base_path('app')]);
$this->info('Analysis complete. See PHPUnit output for errors.');
}
}
vendor/ or node_modules/ from getPaths().getIgnored().How can I help you explore Laravel packages today?