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

Reflection Laravel Package

brick/reflection

Low-level PHP 8.1+ helpers extending native reflection. ReflectionTools can list all instance methods/properties across inheritance (including private parents), get class hierarchies, and create reflections for any callable with meaningful function names.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require brick/reflection
    

    Ensure your project uses PHP 8.1+ (required since v0.6.0).

  2. First Use Case: Inspect a class’s full method hierarchy (including private/parent methods):

    use Brick\Reflection\ReflectionTools;
    
    $tools = new ReflectionTools();
    $methods = $tools->getClassMethods(new ReflectionClass(\App\Models\User::class));
    foreach ($methods as $method) {
        echo $method->getName() . "\n";
    }
    
  3. Key Entry Points:

    • ReflectionTools: For method/property introspection, type extraction, and signature export.
    • ImportResolver: For resolving class names in annotations (e.g., @param App\Models\User $user).

Implementation Patterns

Core Workflows

1. Dynamic Method/Property Inspection

  • Pattern: Use getClassMethods()/getClassProperties() to traverse inheritance chains.
  • Example: Generate a runtime API documentation snippet:
    $tools = new ReflectionTools();
    $class = new ReflectionClass(\App\Services\PaymentProcessor::class);
    $methods = $tools->getClassMethods($class);
    
    foreach ($methods as $method) {
        $signature = $tools->exportFunctionSignature($method);
        echo "$signature\n\n";
    }
    
  • Laravel Integration: Hook into service container binding resolution to log method calls:
    $container->resolving(function ($object, $abstract) {
        if ($object instanceof \App\Contracts\Logger) {
            $tools = new ReflectionTools();
            $methods = $tools->getClassMethods(new ReflectionClass($object));
            // Log method signatures for debugging
        }
    });
    

2. Annotation/Attribute Resolution

  • Pattern: Use ImportResolver to resolve class names in annotations (e.g., for Doctrine-like ORM mappings).
  • Example: Parse a custom @Route attribute:
    $resolver = new ImportResolver(new ReflectionClass(\App\Http\Controllers\UserController::class));
    $routeClass = $resolver->resolve('Route'); // Resolves to fully qualified name
    

3. Type-Safe Dynamic Invocation

  • Pattern: Combine exportFunctionSignature() with ReflectionMethod to validate or generate dynamic calls.
  • Example: Build a proxy for a service with type-checked arguments:
    $method = new ReflectionMethod(\App\Services\Cache::class, 'store');
    $signature = $tools->exportFunctionSignature($method);
    // Use signature to validate user input before calling $method->invoke(...)
    

4. Hierarchy-Aware Operations

  • Pattern: Use getClassHierarchy() to traverse parent classes (e.g., for trait-based logic).
  • Example: Find all methods implementing an interface across inheritance:
    $hierarchy = $tools->getClassHierarchy(new ReflectionClass(\App\Models\Post::class));
    foreach ($hierarchy as $classReflection) {
        foreach ($classReflection->getMethods() as $method) {
            if ($method->implementsInterface(\App\Contracts\Publishable::class)) {
                // Handle publishable methods
            }
        }
    }
    

Integration Tips

Laravel-Specific Patterns

  1. Service Provider Bootstrapping:

    • Register a global ReflectionTools instance for reuse:
      $this->app->singleton(ReflectionTools::class, function ($app) {
          return new ReflectionTools();
      });
      
  2. Middleware for Debugging:

    • Use exportFunctionSignature() to log incoming request handling:
      public function handle($request, Closure $next) {
          $controller = new ReflectionClass($request->route()->getController());
          $action = $request->route()->getActionMethod();
          $signature = app(ReflectionTools::class)->exportFunctionSignature(
              $controller->getMethod($action)
          );
          Log::debug("Handling: $signature", ['route' => $request->route()->getName()]);
          return $next($request);
      }
      
  3. Dynamic Form Generation:

    • Extract property types from models to auto-generate forms:
      $tools = app(ReflectionTools::class);
      $properties = $tools->getClassProperties(new ReflectionClass(\App\Models\User::class));
      foreach ($properties as $property) {
          $type = $property->getType()?->getName();
          // Generate Blade input based on $type (e.g., text, select, etc.)
      }
      
  4. Event Listeners for Reflection:

    • Trigger actions when classes are loaded (e.g., cache method signatures):
      $this->app->booted(function () {
          $tools = app(ReflectionTools::class);
          $classes = [\App\Models\User::class, \App\Services\Cache::class];
          foreach ($classes as $class) {
              $signatureCache = $tools->getClassMethods(new ReflectionClass($class));
              // Store in cache or database
          }
      });
      

