Skip to content

[PropertyInfo] Remove PropertyInfo's Type #60726

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions UPGRADE-8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,21 @@ Console

* Add method `isSilent()` to `OutputInterface`

DoctrineBridge
--------------

* Remove the `DoctrineExtractor::getTypes()` method, use `DoctrineExtractor::getType()` instead

*Before*
```php
$types = $extractor->getTypes(Foo::class, 'property');
```

*After*
```php
$type = $extractor->getType(Foo::class, 'property');
```

HttpClient
----------

Expand All @@ -91,6 +106,107 @@ OptionsResolver
});
```

PropertyInfo
------------

* Remove the `PropertyTypeExtractorInterface::getTypes()` method, use `PropertyTypeExtractorInterface::getType()` instead

*Before*
```php
$types = $extractor->getTypes(Foo::class, 'property');
```

*After*
```php
$type = $extractor->getType(Foo::class, 'property');
```

* Remove the `ConstructorArgumentTypeExtractorInterface::getTypesFromConstructor()` method, use `ConstructorArgumentTypeExtractorInterface::getTypeFromConstructor()` instead

*Before*
```php
$types = $extractor->getTypesFromConstructor(Foo::class, 'property');
```

*After*
```php
$type = $extractor->getTypeFromConstructor(Foo::class, 'property');
```

* Remove the `Type` class, use `Symfony\Component\TypeInfo\Type` class from `symfony/type-info` instead

*Before*
```php
use Symfony\Component\PropertyInfo\Type;

// create types
$int = [new Type(Type::BUILTIN_TYPE_INT)];
$nullableString = [new Type(Type::BUILTIN_TYPE_STRING, true)];
$object = [new Type(Type::BUILTIN_TYPE_OBJECT, false, Foo::class)];
$boolList = [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(BUILTIN_TYPE_INT), new Type(BUILTIN_TYPE_BOOL))];
$union = [new Type(Type::BUILTIN_TYPE_STRING), new Type(BUILTIN_TYPE_INT)];
$intersection = [new Type(Type::BUILTIN_TYPE_OBJECT, false, \Traversable::class), new Type(Type::BUILTIN_TYPE_OBJECT, false, \Stringable::class)];

// test if a type is nullable
$intIsNullable = $int[0]->isNullable();

// echo builtin types of union
foreach ($union as $type) {
echo $type->getBuiltinType();
}

// test if a type represents an instance of \ArrayAccess
if ($object[0]->getClassName() instanceof \ArrayAccess::class) {
// ...
}

// handle collections
if ($boolList[0]->isCollection()) {
$k = $boolList->getCollectionKeyTypes();
$v = $boolList->getCollectionValueTypes();

// ...
}
```

*After*
```php
use Symfony\Component\TypeInfo\BuiltinType;
use Symfony\Component\TypeInfo\CollectionType;
use Symfony\Component\TypeInfo\Type;

// create types
$int = Type::int();
$nullableString = Type::nullable(Type::string());
$object = Type::object(Foo::class);
$boolList = Type::list(Type::bool());
$union = Type::union(Type::string(), Type::int());
$intersection = Type::intersection(Type::object(\Traversable::class), Type::object(\Stringable::class));

// test if a type is nullable
$intIsNullable = $int->isNullable();

// echo builtin types of union
foreach ($union->traverse() as $type) {
if ($type instanceof BuiltinType) {
echo $type->getTypeIdentifier()->value;
}
}

// test if a type represents an instance of \ArrayAccess
if ($object->isIdentifiedBy(\ArrayAccess::class)) {
// ...
}

