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

Surveyor Laravel Package

laravel/surveyor

Laravel Surveyor is a mostly static analysis tool for PHP/Laravel that scans files or classes to extract rich metadata (classes, methods, properties, types, bindings, models) in a structured format for other tools. Beta API; may touch DB for model inspection.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require laravel/surveyor
    

    No additional configuration is required for basic usage.

  2. First Analysis:

    use Laravel\Surveyor\Analyzer\Analyzer;
    
    $analyzer = app(Analyzer::class);
    $result = $analyzer->analyzeClass(\App\Models\User::class);
    $classResult = $result->result();
    

    This immediately returns structured metadata about the User class.

  3. Key First Use Cases:

    • Introspecting Models: Extract database columns, relationships, and accessors.
    • Method Inspection: Analyze return types, parameters, and validation rules.
    • Type System: Leverage the built-in type system for static checks or documentation generation.

Where to Look First

  • Analyzer Class: Core entry point for analysis.
  • ClassLikeResult: Primary object returned for class inspection.
  • Type System: Laravel\Surveyor\Types\Type for creating and comparing types programmatically.
  • Model-Specific Features: Methods like isModelRelation() for Eloquent models.

Implementation Patterns

Core Workflows

1. Model Analysis Pipeline

$analyzer = app(Analyzer::class);
$result = $analyzer->analyzeClass(\App\Models\User::class);

// Extract database columns
$columns = collect($result->result()->properties())
    ->filter(fn($prop) => $prop->isDatabaseAttribute())
    ->pluck('name');

// Extract relationships
$relationships = $result->result()->methods()
    ->filter(fn($method) => $method->isModelRelation())
    ->pluck('name');

2. Type-Driven Validation

$method = $result->result()->getMethod('store');
$returnType = $method->returnType();

if ($returnType->isSame(Type::class(\App\Models\User::class))) {
    // Handle User return type
} elseif ($returnType->isSame(Type::arrayShape(
    Type::string(),
    Type::class(\App\Models\User::class)
))) {
    // Handle array of Users
}

3. Caching for Performance

// Enable disk caching (e.g., in a service provider)
AnalyzedCache::enableDiskCache(storage_path('surveyor-cache'));

// Subsequent analyses will use cached results
$result = $analyzer->analyzeClass(\App\Models\User::class);

4. Integration with Laravel Bindings

// Analyze a service bound in the container
$serviceClass = app()->getBinding(\App\Services\PaymentService::class);
$result = $analyzer->analyzeClass($serviceClass);

Integration Tips

Dynamic Analysis in Routes/Middleware

Route::get('/model-meta', function () {
    $analyzer = app(Analyzer::class);
    $meta = $analyzer->analyzeClass(\App\Models\Post::class)->result();
    return response()->json($meta->toArray());
});

Custom Analyzers

Extend Analyzer to add domain-specific logic:

class CustomAnalyzer extends Analyzer {
    public function analyzeDomainSpecific(ClassLikeResult $result) {
        if ($result->name() === \App\Models\User::class) {
            return $this->extractUserSpecificMetadata($result);
        }
    }
}

Type System for Custom Logic

$type = Type::from($method->returnType()->toString());
if ($type->isSame(Type::union(
    Type::class(\App\Models\User::class),
    Type::null()
))) {
    // Handle nullable User
}

State Tracking for Complex Logic

$scope = $analyzer->analyze(__FILE__)->analyzed();
$state = $scope->state();

// Inspect variable types in method bodies
$variables = $state->variables();
foreach ($variables as $var) {
    if ($var->type()->isSame(Type::class(\Illuminate\Http\Request::class))) {
        // Handle Request variable
    }
}

Gotchas and Tips

