goaop/parser-reflection
AST-based Reflection API for PHP: introspect classes, methods, and properties directly from source code without autoloading or executing anything. Built on nikic/php-parser and compatible with native Reflection classes—ideal for static analyzers, code generators, and IDE tooling.
Installation
composer require goaop/parser-reflection:^3.1.0
Add to require-dev if only needed for testing/analysis. Critical: Requires PHP 8.2+ (minimum version enforced in this release).
First Use Case Analyze a class with PHP 8+ attributes and AST-preserving features:
use GoAop\ParserReflection\ParserReflection;
$reflection = new ParserReflection();
$class = $reflection->getClass('App\Models\User');
// New: Check for PHP 8+ attributes
$attributes = $class->getAttributes();
foreach ($attributes as $attribute) {
echo $attribute->getName(); // e.g., #[Cacheable]
}
// New: AST-preserving node resolution
$usedNodes = $class->getUsedNodes();
foreach ($usedNodes as $node) {
if ($node->isClassReference()) {
echo $node->getClassName(); // Resolves dynamic references
}
}
Key Files to Explore
src/ParserReflection.php (Core class, fully PHP 8.2+ compliant)src/Reflection/ClassReflection.php (Now supports getAttributes() and AST nodes)src/Reflection/MethodReflection.php (Updated for PHP 8.2+ and new expressions in defaults)src/Reflection/PropertyReflection.php (Fixed getModifiers() for PHP 8.4+)PHP 8+ Attributes Analysis
$reflection = new ParserReflection();
$class = $reflection->getClass('App\Services\CachedService');
// Get all attributes on a class
$attributes = $class->getAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getName() === 'App\Attributes\Cacheable') {
$args = $attribute->getArguments();
// Process attribute arguments (e.g., cache TTL)
}
}
// Get attributes on a method
$method = $class->getMethod('execute');
$methodAttributes = $method->getAttributes();
AST-Preserving Dependency Analysis
$reflection = new ParserReflection();
$class = $reflection->getClass('App\Services\OrderService');
// New: AST-preserving node resolution (e.g., for complex expressions)
$usedNodes = $class->getUsedNodes();
foreach ($usedNodes as $node) {
if ($node->isClassReference()) {
echo $node->getClassName(); // Resolves dynamic references
}
if ($node->isNewExpression()) {
echo $node->getClassName(); // Supports new expressions in defaults
}
}
Laravel Integration (PHP 8.2+)
boot() for runtime analysis:
public function boot()
{
$reflection = new ParserReflection();
$this->analyzeServiceClasses($reflection);
}
$this->app->singleton(ParserReflection::class, function ($app) {
return new ParserReflection($app['cache']);
});
Testing Patterns with PHP 8.2+ Features
public function testMethodCoverage()
{
$reflection = new ParserReflection();
$class = $reflection->getClass('App\Tests\Feature\ExampleTest');
// Check for PHP 8.2+ attributes in tests
$testMethods = $class->getMethods(METHOD_PUBLIC);
foreach ($testMethods as $method) {
$attributes = $method->getAttributes();
if (in_array('App\Attributes\TestCase', array_column($attributes, 'name'))) {
// Special handling for annotated tests
}
}
}
New: Unary Expressions Support
$property = $reflection->getClass('App\Model')->getProperty('value');
$defaultValue = $property->getDefaultValue();
if ($defaultValue->isUnaryMinus()) {
echo 'Handles unary minus expressions (e.g., -10)';
}
$cache = new \Symfony\Component\Cache\Adapter\FilesystemAdapter();
$reflection = new ParserReflection($cache);
$reflection = new ParserReflection();
$reflection->setBasePath(base_path('custom/vendor'));
registering event in Laravel).getModifiers() for properties (fixed for PHP 8.4+).new expressions in parameter default values.PHP Version Requirement (BREAKING)
php -v # Must show 8.2.0 or higher
File Not Found
getFileName() to debug:
$reflection->getClass('Class')->getFileName(); // Returns full path
app/ to class names if not autoloaded.Attribute Resolution
App\Attributes\Cacheable).new SomeAttribute()) may not resolve correctly.AST Node Limitations
getUsedNodes() cautiously in production.isNewExpression(), isClassReference(), etc.).Deprecated Features
// Avoid (deprecated in PHP 8.4+)
public function foo(?string $bar): ?string;
// Use instead
public function foo(string|null $bar): string|null;
New Expressions in Defaults
new expressions in parameter defaults is new. Test thoroughly:
// Example: new \App\Models\User() in default value
$method = $reflection->getClass('App\Service')->getMethod('create');
$defaultValue = $method->getParameter('user')->getDefaultValue();
if ($defaultValue->isNewExpression()) {
echo 'Handles new expressions in defaults';
}
$reflection = new ParserReflection();
$reflection->setVerbose(true); // Logs parsing steps
try {
$reflection->getClass('BrokenClass');
} catch (\GoAop\ParserReflection\Exception\ParseException $e) {
report($e); // Laravel-specific error handling
}
getModifiers() fails on properties, ensure your PHP version is 8.4+ or use:
$modifiers = $property->getModifiers() ?? 0;
if (!$defaultValue->isUnaryMinus() && !$defaultValue->isUnaryPlus()) {
// Handle other cases
}
Custom Reflection Classes
Extend GoAop\ParserReflection\Reflection\AbstractReflection for domain-specific analysis (now supports attributes and AST nodes).
Hook into Parsing
Override ParserReflection::parseFile() to preprocess files (supports PHP 8.2+ syntax and AST preservation).
Laravel Service Provider Bind the parser to the container (ensure PHP 8.2+ compatibility):
$this->app->singleton(ParserReflection::class, function ($app) {
return new ParserReflection($app['cache']);
});
New: AST Node Extensions Extend AST node resolution for custom logic:
$nodes = $reflection->getClass('App\Service')->getUsedNodes();
foreach ($nodes as $node) {
if ($node->isAttribute()) {
// Custom logic for attributes
}
if ($node->
How can I help you explore Laravel packages today?