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

Php Structure Discoverer Laravel Package

spatie/php-structure-discoverer

Discover PHP classes, interfaces, traits, and enums that match conditions (e.g., implement an interface) across your project. Fast scanning with built-in caching and rich metadata—ideal for auto-registration, tooling, and framework integrations.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require spatie/php-structure-discoverer
    

    For Laravel, publish the config:

    php artisan vendor:publish --tag="structure-discoverer-config"
    
  2. First Use Case: Discover all classes implementing Arrayable in your app directory:

    use Spatie\StructureDiscoverer\Discover;
    
    $arrayableClasses = Discover::in(app_path())->classes()->implementing(\Illuminate\Contracts\Support\Arrayable::class)->get();
    
  3. Where to Look First:

    • README.md for basic usage and examples.
    • config/structure-discoverer.php for configuration options (ignored files, cache settings).
    • app/Providers/AppServiceProvider.php (or your package's service provider) for registering structure scouts.

Implementation Patterns

Core Workflows

1. Discovering Structures

  • Basic Discovery:

    // Get all classes in a directory
    Discover::in(app_path('Models'))->classes()->get();
    
    // Get enums with a specific namespace
    Discover::in(app_path())->enums()->custom(fn($structure) => str_starts_with($structure->namespace, 'App\\Enums'))->get();
    
  • Conditional Discovery:

    // Classes extending a base model
    Discover::in(app_path('Models'))->extending(\Illuminate\Database\Eloquent\Model::class)->get();
    
    // Classes using a specific attribute
    Discover::in(app_path())->withAttribute(\Illuminate\Foundation\Testing\RefreshDatabase::class)->get();
    

2. Caching with Structure Scouts

  • Define a Scout:

    // app/Scouts/ArrayableScout.php
    use Spatie\StructureDiscoverer\StructureScout;
    
    class ArrayableScout extends StructureScout
    {
        protected function definition(): Discover
        {
            return Discover::in(app_path())->classes()->implementing(\Illuminate\Contracts\Support\Arrayable::class);
        }
    }
    
  • Register and Use:

    // In a ServiceProvider
    StructureScoutManager::add(ArrayableScout::class);
    
    // Usage
    $arrayableClasses = ArrayableScout::create()->get(); // Cached after first run
    

3. Combining Conditions

  • AND/OR Logic:
    // Classes OR enums implementing Arrayable/Stringable
    Discover::in(app_path())
        ->any(
            ConditionBuilder::create()->exact(
                ConditionBuilder::create()->classes()->implementing(\Illuminate\Contracts\Support\Arrayable::class)
            ),
            ConditionBuilder::create()->exact(
                ConditionBuilder::create()->enums()->implementing(\Stringable::class)
            )
        )
        ->get();
    

4. Parallel Discovery

  • Speed Up Large Projects:
    // Scan 100 files in parallel
    Discover::in(app_path())->parallel(100)->get();
    
    Requires amphp/parallel:
    composer require amphp/parallel
    

5. Full Metadata

  • Access Extended Properties:
    $structures = Discover::in(app_path())->full()->get();
    foreach ($structures as $structure) {
        if ($structure instanceof \Spatie\StructureDiscoverer\DiscoveredClass) {
            dump($structure->extendsChain); // Full inheritance chain
        }
    }
    

Integration Tips

1. Leveraging in Laravel

  • Service Providers: Register scouts in AppServiceProvider@boot():

    public function boot()
    {
        StructureScoutManager::add(\App\Scouts\ArrayableScout::class);
    }
    
  • Artisan Commands: Cache all scouts during deployment:

    php artisan structure-scouts:cache
    
  • Dynamic Discovery: Use in register() to lazy-load configurations:

    $this->app->singleton(\App\Contracts\ArrayableRepository::class, function () {
        $classes = ArrayableScout::create()->get();
        return new ArrayableRepository($classes);
    });
    

2. Testing

  • Mocking Discovery: Use NullDiscoverCacheDriver in tests:

    Discover::in(__DIR__)
        ->withCache('test', new \Spatie\StructureDiscoverer\Cache\NullDiscoverCacheDriver())
        ->get();
    
  • Assertions:

    $this->assertContains(\App\Models\User::class, Discover::in(app_path('Models'))->classes()->get());
    

3. Dynamic Configuration

  • Runtime Directories:

    $directories = [app_path('Models'), app_path('Policies')];
    Discover::in(...$directories)->classes()->get();
    
  • Environment-Based:

    $discoverDir = config('app.env') === 'local' ? base_path('tests') : app_path();
    Discover::in($discoverDir)->get();
    

4. Custom Cache Drivers

  • Redis Example:
    use Spatie\StructureDiscoverer\Cache\DiscoverCacheDriver;
    
    class RedisDiscoverCacheDriver implements DiscoverCacheDriver
    {
        public function has(string $id): bool { /* ... */ }
        public function get(string $id): array { /* ... */ }
        public function put(string $id, array $discovered): void { /* ... */ }
        public function forget(string $id): void { /* ... */ }
    }
    

Gotchas and Tips

Pitfalls

  1. Cache Invalidation:

    • Issue: Forgetting to clear caches after refactoring (e.g., renaming classes).
    • Fix: Run php artisan structure-scouts:clear or manually clear via:
      StructureScoutManager::clear([app_path('Scouts')]);
      
  2. Performance Spikes:

    • Issue: Parallel discovery (->parallel()) may overload the system if not configured properly.
    • Fix: Limit parallel chunks:
      Discover::in(app_path())->parallel(50)->get(); // Safer for shared hosting
      
  3. Namespace Conflicts:

    • Issue: Discovery may include vendor classes if directories aren’t restricted.
    • Fix: Explicitly exclude vendor paths:
      Discover::in(app_path())->custom(fn($structure) => !str_starts_with($structure->namespace, 'Vendor\\'))->get();
      
  4. Chains Overhead:

    • Issue: Resolving inheritance chains (extendsChain, implementsChain) can be slow for large projects.
    • Fix: Disable chains when not needed:
      Discover::in(app_path())->withoutChains()->get();
      
  5. Attribute Detection:

    • Issue: Custom attributes may not be detected if not properly registered with PHP 8.0+.
    • Fix: Ensure attributes are loaded via autoload-dev or manually include them in composer.json:
      "autoload-dev": {
          "files": ["vendor/your-package/src/Attributes.php"]
      }
      
  6. Case Sensitivity:

    • Issue: File system case sensitivity (e.g., macOS vs. Linux) may cause discrepancies.
    • Fix: Use Sort::CaseInsensitiveName for consistent sorting:
      Discover::in(app_path())->sortBy(\Spatie\StructureDiscoverer\Enums\Sort::CaseInsensitiveName)->get();
      

Debugging Tips

  1. Inspect Discovered Structures:

    • Use ->full()->get() to debug metadata:
      $structures = Discover::in(app_path())->full()->get();
      dump($structures[0]->file); // Check file paths
      
  2. Log Discovery Queries:

    • Add logging to scouts for debugging:
      class DebugScout extends StructureScout
      {
          protected function definition(): Discover
          {
              \Log::info('Discovering structures in: ' . app_path());
              return Discover::in(app_path())->classes();
          }
      }
      
  3. Verify Cache:

    • Check cache keys and contents:
      $cache = new FileDiscoverCacheDriver(storage_path('framework/cache'));
      dump($cache->has('scout_key')); // Check if cached
      dump($cache->get('scout_key')); // Inspect cached data
      
  4. Slow Discovery:

    • Profile with Xdebug or Blackfire to identify bottlenecks (e.g., large directories or complex conditions).

Extension Points

  1. Custom Conditions:
    • Example: Filter by class visibility:
      class PublicClassCondition extends DiscoverCondition
      {
          public function satisfies(DiscoveredStructure $
      
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.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata