phpdocumentor/reflection
Static PHP code reflection library that parses one or more files (no execution) to build an object graph of your application's structure, including DocBlocks. Supports analyzing PHP versions from 5.2 up to your installed PHP version; useful for reflecting whole projects.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require phpdocumentor/reflection:~6.0
Add to composer.json under require-dev if only needed for testing/analysis.
require 'vendor/autoload.php';
$projectFactory = \phpDocumentor\Reflection\Php\ProjectFactory::createInstance();
$project = $projectFactory->create('MyApp', [
new \phpDocumentor\Reflection\File\LocalFile(app_path('Http/Controllers/UserController.php'))
]);
// Access classes in the project
$classes = $project->getClasses();
foreach ($classes as $class) {
echo $class->getName() . "\n";
}
RemoteFile).Workflow:
// Analyze entire Laravel app (adjust paths as needed)
$projectFactory = ProjectFactory::createInstance();
$project = $projectFactory->create('LaravelApp', [
new LocalFile(base_path('app/Http/Controllers/*.php')),
new LocalFile(base_path('app/Models/*.php')),
new LocalFile(base_path('app/Providers/*.php')),
]);
// Query the project graph
$controllers = $project->getClasses()->filter(fn($class) =>
str_contains($class->getFile()->getPath(), 'Controllers')
);
Integration Tips:
Project object to avoid reprocessing:
$cachePath = storage_path('reflection_cache.json');
if (!file_exists($cachePath)) {
$project = $projectFactory->create(...);
file_put_contents($cachePath, serialize($project));
} else {
$project = unserialize(file_get_contents($cachePath));
}
public function register()
{
$this->app->singleton('reflection.project', function () {
return ProjectFactory::createInstance()->create('LaravelApp', [
new LocalFile(base_path('app/**/*.php'))
]);
});
}
Common Use Cases:
// Get a specific class
$class = $project->getClass('App\\Http\\Controllers\\UserController');
// Inspect methods
foreach ($class->getMethods() as $method) {
echo "Method: {$method->getName()}\n";
echo "DocBlock: {$method->getDocBlock()}\n";
echo "Return Type: {$method->getReturnType()}\n";
}
// Inspect properties
foreach ($class->getProperties() as $property) {
echo "Property: {$property->getName()} (Type: {$property->getType()})\n";
}
Laravel-Specific Patterns:
Route Controller Binding:
$routes = collect($project->getClasses())
->filter(fn($class) => $class->hasTag('Route'))
->map(fn($class) => [
'controller' => $class->getName(),
'methods' => $class->getMethods()->pluck('name')->toArray(),
]);
Middleware Analysis:
$middleware = $project->getClasses()
->filter(fn($class) => $class->implementsInterface('Illuminate\\Contracts\\Auth\\Authenticatable'))
->pluck('name');
Extracting PHPDoc Tags:
$class = $project->getClass('App\\Models\\User');
$docBlock = $class->getDocBlock();
if ($docBlock) {
$tags = $docBlock->getTags();
$author = $tags['author']?->getContent();
$deprecated = $tags['deprecated']?->getContent();
}
Custom Annotations:
// Example: Parse `@api` tags for API documentation
$apiRoutes = $project->getClasses()
->filter(fn($class) => $class->getDocBlock()?->hasTag('api'))
->map(fn($class) => [
'path' => $class->getDocBlock()->getTag('api')->getContent(),
'class' => $class->getName(),
]);
Handling Default Arguments:
$method = $project->getClass('App\\Services\\UserService')->getMethod('find');
$parameters = $method->getParameters();
foreach ($parameters as $param) {
$type = $param->getType();
$defaultValue = $param->getDefaultValue();
echo "Parameter: {$param->getName()}\n";
echo "Type: {$type}\n";
echo "Default: {$defaultValue}\n";
}
Expression Analysis (PHP 8.0+):
// Resolve complex default values (e.g., `[]`, `new DateTime()`)
$param = $method->getParameters()->first();
$expression = $param->getDefaultValueExpression();
if ($expression) {
$resolvedType = $expression->getType();
echo "Resolved Type: {$resolvedType}"; // e.g., `array`, `DateTime`
}
Extracting Attributes:
$class = $project->getClass('App\\Http\\Middleware\\VerifyCsrfToken');
$attributes = $class->getAttributes();
foreach ($attributes as $attribute) {
echo "Attribute: {$attribute->getName()}\n";
$arguments = $attribute->getArguments();
foreach ($arguments as $arg) {
echo " Argument: {$arg->getName()} = {$arg->getValue()}\n";
}
}
Laravel-Specific:
// Find all middleware with `@Middleware` annotations
$middleware = $project->getClasses()
->filter(fn($class) => $class->hasAttribute('Illuminate\\Pipeline\\Middleware'))
->pluck('name');
File Path Handling:
LocalFile for local paths and RemoteFile for URLs (e.g., GitHub Gists).// Fix: Use absolute paths
$file = new LocalFile(realpath(__DIR__ . '/../app/Models/User.php'));
PHP Version Compatibility:
phpDocumentor\Reflection\Php\Parser\ParserFactory.Circular Dependencies:
~6.4.4 or later (see release notes).Memory Usage:
ProjectFactory::setMemoryLimit().DocBlock Parsing Quirks:
@param tags may not parse correctly if indentation varies.$docBlock = str_replace(["\r\n", "\r"], "\n", $docBlock);
Parse Errors:
$projectFactory = ProjectFactory::createInstance();
$projectFactory->setLogger(new \Monolog\Logger('reflection', [
new \Monolog\Handler\StreamHandler('php://stderr', \Monolog\Logger::DEBUG),
]));
Type Resolution Issues:
mixed or null, check:
phpparser version (upgrade to ~4.0 for PHP 8.1+).ProjectFactory::addStrategy().Performance Profiling:
$projectFactory->setParser(new \phpDocumentor\Reflection\Php\Parser\Parser([
'cacheFile' => storage_path('parser_cache'),
]));
How can I help you explore Laravel packages today?