// handle collections
if ($boolList instanceof CollectionType) {
$k = $boolList->getCollectionKeyType();
$v = $boolList->getCollectionValueType();

// ...
}
```

TwigBridge
----------

Expand Down
179 changes: 0 additions & 179 deletions src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\PropertyInfo\Type as LegacyType;
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\TypeIdentifier;

Expand Down Expand Up @@ -161,152 +160,6 @@ public function getType(string $class, string $property, array $context = []): ?
};
}

/**
* @deprecated since Symfony 7.3, use "getType" instead
*/
public function getTypes(string $class, string $property, array $context = []): ?array
{
trigger_deprecation('symfony/property-info', '7.3', 'The "%s()" method is deprecated, use "%s::getType()" instead.', __METHOD__, self::class);

if (null === $metadata = $this->getMetadata($class)) {
return null;
}

if ($metadata->hasAssociation($property)) {
$class = $metadata->getAssociationTargetClass($property);

if ($metadata->isSingleValuedAssociation($property)) {
if ($metadata instanceof ClassMetadata) {
$associationMapping = $metadata->getAssociationMapping($property);

$nullable = $this->isAssociationNullable($associationMapping);
} else {
$nullable = false;
}

return [new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, $nullable, $class)];
}

$collectionKeyType = LegacyType::BUILTIN_TYPE_INT;

if ($metadata instanceof ClassMetadata) {
$associationMapping = $metadata->getAssociationMapping($property);

if (self::getMappingValue($associationMapping, 'indexBy')) {
$subMetadata = $this->entityManager->getClassMetadata(self::getMappingValue($associationMapping, 'targetEntity'));

// Check if indexBy value is a property
$fieldName = self::getMappingValue($associationMapping, 'indexBy');
if (null === ($typeOfField = $subMetadata->getTypeOfField($fieldName))) {
$fieldName = $subMetadata->getFieldForColumn(self::getMappingValue($associationMapping, 'indexBy'));
// Not a property, maybe a column name?
if (null === ($typeOfField = $subMetadata->getTypeOfField($fieldName))) {
// Maybe the column name is the association join column?
$associationMapping = $subMetadata->getAssociationMapping($fieldName);

$indexProperty = $subMetadata->getSingleAssociationReferencedJoinColumnName($fieldName);
$subMetadata = $this->entityManager->getClassMetadata(self::getMappingValue($associationMapping, 'targetEntity'));

// Not a property, maybe a column name?
if (null === ($typeOfField = $subMetadata->getTypeOfField($indexProperty))) {
$fieldName = $subMetadata->getFieldForColumn($indexProperty);
$typeOfField = $subMetadata->getTypeOfField($fieldName);
}
}
}

if (!$collectionKeyType = $this->getTypeIdentifierLegacy($typeOfField)) {
return null;
}
}
}

return [new LegacyType(
LegacyType::BUILTIN_TYPE_OBJECT,
false,
Collection::class,
true,
new LegacyType($collectionKeyType),
new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, $class)
)];
}

if ($metadata instanceof ClassMetadata && isset($metadata->embeddedClasses[$property])) {
return [new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, false, self::getMappingValue($metadata->embeddedClasses[$property], 'class'))];
}

if ($metadata->hasField($property)) {
$typeOfField = $metadata->getTypeOfField($property);

if (!$builtinType = $this->getTypeIdentifierLegacy($typeOfField)) {
return null;
}

$nullable = $metadata instanceof ClassMetadata && $metadata->isNullable($property);

// DBAL 4 has a special fallback strategy for BINGINT (int -> string)
if (Types::BIGINT === $typeOfField && !method_exists(BigIntType::class, 'getName')) {
return [
new LegacyType(LegacyType::BUILTIN_TYPE_INT, $nullable),
new LegacyType(LegacyType::BUILTIN_TYPE_STRING, $nullable),
];
}

$enumType = null;
if (null !== $enumClass = self::getMappingValue($metadata->getFieldMapping($property), 'enumType') ?? null) {
$enumType = new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, $nullable, $enumClass);
}

switch ($builtinType) {
case LegacyType::BUILTIN_TYPE_OBJECT:
switch ($typeOfField) {
case Types::DATE_MUTABLE:
case Types::DATETIME_MUTABLE:
case Types::DATETIMETZ_MUTABLE:
case 'vardatetime':
case Types::TIME_MUTABLE:
return [new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, $nullable, 'DateTime')];

case Types::DATE_IMMUTABLE:
case Types::DATETIME_IMMUTABLE:
case Types::DATETIMETZ_IMMUTABLE:
case Types::TIME_IMMUTABLE:
return [new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, $nullable, 'DateTimeImmutable')];

case Types::DATEINTERVAL:
return [new LegacyType(LegacyType::BUILTIN_TYPE_OBJECT, $nullable, 'DateInterval')];
}

break;
case LegacyType::BUILTIN_TYPE_ARRAY:
switch ($typeOfField) {
case 'array': // DBAL < 4
case 'json_array': // DBAL < 3
// return null if $enumType is set, because we can't determine if collectionKeyType is string or int
if ($enumType) {
return null;
}

return [new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, $nullable, null, true)];

case Types::SIMPLE_ARRAY:
return [new LegacyType(LegacyType::BUILTIN_TYPE_ARRAY, $nullable, null, true, new LegacyType(LegacyType::BUILTIN_TYPE_INT), $enumType ?? new LegacyType(LegacyType::BUILTIN_TYPE_STRING))];
}
break;
case LegacyType::BUILTIN_TYPE_INT:
case LegacyType::BUILTIN_TYPE_STRING:
if ($enumType) {
return [$enumType];
}
break;
}

return [new LegacyType($builtinType, $nullable)];
}

return null;
}

public function isReadable(string $class, string $property, array $context = []): ?bool
{
return null;
Expand Down Expand Up @@ -396,38 +249,6 @@ private function getTypeIdentifier(string $doctrineType): ?TypeIdentifier
};
}

private function getTypeIdentifierLegacy(string $doctrineType): ?string
{
return match ($doctrineType) {
Types::SMALLINT,
Types::INTEGER => LegacyType::BUILTIN_TYPE_INT,
Types::FLOAT => LegacyType::BUILTIN_TYPE_FLOAT,
Types::BIGINT,
Types::STRING,
Types::TEXT,
Types::GUID,
Types::DECIMAL => LegacyType::BUILTIN_TYPE_STRING,
Types::BOOLEAN => LegacyType::BUILTIN_TYPE_BOOL,
Types::BLOB,
Types::BINARY => LegacyType::BUILTIN_TYPE_RESOURCE,
'object', // DBAL < 4
Types::DATE_MUTABLE,
Types::DATETIME_MUTABLE,
Types::DATETIMETZ_MUTABLE,
'vardatetime',
Types::TIME_MUTABLE,
Types::DATE_IMMUTABLE,
Types::DATETIME_IMMUTABLE,
Types::DATETIMETZ_IMMUTABLE,
Types::TIME_IMMUTABLE,
Types::DATEINTERVAL => LegacyType::BUILTIN_TYPE_OBJECT,
'array', // DBAL < 4
'json_array', // DBAL < 3
Types::SIMPLE_ARRAY => LegacyType::BUILTIN_TYPE_ARRAY,
default => null,
};
}

private static function getMappingValue(array|AssociationMapping|EmbeddedClassMapping|FieldMapping|JoinColumnMapping $mapping, string $key): mixed
{
if ($mapping instanceof AssociationMapping || $mapping instanceof EmbeddedClassMapping || $mapping instanceof FieldMapping || $mapping instanceof JoinColumnMapping) {
Expand Down
Loading
Loading