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

typhoon/reflection

Static, fast alternative to PHP’s native Reflection. Reflects code without running or autoloading it, uses lazy loading + caching, and stays compatible with native reflection. Supports Psalm/PHPStan phpDoc types, template resolution, and avoids memory leaks (safe with zend.enable_gc=0).

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require typhoon/reflection
    

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

  2. First Use Case: Reflect a class without instantiating it:

    use Typhoon\Reflection\ReflectionClass;
    
    $reflection = ReflectionClass::fromName('App\Models\User');
    $properties = $reflection->getProperties(); // Static analysis only
    
  3. Key Entry Points:

    • ReflectionClass::fromName() – Primary static class reflection.
    • ReflectionMethod::fromName() – Method reflection.
    • ReflectionProperty::fromName() – Property reflection.
    • ReflectionParameter::fromName() – Parameter reflection.
  4. Where to Look First:


Implementation Patterns

Common Workflows

1. Static Code Analysis

  • Use Case: Validate method signatures, properties, or return types without runtime overhead.
$method = ReflectionMethod::fromName('App\Services\OrderService', 'calculateTotal');
$returnType = $method->getReturnType(); // e.g., `float`
$params = $method->getParameters();

2. Dynamic Form/Validation Generation

  • Use Case: Auto-generate forms or validation rules from annotated properties.
$property = ReflectionProperty::fromName('App\Models\User', 'email');
$type = $property->getType(); // e.g., `string`
$rules = $property->getDocComment() // Parse `@Assert\Email` annotations
    ?->match('/@Assert\\\\Email/') ?? false;

3. Dependency Injection (DI) Container Enhancement

  • Use Case: Resolve constructor parameters or tagged services dynamically.
$class = ReflectionClass::fromName('App\Services\PaymentProcessor');
$constructor = $class->getConstructor();
$params = $constructor->getParameters();
foreach ($params as $param) {
    $service = $param->getType()?->getName(); // e.g., 'App\Services\Gateway'
    // Resolve $service from container
}

4. Template Resolution

  • Use Case: Resolve generic classes (e.g., Collection<int, User>).
$collection = ReflectionClass::fromName('Illuminate\Support\Collection');
$templateArgs = $collection->getTemplateArguments(); // ['int', 'App\Models\User']

5. Testing Utilities

  • Use Case: Generate test data or validate test doubles.
$mockClass = ReflectionClass::fromName('App\DTOs\UserDTO');
$properties = $mockClass->getProperties();
$testData = collect($properties)->mapWithKeys(fn ($prop) => [
    $prop->getName() => match ($prop->getType()?->getName()) {
        'int' => 123,
        'string' => 'test@example.com',
        default => null,
    }
]);

Integration Tips

Laravel-Specific Patterns

  1. Service Provider Bootstrapping:

    public function boot(): void
    {
        $this->app->resolving(function ($service, $app) {
            $reflection = ReflectionClass::fromName(get_class($service));
            if ($reflection->hasTag('lazy')) {
                // Custom lazy-loading logic
            }
        });
    }
    
  2. Artisan Commands:

    • Use reflection to list available commands or validate their signatures.
    $commands = collect(Artisan::all())
        ->map(fn ($name) => ReflectionClass::fromName($name))
        ->filter(fn ($reflection) => $reflection->hasTag('hidden') === false);
    
  3. Event Listeners:

    • Dynamically register listeners based on annotated methods.
    $listeners = ReflectionClass::fromName('App\Listeners\LogEvent')
        ->getMethods()
        ->filter(fn ($method) => $method->hasTag('listen'));
    

Performance Optimization

  • Cache Reflection Results:

    $cache = new \Symfony\Component\Cache\Simple\FilesystemCache();
    $reflection = ReflectionClass::fromName('App\Models\Post', cache: $cache);
    
  • Batch Processing:

    $classes = ['App\Models\User', 'App\Models\Post'];
    $reflections = array_map(
        fn ($class) => ReflectionClass::fromName($class),
        $classes
    );
    

Extending Functionality

  • Custom DocBlock Parsing: Use getDocComment() + regex or a parser like phpDocumentor/reflection-docblock.

    $docComment = $method->getDocComment();
    $annotations = (new \phpDocumentor\Reflection\DocBlockFactory())->createFromString($docComment);
    
  • Native Reflection Fallback:

    try {
        $reflection = ReflectionClass::fromName('App\Models\User');
    } catch (\Typhoon\Reflection\Exception\ReflectionException $e) {
        $reflection = new \ReflectionClass('App\Models\User'); // Fallback
    }
    

Gotchas and Tips

Pitfalls

  1. DocBlock Parsing Limitations:

    • Issue: Not all @var/@param types are supported (e.g., complex generics).
    • Workaround: Use getDocComment() + custom parsing or fall back to native reflection.
    • Example:
      // Unsupported: `@var Collection<int, User>`
      $type = $property->getType(); // May return `Collection` without template args
      
  2. Template Argument Resolution:

    • Issue: Generic classes (e.g., Collection<T>) may not resolve template args (T) if not explicitly defined.
    • Workaround: Use getTemplateArguments() and handle null cases.
      $args = $collection->getTemplateArguments(); // [null, null] if unresolved
      
  3. Memory Leaks:

    • Issue: Unlike native reflection, Typhoon Reflection is designed to avoid leaks, but custom caches (e.g., Symfony\Cache) must be managed.
    • Tip: Use short-lived caches or clear them after use.
      $cache->delete('typhoon_reflection_*');
      
  4. Case Sensitivity:

    • Issue: Class names are case-sensitive. ReflectionClass::fromName('app/models/user') fails if the actual class is App\Models\User.
    • Tip: Normalize case before reflection:
      $normalized = str_replace('\\', DIRECTORY_SEPARATOR, $className);
      $reflection = ReflectionClass::fromName($normalized);
      
  5. Autoloading:

    • Issue: Typhoon Reflection is static and does not trigger autoloading. If a class is not autoloaded, it throws ClassNotFoundException.
    • Tip: Ensure classes are autoloaded (e.g., via Composer) before reflection.

Debugging Tips

  1. Enable Verbose Errors:

    ReflectionClass::setDebug(true); // Logs missing classes/annotations
    
  2. Inspect DocBlocks:

    • Use getDocComment() to debug annotation parsing:
      dd($method->getDocComment());
      
  3. Compare with Native Reflection:

    • Verify results against PHP’s native reflection:
      $native = new \ReflectionClass('App\Models\User');
      $typhoon = ReflectionClass::fromName('App\Models\User');
      dd(
          $native->getProperties(),
          $typhoon->getProperties()
      );
      
  4. Handle Exceptions:

    • Catch specific exceptions for graceful degradation:
      try {
          $reflection = ReflectionClass::fromName('NonExistentClass');
      } catch (\Typhoon\Reflection\Exception\ClassNotFoundException $e) {
          // Fallback logic
      }
      

Configuration Quirks

  1. Cache Directories:

    • If using Symfony\Cache, ensure the cache directory is writable:
      $cache = new \Symfony\Component\Cache\FilesystemCache(
          storage_path('framework/cache/typhoon_reflection')
      );
      
  2. Psalm/PHPStan Integration:

    • Tip: Use psalm-plugin-phpdoc or phpstan/extension-installer to align static analysis tools with Typhoon’s type support.
    • **Example Ps
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