Pitfalls

  1. Non-Static Analysis Quirks:

    • Database Connection: Surveyor briefly connects to the database to inspect models. Avoid running it in environments where DB connections are expensive (e.g., CI pipelines without a DB).
    • Binding Resolution: If a class is not autoloadable or bound in the container, analysis may fail. Ensure all analyzed classes are properly registered.
  2. Caching Issues:

    • Corrupted Cache: If cache files are manually deleted or corrupted, Surveyor treats them as cache misses (fixed in v0.2.3).
    • Dependency Tracking: Changes to parent classes/traits may not invalidate caches immediately. Clear cache explicitly if needed:
      AnalyzedCache::clear();
      
  3. Type Inference Limitations:

    • Dynamic Properties: Properties without type hints or @property docblocks may return MixedType.
    • PHP Keywords: Method names like array or string may cause resolution issues (fixed in v0.2.4).
  4. Performance:

    • Memory Usage: Large codebases may cause high memory consumption. Use caching aggressively:
      AnalyzedCache::enableDiskCache(storage_path('surveyor-cache'));
      
    • Parallel Analysis: For batch analysis, consider running analyses in parallel (e.g., using Laravel queues).

Debugging Tips

  1. Inspect Raw Analysis:

    $result = $analyzer->analyzeClass(\App\Models\User::class);
    dump($result->analyzed()->toArray()); // Debug scope
    dump($result->result()->toArray());   // Debug class result
    
  2. Handle Missing Classes:

    try {
        $result = $analyzer->analyzeClass(\NonExistentClass::class);
    } catch (\Laravel\Surveyor\Exceptions\ClassNotFoundException $e) {
        // Fallback logic
    }
    
  3. Validate Types:

    $type = Type::from($method->returnType()->toString());
    if (!$type->isValid()) {
        // Handle invalid type (e.g., unsupported union)
    }
    
  4. Clear Cache on Changes:

    php artisan surveyor:clear-cache
    

    (Note: As of v0.2.6, this is not a built-in command; use AnalyzedCache::clear() programmatically.)


Extension Points

  1. Custom Type Resolvers: Extend the type system by creating a custom Type resolver:

    class CustomTypeResolver implements \Laravel\Surveyor\Types\Contracts\TypeResolver {
        public function resolve(string $typeString): ?Type {
            if (str_starts_with($typeString, 'App\\Custom\\')) {
                return Type::class($typeString);
            }
            return null;
        }
    }
    

    Register it via the Analyzer service provider.

  2. Hook into Analysis Pipeline: Use events or decorators to modify analysis results:

    $analyzer->analyzeClass(\App\Models\User::class)
        ->then(function ($result) {
            // Post-process result
        });
    
  3. Override Model Analysis: For Eloquent models, extend ModelAnalyzer to add custom logic:

    class CustomModelAnalyzer extends \Laravel\Surveyor\Analyzer\ModelAnalyzer {
        protected function analyzeModelRelationships(ClassLikeResult $result) {
            // Custom relationship detection
        }
    }
    
  4. Add Custom DocBlock Parsing: Extend DocBlockParser to support custom annotations:

    class CustomDocBlockParser extends \Laravel\Surveyor\Parser\DocBlockParser {
        protected function parseCustomAnnotations(string $content) {
            // Handle @custom annotations
        }
    }
    

Configuration Quirks

  1. Environment Variables:

    • SURVEYOR_CACHE_ENABLED: Defaults to false; enable for production.
    • SURVEYOR_CACHE_DIR: Must be writable by the PHP process.
  2. Facade Resolution: Surveyor resolves facades to their root classes (e.g., Auth::user()\Illuminate\Auth\AuthManager). If this causes issues, disable facade resolution in the Analyzer config.

  3. Generics Support:

    • Eloquent builder method chains (e.g., User::where(...)->get()) now propagate generics correctly (fixed in v0.2.4).
    • For custom generics, ensure type hints are properly defined in docblocks.
  4. Inertia-Specific Types: Surveyor supports Inertia’s special props (e.g., @prop('user' => ['type' => 'Inertia\InertiaResponse'])). If missing, ensure your Inertia versions are compatible.

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.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
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