diff --git a/UPGRADE.md b/UPGRADE.md index b99c034c8cd..339886657a9 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,3 +1,15 @@ +# Upgrade to 3.1 + +## Deprecate array access + +Use array access on instances of the following classes is deprecated: + +- `Doctrine\ORM\Mapping\DiscriminatorColumnMapping` +- `Doctrine\ORM\Mapping\EmbedClassMapping` +- `Doctrine\ORM\Mapping\FieldMapping` +- `Doctrine\ORM\Mapping\JoinColumnMapping` +- `Doctrine\ORM\Mapping\JoinTableMapping` + # Upgrade to 3.0 ## BC BREAK: `Doctrine\ORM\Proxy\Autoloader` no longer extends `Doctrine\Common\Proxy\Autoloader` diff --git a/src/Mapping/ArrayAccessImplementation.php b/src/Mapping/ArrayAccessImplementation.php index e740db8409e..3fd0988cf1b 100644 --- a/src/Mapping/ArrayAccessImplementation.php +++ b/src/Mapping/ArrayAccessImplementation.php @@ -4,6 +4,7 @@ namespace Doctrine\ORM\Mapping; +use Doctrine\Deprecations\Deprecation; use InvalidArgumentException; use function property_exists; @@ -14,12 +15,26 @@ trait ArrayAccessImplementation /** @param string $offset */ public function offsetExists(mixed $offset): bool { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/11211', + 'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.', + static::class, + ); + return isset($this->$offset); } /** @param string $offset */ public function offsetGet(mixed $offset): mixed { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/11211', + 'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.', + static::class, + ); + if (! property_exists($this, $offset)) { throw new InvalidArgumentException('Undefined property: ' . $offset); } @@ -30,12 +45,26 @@ public function offsetGet(mixed $offset): mixed /** @param string $offset */ public function offsetSet(mixed $offset, mixed $value): void { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/11211', + 'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.', + static::class, + ); + $this->$offset = $value; } /** @param string $offset */ public function offsetUnset(mixed $offset): void { + Deprecation::trigger( + 'doctrine/orm', + 'https://github.com/doctrine/orm/pull/11211', + 'Using ArrayAccess on %s is deprecated and will not be possible in Doctrine ORM 4.0. Use the corresponding property instead.', + static::class, + ); + $this->$offset = null; } } diff --git a/src/Persisters/Entity/JoinedSubclassPersister.php b/src/Persisters/Entity/JoinedSubclassPersister.php index 10eb31ec50b..76719a2c275 100644 --- a/src/Persisters/Entity/JoinedSubclassPersister.php +++ b/src/Persisters/Entity/JoinedSubclassPersister.php @@ -466,7 +466,7 @@ protected function getInsertColumnList(): array || isset($this->class->associationMappings[$name]->inherited) || ($this->class->isVersioned && $this->class->versionField === $name) || isset($this->class->embeddedClasses[$name]) - || isset($this->class->fieldMappings[$name]['notInsertable']) + || isset($this->class->fieldMappings[$name]->notInsertable) ) { continue; } @@ -519,9 +519,9 @@ protected function fetchVersionAndNotUpsertableValues(ClassMetadata $versionedCl $class = null; if ($this->class->isVersioned && $key === $versionedClass->versionField) { $class = $versionedClass; - } elseif (isset($column['generated'])) { - $class = isset($column['inherited']) - ? $this->em->getClassMetadata($column['inherited']) + } elseif (isset($column->generated)) { + $class = isset($column->inherited) + ? $this->em->getClassMetadata($column->inherited) : $this->class; } else { continue; diff --git a/src/Query/ResultSetMappingBuilder.php b/src/Query/ResultSetMappingBuilder.php index 2e81db7d71b..f28f3a9c005 100644 --- a/src/Query/ResultSetMappingBuilder.php +++ b/src/Query/ResultSetMappingBuilder.php @@ -134,7 +134,7 @@ protected function addAllClassFields(string $class, string $alias, array $column $this->addFieldResult($alias, $columnAlias, $propertyName); - $enumType = $classMetadata->getFieldMapping($propertyName)['enumType'] ?? null; + $enumType = $classMetadata->getFieldMapping($propertyName)->enumType ?? null; if (! empty($enumType)) { $this->addEnumResult($columnAlias, $enumType); } diff --git a/src/Tools/SchemaValidator.php b/src/Tools/SchemaValidator.php index 687fd1d36ba..43b10ee3983 100644 --- a/src/Tools/SchemaValidator.php +++ b/src/Tools/SchemaValidator.php @@ -343,7 +343,7 @@ function (FieldMapping $fieldMapping) use ($class): string|null { return null; } - $metadataFieldType = $this->findBuiltInType(Type::getType($fieldMapping['type'])); + $metadataFieldType = $this->findBuiltInType(Type::getType($fieldMapping->type)); //If the metadata field type is not a mapped built-in type, we cannot check it if ($metadataFieldType === null) { @@ -371,7 +371,7 @@ function (FieldMapping $fieldMapping) use ($class): string|null { ); } - if (! isset($fieldMapping['enumType']) || $propertyType === $fieldMapping['enumType']) { + if (! isset($fieldMapping->enumType) || $propertyType === $fieldMapping->enumType) { return null; } @@ -380,17 +380,17 @@ function (FieldMapping $fieldMapping) use ($class): string|null { $class->name, $fieldName, $propertyType, - $fieldMapping['enumType'], + $fieldMapping->enumType, ); } if ( - isset($fieldMapping['enumType']) - && $propertyType !== $fieldMapping['enumType'] + isset($fieldMapping->enumType) + && $propertyType !== $fieldMapping->enumType && interface_exists($propertyType) - && is_a($fieldMapping['enumType'], $propertyType, true) + && is_a($fieldMapping->enumType, $propertyType, true) ) { - $backingType = (string) (new ReflectionEnum($fieldMapping['enumType']))->getBackingType(); + $backingType = (string) (new ReflectionEnum($fieldMapping->enumType))->getBackingType(); if ($metadataFieldType === $backingType) { return null; @@ -400,14 +400,14 @@ function (FieldMapping $fieldMapping) use ($class): string|null { "The field '%s#%s' has the metadata enumType '%s' with a backing type of '%s' that differs from the metadata field type '%s'.", $class->name, $fieldName, - $fieldMapping['enumType'], + $fieldMapping->enumType, $backingType, $metadataFieldType, ); } if ( - $fieldMapping['type'] === 'json' + $fieldMapping->type === 'json' && in_array($propertyType, ['string', 'int', 'float', 'bool', 'true', 'false', 'null'], true) ) { return null; @@ -419,7 +419,7 @@ function (FieldMapping $fieldMapping) use ($class): string|null { $fieldName, $propertyType, $metadataFieldType, - $fieldMapping['type'], + $fieldMapping->type, ); }, $class->fieldMappings, diff --git a/src/UnitOfWork.php b/src/UnitOfWork.php index 37fcf71253c..f82a9afc055 100644 --- a/src/UnitOfWork.php +++ b/src/UnitOfWork.php @@ -997,7 +997,7 @@ public function recomputeSingleEntityChangeSet(ClassMetadata $class, object $ent foreach ($actualData as $propName => $actualValue) { $orgValue = $originalData[$propName] ?? null; - if (isset($class->fieldMappings[$propName]['enumType'])) { + if (isset($class->fieldMappings[$propName]->enumType)) { if (is_array($orgValue)) { foreach ($orgValue as $id => $val) { if ($val instanceof BackedEnum) { @@ -1267,16 +1267,16 @@ private function computeDeleteExecutionOrder(): array } $joinColumns = reset($assoc->joinColumns); - if (! isset($joinColumns['onDelete'])) { + if (! isset($joinColumns->onDelete)) { continue; } - $onDeleteOption = strtolower($joinColumns['onDelete']); + $onDeleteOption = strtolower($joinColumns->onDelete); if ($onDeleteOption !== 'cascade') { continue; } - $targetEntity = $class->getFieldValue($entity, $assoc['fieldName']); + $targetEntity = $class->getFieldValue($entity, $assoc->fieldName); // If the association does not refer to another entity or that entity // is not to be deleted, there is no ordering problem and we can diff --git a/tests/Tests/ORM/Functional/Ticket/GH11135Test.php b/tests/Tests/ORM/Functional/Ticket/GH11135Test.php index 1afc872fd2d..ae514cfaa4f 100644 --- a/tests/Tests/ORM/Functional/Ticket/GH11135Test.php +++ b/tests/Tests/ORM/Functional/Ticket/GH11135Test.php @@ -25,8 +25,8 @@ public function testOverrideInheritsDeclaringClass(): void $cm1 = $this->_em->getClassMetadata(GH11135EntityWithOverride::class); $cm2 = $this->_em->getClassMetadata(GH11135EntityWithoutOverride::class); - self::assertSame($cm1->getFieldMapping('id')['declared'], $cm2->getFieldMapping('id')['declared']); - self::assertSame($cm1->getAssociationMapping('ref')['declared'], $cm2->getAssociationMapping('ref')['declared']); + self::assertSame($cm1->getFieldMapping('id')->declared, $cm2->getFieldMapping('id')->declared); + self::assertSame($cm1->getAssociationMapping('ref')->declared, $cm2->getAssociationMapping('ref')->declared); } } diff --git a/tests/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Tests/ORM/Mapping/ClassMetadataTest.php index edd05959408..1ce384b5249 100644 --- a/tests/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Tests/ORM/Mapping/ClassMetadataTest.php @@ -859,8 +859,7 @@ public function testAttributeOverrideKeepsDeclaringClass(): void $mapping = $cm->getFieldMapping('id'); - self::assertArrayHasKey('declared', $mapping); - self::assertSame(AbstractContentItem::class, $mapping['declared']); + self::assertSame(AbstractContentItem::class, $mapping->declared); } public function testAssociationOverrideKeepsDeclaringClass(): void diff --git a/tests/Tests/ORM/Mapping/XmlMappingDriverTest.php b/tests/Tests/ORM/Mapping/XmlMappingDriverTest.php index 86e6fde2cf5..5426da45c91 100644 --- a/tests/Tests/ORM/Mapping/XmlMappingDriverTest.php +++ b/tests/Tests/ORM/Mapping/XmlMappingDriverTest.php @@ -297,8 +297,8 @@ public function testClassNameInFieldOrId(): void /** @var array{type: string} $name */ $name = $class->getFieldMapping('name'); - self::assertEquals(ProjectId::class, $id['type']); - self::assertEquals(ProjectName::class, $name['type']); + self::assertEquals(ProjectId::class, $id->type); + self::assertEquals(ProjectName::class, $name->type); } public function testDisablingXmlValidationIsPossible(): void