Performance Considerations

  • Cache Reflections: Store results of getClassMethods()/getClassProperties() in Laravel’s cache or a static variable to avoid repeated reflection overhead.
  • Lazy Loading: Defer reflection until needed (e.g., only resolve ImportResolver when parsing annotations).

Gotchas and Tips

Pitfalls

  1. Breaking Changes in 0.x.y:

    • Methods like getParameterTypes() were removed in v0.5.0—use exportFunctionSignature() instead for type extraction.
    • Lock to a release cycle (e.g., 0.7.*) to avoid surprises during upgrades.
  2. PHP 8.1+ Dependency:

    • The package requires PHP 8.1+ (since v0.6.0). Test thoroughly if downgrading.
  3. ImportResolver Context Sensitivity:

    • The resolver depends on the context reflection object (e.g., ReflectionClass). Passing the wrong context (e.g., from a different file) will yield incorrect resolutions.
    • Fix: Always use a reflection object from the same file where the annotation/class is defined.
  4. Type Export Quirks:

    • exportFunctionSignature() may produce unexpected output for:
      • Complex generics: Nested generics (e.g., ArrayObject<string>) may not resolve perfectly.
      • Custom types: User-defined types without FQCNs may cause issues.
    • Workaround: Pre-process types or use getParameterTypes() (if available in your locked version).
  5. Hierarchy Order:

    • getClassMethods()/getClassProperties() return parent methods first, which may not match expected order in some use cases (e.g., alphabetical sorting).
    • Tip: Sort results manually if order matters:
      usort($methods, fn ($a, $b) => $a->getName() <=> $b->getName());
      
  6. Final Classes:

    • ReflectionTools and ImportResolver are final since v0.7.0—you cannot extend them.

Debugging Tips

  1. Verify Reflection Objects:

    • Always check if a ReflectionClass is valid before passing to ReflectionTools:
      $class = new ReflectionClass($className);
      if (!$class->isInstantiable()) {
          throw new \RuntimeException("Cannot instantiate $className");
      }
      
  2. Inspect Resolver Output:

    • Log ImportResolver results to debug annotation parsing:
      $resolver = new ImportResolver($contextReflection);
      $resolved = $resolver->resolve('Some\Class');
      Log::debug("Resolved 'Some\Class' to: $resolved");
      
  3. Handle Missing Types:

    • exportFunctionSignature() may return mixed for untyped parameters. Validate or sanitize output:
      $signature = $tools->exportFunctionSignature($method);
      if (str_contains($signature, 'mixed')) {
          // Handle fallback or error
      }
      
  4. Performance Profiling:

    • Reflection is slow. Use Laravel’s Benchmark facade to measure overhead:
      use Illuminate\Support\Facades\Benchmark;
      
      $time = Benchmark::measure(function () {
          $tools->getClassMethods(new ReflectionClass(\App\Models\User::class));
      });
      Log::debug("Reflection took: {$time->average()}ms");
      

Extension Points

  1. Custom Type Exporters:
    • Extend exportFunctionSignature() behavior by wrapping it:
      class CustomReflectionTools extends ReflectionTools {
          public function exportCustomSignature(ReflectionMethod $method): string {
              $signature = parent::exportFunctionSignature($method);
              // Post-process (e.g., replace 'int' with 'integer' for consistency)
              return str_replace('int', 'integer
      
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.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky