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

Type Info Laravel Package

symfony/type-info

Symfony TypeInfo extracts and normalizes PHP type information from reflections and type strings, with support for generics, nullables, enums, and collections. Resolve types via TypeResolver and work with a rich Type API for inspection and string casting.

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation** (unchanged):
   ```bash
   composer require symfony/type-info
   composer require phpstan/phpdoc-parser  # Required for raw string resolution
  1. First Use Case (updated for ObjectShapeType hardening): Resolve a property type from a class with stricter shape validation:

    use Symfony\Component\TypeInfo\TypeResolver;
    
    $resolver = TypeResolver::create();
    $type = $resolver->resolve(new \ReflectionProperty(User::class, 'address'));
    echo (string) $type; // Outputs: "array{street?: string, city?: string, zip?: string|null}"
    
  2. Key Entry Points (updated):

    • TypeResolver::create(): Now includes hardened ObjectShapeType validation.
    • Type::arrayShape(): Explicitly define strict object-like shapes (e.g., array{key?: type}).
    • (string) $type: Improved readability for nested shapes (e.g., array{items: list<int>}).

Implementation Patterns

1. Type Resolution Workflows (updated)

Reflection-Based Resolution with Shapes

$resolver = TypeResolver::create();
$shapeType = $resolver->resolve(new \ReflectionProperty(User::class, 'metadata'));
// Outputs: "array{theme?: string, preferences?: array{notifications?: bool}}"

String-Based Resolution for Shapes

$type = $resolver->resolve('array{id: int, name: string|null}');
$type->isArrayShape(); // true
$type->getShape()->get('name')->isNullable(); // true

Custom Resolvers (unchanged)

Extend TypeResolver or chain resolvers:

use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface;

class CustomResolver implements TypeResolverInterface {
    public function resolve($subject): Type {
        if ($subject instanceof \ReflectionClass) {
            return Type::object($subject->getName());
        }
        return TypeResolver::create()->resolve($subject);
    }
}

2. Type Composition (updated)

Object Shapes (new focus)

// Strict shape definition
$shape = Type::arrayShape([
    'id' => Type::int(),
    'name' => Type::string(),
    'metadata' => Type::nullable(Type::arrayShape([
        'created_at' => Type::string(),
    ])),
]);

// Runtime resolution
$resolver = TypeResolver::create();
$type = $resolver->resolve($shape);
echo (string) $type;
// Outputs: "array{id: int, name: string, metadata?: array{created_at: string}}"

Common Patterns (unchanged)

// Nullable types
$nullableInt = Type::nullable(Type::int());

// Lists/Arrays
$listOfStrings = Type::list(Type::string());
$arrayShape = Type::arrayShape(['id' => Type::int()]);

// Generics
$collectionType = Type::generic(Type::object(Collection::class), Type::int());

// Unions
$unionType = Type::union(Type::int(), Type::string());

3. Integration with Laravel (updated)

Validation Rules (updated for shapes)

use Illuminate\Validation\Rule;
use Symfony\Component\TypeInfo\TypeResolver;

class ShapeValidationRule extends Rule {
    protected $expectedShape;

    public function __construct(string $expectedShape) {
        $this->expectedShape = $expectedShape;
        $this->resolver = TypeResolver::create();
    }

    public function passes($attribute, $value) {
        $actualType = $this->resolver->resolve($value);
        $expectedType = $this->resolver->resolve($this->expectedShape);
        return $expectedType->isSatisfiedBy(fn(Type $t) =>
            $t->isArrayShape() &&
            $expectedType->getShape()->equals($t->getShape())
        );
    }
}

Dynamic Form Requests (updated)

use Illuminate\Foundation\Http\FormRequest;

class StoreUserRequest extends FormRequest {
    public function rules() {
        $resolver = TypeResolver::create();
        $shapeType = $resolver->resolve('array{name: string, age?: int}');
        return [
            'user' => ['required', new ShapeValidationRule((string) $shapeType)],
        ];
    }
}

API Response Transformation (updated)

use Symfony\Component\HttpFoundation\JsonResponse;

class ApiTransformer {
    public function transform($data, string $expectedShape): array {
        $resolver = TypeResolver::create();
        $type = $resolver->resolve($expectedShape);

        if (!$type->isArrayShape()) {
            throw new \InvalidArgumentException("Expected array shape type");
        }

        return array_map(function($item) use ($type) {
            return $this->castToShape($item, $type->getShape());
        }, $data);
    }

    protected function castToShape($item, Shape $shape): array {
        $result = [];
        foreach ($shape->getKeys() as $key) {
            $valueType = $shape->get($key);
            $result[$key] = $valueType->isNullable()
                ? ($item[$key] ?? null)
                : $item[$key];
        }
        return $result;
    }
}

4. Caching Strategies (unchanged)

Leverage Symfony’s caching component for performance:

use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\TypeInfo\TypeContextFactory;

$cache = new ArrayAdapter();
$contextFactory = new TypeContextFactory($cache);
$resolver = TypeResolver::create($contextFactory);

Gotchas and Tips

Pitfalls (updated)

  1. PHPDoc Parser Dependency (unchanged):

    • Raw string resolution requires phpstan/phpdoc-parser. Install explicitly:
      composer require phpstan/phpdoc-parser
      
  2. Object Shape Validation Hardening (new):

    • Strict Shape Matching: ObjectShapeType now enforces exact key presence/absence. Optional keys must use ? syntax:
      // Valid
      $shape = Type::arrayShape(['name?' => Type::string()]);
      
      // Invalid (throws error in v8.1.0+)
      $shape = Type::arrayShape(['name' => Type::nullable(Type::string())]);
      
    • Backward Compatibility: Existing shapes with nullable keys may fail. Update to explicit ? syntax:
      // Before (may fail)
      $shape = Type::arrayShape(['age' => Type::nullable(Type::int())]);
      
      // After (explicit)
      $shape = Type::arrayShape(['age?' => Type::int()]);
      
  3. Union Type Quirks (unchanged):

    • Nested unions may not merge as expected. Use Type::nullable() for optional values.
  4. Template Type Resolution (unchanged):

    • Generic classes require proper namespace resolution. Ensure autoloading:
      $type = Type::generic(Type::object(Collection::class), Type::int());
      
  5. Array vs. List (unchanged):

    • Prefer Type::list() for PHP 8.1+ typed arrays.

Debugging Tips (updated)

  1. Inspect Shape Structure: Use (string) $type for detailed shape output:

    $shape = Type::arrayShape(['user?' => Type::object(User::class)]);
    echo (string) $shape; // Outputs: "array{user?: App\Models\User}"
    
  2. Check Shape Satisfaction: Validate shapes against runtime data:

    $type->isSatisfiedBy(fn(Type $t) =>
        $t->isArrayShape() &&
        $t->getShape()->hasKey('id') &&
        $t->getShape()->get('id')->isIdentifiedBy(TypeIdentifier::INT)
    );
    
  3. Handle Edge Cases (updated):

    • Optional Keys: Use hasKey() and get() with null checks:
      if ($shape->hasKey('metadata') && $shape->get('metadata')->isNullable()) {
          $metadataType = $shape->get('metadata')->getInnerType();
      }
      
    • Mixed Shapes: Explicitly handle dynamic typing:
      if ($type->isIdentifiedBy(TypeIdentifier::ARRAY) && !$type->isArrayShape()) {
          // Handle non-shape arrays
      }
      

Extension Points (updated)

  1. Custom Shape Resolvers: Implement TypeResolverInterface for domain-specific shape resolution:

    class DatabaseShapeResolver implements TypeResolverInterface {
        public function resolve($subject): Type {
            if ($subject instanceof \ReflectionProperty) {
                $shape = $this->fetchShapeFromDatabase($subject->getName());
                return Type::arrayShape($shape);
            }
            return Type::mixed();
        }
    }
    
  2. Type Aliases (unchanged): Register custom aliases via `TypeContextFactory

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.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai