Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Better Reflection Laravel Package

roave/better-reflection

Better Reflection is an enhanced PHP reflection API for static analysis. Reflect on classes without loading them, from PHP code strings, and on closures; extract method/function AST and type declarations. Feature-rich but slower than native reflection (not for runtime).

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require roave/better-reflection
    

    Add to composer.json under require-dev if only needed for static analysis.

  2. First Use Case: Reflect a class without autoloading it (e.g., for unloaded classes or strings):

    use Roave\BetterReflection\BetterReflection;
    
    $reflector = (new BetterReflection())->reflector();
    $classInfo = $reflector->reflectClass('App\\Models\\User'); // Works even if class isn't loaded
    
  3. Key Entry Points:

    • BetterReflection::reflector(): Core reflector instance.
    • reflectClass(), reflectMethod(), reflectFunction(): Reflection methods.
    • reflectFile(): Reflect an entire file (e.g., for AST extraction).

Where to Look First

  • Basic Usage: Covers core workflows (e.g., reflecting classes, methods, AST).
  • Features: Lists advanced capabilities (e.g., closures, enums, PHP 8+ support).
  • Compatibility: How it aligns with PHP’s native Reflection API.

Implementation Patterns

1. Static Analysis Workflows

Type Introspection

Extract return/parameter types from methods (including PHP 7+ declarations):

$method = $reflector->reflectMethod('App\\Services\\UserService::find');
$returnType = $method->getReturnType(); // Returns `Roave\BetterReflection\Reflection\Type\StaticType`
$parameterTypes = $method->getParameters(); // Array of `ReflectionParameter` objects

AST Extraction

Analyze method bodies without executing code:

$method = $reflector->reflectMethod('App\\Jobs\\ProcessOrder::handle');
$ast = $method->getDocComment(); // PHPDoc blocks
$bodyAst = $method->getBodyNodes(); // Abstract Syntax Tree nodes

Closure Reflection

Inspect anonymous functions:

$closure = fn($x) => $x * 2;
$reflected = $reflector->reflectClosure($closure);
$parameters = $reflected->getParameters(); // [ReflectionParameter]

2. Integration with Laravel

Service Provider Setup

Register BetterReflection as a singleton in AppServiceProvider:

public function register()
{
    $this->app->singleton(BetterReflection::class, fn() => new BetterReflection());
}

Dynamic Class Inspection

Useful for:

  • Dynamic Proxies: Inspect generated proxy classes (e.g., Laravel’s HasFactory).
  • Macroable Classes: Analyze traits/methods to validate macros.
  • Event Listeners: Reflect listeners to enforce contracts (e.g., handle() method).

Example: Validate a macroable class:

$class = $reflector->reflectClass('App\\Models\\User');
$macroMethods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach ($macroMethods as $method) {
    if ($method->getName() === 'macro') {
        // Validate macro signature...
    }
}

Artisan Commands

Build CLI tools for code analysis:

use Roave\BetterReflection\BetterReflection;

class AnalyzeCommand extends Command
{
    protected $signature = 'code:analyze {class}';
    protected $description = 'Analyze a class for compliance';

    public function handle(BetterReflection $reflection)
    {
        $class = $reflection->reflector()->reflectClass($this->argument('class'));
        $this->info($class->getDocComment());
    }
}

3. Performance Considerations

  • Cache Reflection Results: Store reflected classes in memory or a cache layer (e.g., Redis) to avoid repeated parsing.
  • Lazy Loading: Use BetterReflection::reflector() once and reuse the instance.
  • Avoid Runtime Use: BetterReflection is not optimized for runtime. Use PHP’s native Reflection for performance-critical paths.

4. Testing Patterns

Unit Testing Helpers

Generate test doubles or validate method signatures:

public function testUserServiceFindMethod()
{
    $method = (app(BetterReflection::class)->reflector())
        ->reflectMethod('App\\Services\\UserService::find');

    $this->assertEquals('App\\Models\\User', $method->getReturnType()->getClassName());
}

Mutation Testing

Use with tools like Infection to ensure reflection logic handles edge cases (e.g., unloaded classes, traits).


Gotchas and Tips

Pitfalls

  1. Runtime Performance:

    • BetterReflection is slower than native Reflection. Avoid using it for runtime introspection (e.g., in loops or hot paths).
    • Fix: Cache results or use native reflection where possible.
  2. Unloaded Classes:

    • Reflecting classes not autoloaded requires file paths or strings:
      $reflector->reflectClassFromName('App\\Models\\User'); // Fails if not autoloaded
      $reflector->reflectFile(__DIR__.'/../app/Models/User.php'); // Works
      
  3. PHP 8+ Features:

    • Enums, attributes, and readonly properties are supported but may have quirks (e.g., IS_PROTECTED_SET_COMPATIBILITY).
    • Tip: Check release notes for PHP version-specific fixes.
  4. AST Limitations:

    • Not all PHP constructs are perfectly parsed (e.g., complex dynamic code).
    • Workaround: Use getBodyNodes() for simple cases; fall back to native reflection for critical paths.
  5. Closure Reflection:

    • Only works for anonymous functions passed directly to reflectClosure(). Lambdas in strings/files require reflectFile() + manual parsing.

Debugging Tips

  1. Verify Class Loading: Use BetterReflection::reflectFile() to debug unloaded classes:

    try {
        $class = $reflector->reflectClass('NonExistentClass');
    } catch (\Roave\BetterReflection\Exception\IdentifierNotFoundException $e) {
        $this->error('Class not found. Check file paths or autoloader.');
    }
    
  2. Inspect AST Nodes: Dump AST for complex methods:

    $nodes = $method->getBodyNodes();
    $this->dump($nodes); // Use a debugger or `var_dump()`
    
  3. Compatibility Mode: Enable strict compatibility checks:

    $reflector = (new BetterReflection())->setCompatibilityMode(true);
    

Extension Points

  1. Custom Reflections: Extend Roave\BetterReflection\Reflection\ReflectionClass to add domain-specific logic:

    class CustomReflectionClass extends \Roave\BetterReflection\Reflection\ReflectionClass
    {
        public function hasCustomMethod()
        {
            return $this->hasMethod('customMethod');
        }
    }
    
  2. Plugin System: Use BetterReflection::withExtensions() to add custom providers:

    $betterReflection = BetterReflection::create()
        ->withExtensions([new CustomExtension()]);
    
  3. File Source Integration: Override file sources for custom paths (e.g., vendor classes):

    $betterReflection = BetterReflection::createFromIde(
        BetterReflection::createFromIde()->getSourceLocator()
    );
    

Configuration Quirks

  1. IDE Support:

    • Use BetterReflection::createFromIde() for PHPStorm/PSR-4 compatibility:
      $reflector = BetterReflection::createFromIde()->reflector();
      
  2. Memory Limits:

    • Large codebases (e.g., monoliths) may hit memory limits. Use setCache() to persist reflections:
      $betterReflection = BetterReflection::create()
          ->setCache(new \Roave\BetterReflection\SourceLocator\Type\SetCache());
      
  3. PHP 8.2+ Deprecations:

    • Dropped support for PHP 8.2 in v6.67.0. Ensure your composer.json targets PHP 8.3+:
      "require": {
          "php": "^8.3"
      }
      

Pro Tips

  1. Leverage for Code Generation: Use reflection to generate boilerplate (e.g., DTOs, API resources):
    $class = $reflector->reflectClass('App\\Models\\Order');
    $properties = $class->getProperties();
    // Generate a DataTransferObject class dynamically...
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport