Symfony PropertyInfo 组件详解
PropertyInfo 是 Symfony 中用于获取类属性信息的强大组件,特别在类型推断方面非常实用。

基本安装
composer require symfony/property-info
核心功能
获取属性类型
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
// 创建提取器
$reflectionExtractor = new ReflectionExtractor();
$propertyInfo = new PropertyInfoExtractor(
[$reflectionExtractor], // 属性列表提取器
[$reflectionExtractor], // 类型提取器
[], // 可读性提取器
[], // 可写性提取器
[$reflectionExtractor] // 访问器提取器
);
// 获取属性类型
$types = $propertyInfo->getTypes(User::class, 'email');
foreach ($types as $type) {
echo $type->getBuiltinType(); // string
echo $type->isNullable(); // false
echo $type->getClassName(); // null for built-in types
}
结合 Doctrine 注解
// 安装 Doctrine 注解支持
composer require doctrine/annotations
// 获取 Doctrine 注解的实体信息
use Symfony\Component\PropertyInfo\Extractor\DoctrineExtractor;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
class UserService
{
private PropertyInfoExtractor $propertyInfo;
public function __construct(EntityManagerInterface $entityManager)
{
// Doctrine 提取器可以获取更多类型信息
$doctrineExtractor = new DoctrineExtractor($entityManager);
$this->propertyInfo = new PropertyInfoExtractor(
[$doctrineExtractor],
[$doctrineExtractor, new ReflectionExtractor()],
[$doctrineExtractor],
[$doctrineExtractor],
[$doctrineExtractor]
);
}
public function analyzeEntity(string $class): array
{
$properties = $this->propertyInfo->getProperties($class);
$info = [];
foreach ($properties as $property) {
$types = $this->propertyInfo->getTypes($class, $property);
$info[$property] = [
'types' => $types,
'readable' => $this->propertyInfo->isReadable($class, $property),
'writable' => $this->propertyInfo->isWritable($class, $property),
];
}
return $info;
}
}
高级用法示例
实体类型分析
// 示例实体
#[ORM\Entity]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private int $id;
#[ORM\Column(type: 'string', length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column(type: 'json')]
private array $roles = [];
#[ORM\ManyToOne(targetEntity: Company::class)]
private ?Company $company = null;
#[ORM\Column(type: 'datetime_immutable')]
private \DateTimeImmutable $createdAt;
// getters/setters...
}
// 类型分析服务
class TypeAnalyzer
{
private PropertyInfoExtractor $propertyInfo;
public function __construct(PropertyInfoExtractor $propertyInfo)
{
$this->propertyInfo = $propertyInfo;
}
public function getPropertyTypes(string $class, string $property): array
{
$types = $this->propertyInfo->getTypes($class, $property);
$result = [];
foreach ($types as $type) {
$typeInfo = [
'builtin_type' => $type->getBuiltinType(),
'nullable' => $type->isNullable(),
'class_name' => $type->getClassName(),
'collection' => $type->isCollection(),
];
// 处理集合类型
if ($type->isCollection() && $collectionKeyType = $type->getCollectionKeyType()) {
$typeInfo['collection_key_type'] = $collectionKeyType->getBuiltinType();
}
if ($type->isCollection() && $collectionValueType = $type->getCollectionValueType()) {
$typeInfo['collection_value_type'] = [
'builtin_type' => $collectionValueType->getBuiltinType(),
'class_name' => $collectionValueType->getClassName(),
];
}
$result[] = $typeInfo;
}
return $result;
}
public function getTypeCompatibility(string $class, string $property, mixed $value): bool
{
$types = $this->propertyInfo->getTypes($class, $property);
foreach ($types as $type) {
if ($type->isNullable() && $value === null) {
return true;
}
if ($this->isValueCompatible($type, $value)) {
return true;
}
}
return false;
}
private function isValueCompatible(Type $type, mixed $value): bool
{
$builtinType = $type->getBuiltinType();
return match ($builtinType) {
'string' => is_string($value),
'int' => is_int($value),
'float' => is_float($value),
'bool' => is_bool($value),
'array' => is_array($value),
'object' => $type->getClassName() ? is_a($value, $type->getClassName()) : is_object($value),
'resource' => is_resource($value),
default => false,
};
}
}
// 使用示例
$analyzer = new TypeAnalyzer($propertyInfo);
$types = $analyzer->getPropertyTypes(User::class, 'email');
// 输出: [['builtin_type' => 'string', 'nullable' => true, ...]]
$types = $analyzer->getPropertyTypes(User::class, 'company');
// 输出: [['builtin_type' => 'object', 'class_name' => 'App\Entity\Company', 'nullable' => true]]
在 API 平台中使用
// config/packages/api_platform.yaml
api_platform:
# 自动启用 property-info
defaults:
# ...
// 自定义序列化组
#[ApiResource(
normalizationContext: ['groups' => ['user:read']],
denormalizationContext: ['groups' => ['user:write']]
)]
class User
{
#[Groups(['user:read', 'user:write'])]
private string $email;
// ...
}
// 利用 property-info 进行字段验证
class UserValidator
{
private PropertyInfoExtractor $propertyInfo;
public function validateData(string $class, array $data): array
{
$errors = [];
foreach ($data as $property => $value) {
$types = $this->propertyInfo->getTypes($class, $property);
if (empty($types)) {
$errors[$property] = 'Unknown property';
continue;
}
if (!$this->isTypeValid($types, $value)) {
$errors[$property] = 'Invalid type for property';
}
}
return $errors;
}
private function isTypeValid(array $types, mixed $value): bool
{
foreach ($types as $type) {
// 检查 nullable
if ($type->isNullable() && $value === null) {
return true;
}
// 检查标量类型
if ($type->getBuiltinType() === gettype($value)) {
return true;
}
// 检查对象类型
if ($type->getBuiltinType() === 'object' && $type->getClassName()) {
if ($value instanceof $type->getClassName()) {
return true;
}
}
}
return false;
}
}
最佳实践
- 组合多个提取器:使用多个提取器可以获得更完整的类型信息
- 缓存类型信息:对于频繁使用的实体,缓存 type 信息提高性能
- 结合验证器:与 Symfony Validator 配合使用,实现更强大的数据验证
- 使用静态分析:结合 PHPStan 或 Psalm 进行代码分析
PropertyInfo 组件在表单构建、API 序列化、数据验证等场景都非常有用,能够显著提升代码的类型安全性。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播