symfony/property-info
Symfony PropertyInfo component extracts metadata about PHP class properties—types, access, docs, and more—by reading popular sources like PHPDoc, reflection, and other metadata providers. Useful for serializers, validators, and API tooling.
Install via Composer:
composer require symfony/property-info
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
// Create extractor (combines multiple strategies)
$extractor = new PropertyInfoExtractor([
new PhpDocExtractor(), // Handles @var, @return, etc.
new ReflectionExtractor(), // Falls back to reflection
]);
// Get type info for a class property
$class = new \stdClass();
$propertyName = 'someProperty';
$typeInfo = $extractor->getTypes($class, $propertyName);
PropertyInfoExtractor: Main class combining multiple extractors.PhpDocExtractor: Parses PHPDoc annotations.ReflectionExtractor: Uses PHP reflection.PhpStanExtractor: Integrates with PHPStan for advanced type resolution.DoctrineExtractor: For Doctrine annotations.use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
// In a validator or form type
public function buildForm(FormBuilderInterface $builder, array $options)
{
$extractor = $this->container->get(PropertyInfoExtractorInterface::class);
$type = $extractor->getTypes($options['data'], 'propertyName')[0] ?? null;
if ($type === 'DateTime') {
$builder->add('propertyName', DateType::class);
}
}
// Map form fields based on property types
$extractor = new PropertyInfoExtractor([new PhpDocExtractor()]);
$types = $extractor->getTypes($entity, 'property');
$fieldType = match ($types[0] ?? null) {
'string' => TextType::class,
'integer' => IntegerType::class,
'boolean' => CheckboxType::class,
default => TextType::class,
};
// Convert entity to array with typed values
$extractor = new PropertyInfoExtractor([new ReflectionExtractor()]);
$types = $extractor->getTypes($entity, 'property');
$value = $entity->property;
$serialized = match ($types[0] ?? null) {
'DateTime' => $value->format('Y-m-d'),
'Collection' => array_map(fn($item) => $item->id, $value->toArray()),
default => $value,
};
use Symfony\Component\PropertyInfo\Extractor\ExtractorInterface;
class CustomExtractor implements ExtractorInterface
{
public function getTypes(object $object, string $property): array
{
// Custom logic (e.g., database schema, YAML config)
return ['App\\CustomType'];
}
}
// Register with the main extractor
$extractor = new PropertyInfoExtractor([
new CustomExtractor(),
new PhpDocExtractor(),
]);
Service Provider Binding
// In AppServiceProvider
public function register()
{
$this->app->bind(PropertyInfoExtractorInterface::class, function () {
return new PropertyInfoExtractor([
new PhpDocExtractor(),
new ReflectionExtractor(),
new PhpStanExtractor(), // Optional: if using PHPStan
]);
});
}
Dynamic Validation Rules
// In a Form Request
public function rules()
{
$extractor = app(PropertyInfoExtractorInterface::class);
$types = $extractor->getTypes($this->entity, 'attribute');
$rules = [];
foreach ($types as $type) {
if ($type === 'email') {
$rules['attribute'] = 'required|email';
} elseif ($type === 'date') {
$rules['attribute'] = 'required|date';
}
}
return $rules;
}
API Resource Transformation
// In a Fractal transformer
public function transform($entity)
{
$extractor = app(PropertyInfoExtractorInterface::class);
$types = $extractor->getTypes($entity, 'property');
return [
'property' => $this->formatValue($entity->property, $types[0] ?? null),
];
}
$cache = new \Symfony\Component\Cache\SimpleCache();
$extractor = new PropertyInfoExtractor([...], $cache);
ReflectionExtractor) before slower ones (e.g., PhpStanExtractor).Extractor Order Matters
PhpStanExtractor before PhpDocExtractor).PHPStan Conflicts
phpdocumentor/reflection-docblock v6+ causes conflicts.phpdocumentor/reflection-docblock:^5.0 or use the PhpStanExtractor carefully.
composer require phpdocumentor/reflection-docblock:^5.0
Promoted Properties
PhpStanExtractor or ensure @var tags are present.
class User {
public function __construct(
public string $name, // Promoted property
) {}
}
/**
* @var string
*/
public $name; // Explicit docblock fallback
Nullable Types
mixed[] or ?array may not resolve as expected.$type = str_replace('[]', '', $extractedType);
$type = str_replace('?', '', $type); // Handle nullable
Self/Parent Type Resolution
self or parent in DocBlocks may fail.PhpStanExtractor or resolve manually:
$reflectionClass = new \ReflectionClass($object);
$parentClass = $reflectionClass->getParentClass();
Underscore-Only Properties
__token may be skipped.ReflectionExtractor is included.Inspect Extracted Types
$extractor = new PropertyInfoExtractor([...]);
$types = $extractor->getTypes($object, 'property');
dump($types); // Debug output
Enable Verbose Logging
$extractor = new PropertyInfoExtractor([...], null, [
'debug' => true,
]);
Test Edge Cases
@var tags work.ArrayObject, Collection.Custom Type Resolvers
use Symfony\Component\PropertyInfo\Type;
$extractor = new PropertyInfoExtractor([...]);
$type = $extractor->getType($object, 'property');
if ($type instanceof Type && $type->getClassName() === 'App\\CustomType') {
// Handle custom logic
}
Override Default Extractors
$extractor = new PropertyInfoExtractor([
new class implements ExtractorInterface {
public function getTypes(object $object, string $property): array
{
return ['App\\OverriddenType'];
}
},
]);
Add Metadata Sources
use Doctrine\Common\Annotations\Reader;
$annotationReader = new Reader();
$extractor = new PropertyInfoExtractor([
new DoctrineExtractor($annotationReader),
]);
Leverage PropertyDescription
$description = $extractor->getProperties($object);
foreach ($description as $property => $info) {
if ($info->isWritable()) {
// Handle writable properties
}
}
hasMany, belongsTo relations may not resolve correctly.PhpStanExtractor or add customHow can I help you explore Laravel packages today?