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

fsi/reflection

Optimized Reflection for PHP 5.3 that caches ReflectionClass/Method/Property objects via factory() so they’re never created twice. Returned methods/properties are pre-set accessible for private/protected access; some uncached operations throw exceptions.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation: Add the package to your composer.json under require:

    "fsi/reflection": "0.9.*"
    

    Run composer update.

  2. Replace Constructors: Replace all direct instantiations of reflection classes (e.g., new ReflectionClass()) with factory methods:

    // Before (throws exception)
    $reflection = new ReflectionClass('App\Models\User');
    
    // After (cached)
    $reflection = ReflectionClass::factory('App\Models\User');
    
  3. First Use Case: Use it in a Laravel service or command to inspect a model or class dynamically. For example, in a UserService:

    use FSi\Reflection\ReflectionClass;
    
    public function getPrivateProperty($userId)
    {
        $reflection = ReflectionClass::factory(User::class);
        $property = $reflection->getProperty('privateField');
        $property->setAccessible(true); // Automatically handled by the package
        return $property->getValue(User::find($userId));
    }
    
  4. Verify Memory Savings: Use memory_get_usage() before/after reflection operations in a loop to confirm reduced memory churn.


Implementation Patterns

Usage Patterns

  1. Factory-Based Reflection: Always use factory methods for reflection classes:

    // Class reflection
    $classReflection = ReflectionClass::factory('App\Models\Post');
    
    // Method reflection
    $methodReflection = $classReflection->getMethod('privateMethod');
    
    // Direct factory call
    $methodReflection = ReflectionMethod::factory('App\Models\Post', 'privateMethod');
    
  2. Automatic Accessibility: Private/protected properties/methods are automatically accessible:

    $property = ReflectionProperty::factory('App\Models\User', 'hiddenToken');
    $value = $property->getValue($user); // No need for setAccessible(true)
    
  3. Integration with Laravel:

    • Service Container: Cache reflection for bindings or dynamic instantiation:
      $container->bindIf('app.models.user.reflection', function () {
          return ReflectionClass::factory(User::class);
      });
      
    • Eloquent Events: Use in retrieved or saved hooks for dynamic property access:
      Model::retrieved(function ($model) {
          $reflection = ReflectionClass::factory(get_class($model));
          // Inspect or modify properties dynamically
      });
      
    • Testing: Optimize test doubles or mocks:
      $method = ReflectionMethod::factory(User::class, 'validatePassword');
      $method->setAccessible(true);
      $result = $method->invoke($user, $password);
      
  4. Dynamic Proxies: Use in AOP or proxy-based systems (e.g., Laravel’s ProxyQueryBuilder):

    $proxy = new DynamicProxy(
        ReflectionClass::factory('App\Services\PaymentGateway')
    );
    
  5. Code Generation: Reduce overhead in tools that generate classes/methods at runtime (e.g., custom ORM builders):

    $classReflection = ReflectionClass::factory('GeneratedClass');
    // Analyze or modify generated code
    

Workflows

  1. Reflection-Heavy Loops: Cache reflection objects outside loops to avoid repeated instantiation:

    $reflections = collect($classes)
        ->map(fn ($class) => ReflectionClass::factory($class))
        ->all();
    
    foreach ($reflections as $reflection) {
        // Process each reflection (cached)
    }
    
  2. Conditional Reflection: Use factories for conditional reflection (e.g., in plugins or dynamic modules):

    if ($featureEnabled) {
        $reflection = ReflectionClass::factory('DynamicModule');
        // Enable feature-specific logic
    }
    
  3. Dependency Injection: Inject reflection factories into services for lazy loading:

    public function __construct(private ReflectionFactory $reflectionFactory) {}
    
    public function analyze()
    {
        $reflection = $this->reflectionFactory->createClass('App\Service');
        // ...
    }
    

Integration Tips

  1. Laravel Service Providers: Register a global reflection helper in AppServiceProvider:

    public function boot()
    {
        if (! function_exists('reflect')) {
            function reflect(string $class, string $type = 'class'): mixed {
                return match ($type) {
                    'class' => ReflectionClass::factory($class),
                    'method' => throw new \InvalidArgumentException('Use getMethod on ReflectionClass'),
                    default => throw new \InvalidArgumentException("Unsupported reflection type: {$type}"),
                };
            }
        }
    }
    
  2. Testing: Mock reflection factories in unit tests:

    $this->app->instance(
        ReflectionClass::class,
        Mockery::mock(ReflectionClass::class)
    );
    
  3. Performance Profiling: Use Laravel Debugbar or Blackfire to compare memory usage before/after adoption:

    // Before
    $reflection = new ReflectionClass('App\Models\User'); // High memory churn
    
    // After
    $reflection = ReflectionClass::factory('App\Models\User'); // Cached
    
  4. Partial Adoption: Start with non-critical paths (e.g., CLI commands) before applying to core logic:

    // In a command
    $reflection = ReflectionClass::factory('App\Commands\GenerateReport');
    

Gotchas and Tips

Pitfalls

  1. Constructor Exceptions:

    • Issue: Direct instantiation of reflection classes (e.g., new ReflectionClass()) throws exceptions.
    • Fix: Use ReflectionClass::factory() everywhere. For CI/CD, whitelist paths where direct constructors are unavoidable (e.g., third-party code).
    • Tip: Run a static analysis tool (e.g., PHPStan) to detect remaining constructors:
      // phpstan.neon
      rules:
          FSi\Reflection\ReflectionClass::class:
              no-new: true
      
  2. Partial Method Coverage:

    • Issue: Not all reflection methods are optimized. Dynamic calls (e.g., getMethod() on non-cached classes) may still create objects.
    • Fix: Cache reflection objects at the highest possible level (e.g., class-level):
      // Cache per class
      static $reflections = [];
      $reflection = $reflections[$class] ??= ReflectionClass::factory($class);
      
  3. PHP 5.3 Limitations:

    • Issue: Missing features in PHP 5.3 (e.g., no ReflectionAttribute) may break modern Laravel code.
    • Fix: Avoid using this package in Laravel 8.x+. For PHP 5.3, manually implement missing reflection features.
  4. Thread Safety:

    • Issue: Reflection cache is not thread-safe (though PHP is single-threaded by default).
    • Fix: In multi-process environments (e.g., PHP-FPM), ensure cache invalidation or use a process-local cache.
  5. Debugging Complexity:

    • Issue: Stack traces for reflection errors may point to factory methods instead of the original code.
    • Fix: Add custom error handlers to prettify reflection-related errors:
      set_error_handler(function ($errno, $errstr) {
          if (str_contains($errstr, 'Reflection')) {
              return false; // Let Laravel handle it
          }
          return false;
      });
      
  6. Third-Party Conflicts:

    • Issue: Packages like doctrine/orm or symfony/property-access may use reflection internally and fail with factory exceptions.
    • Fix: Exclude vendor directories from factory enforcement or patch dependencies.

Debugging Tips

  1. Enable Reflection Logging: Add a debug wrapper to log factory calls:

    ReflectionClass::factory = function ($class) {
        \Log::debug("ReflectionClass factory called for: {$class}");
        return \FSi\Reflection\ReflectionClass::factory($class);
    };
    
  2. Memory Leak Detection: Use memory_get_usage() in loops to identify reflection-related memory spikes:

    $before = memory_get_usage();
    foreach ($classes as $class) {
        $reflection = ReflectionClass::factory($class);
    }
    $after = memory_get_usage();
    \Log::info("Memory used: " . ($after - $before));
    
  3. Cache Validation: Verify cached objects are reused:

    $ref1 = ReflectionClass::factory('App\Models\User');
    $ref2 = ReflectionClass::factory('App\Models\User');
    \Log::info("Same object: " . ($ref1 === $ref2)); // Should be true
    
  4. Exception Handling: Catch factory exceptions gracefully:

    try {
        $reflection = ReflectionClass::factory('NonExistentClass');
    }
    
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