diff --git a/composer.json b/composer.json
index 810c2f06a22..ed7615765ce 100644
--- a/composer.json
+++ b/composer.json
@@ -43,7 +43,7 @@
"doctrine/annotations": "^1.13",
"doctrine/coding-standard": "^9.0",
"phpbench/phpbench": "^0.16.10 || ^1.0",
- "phpstan/phpstan": "1.4.0",
+ "phpstan/phpstan": "1.4.1",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
"squizlabs/php_codesniffer": "3.6.2",
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
diff --git a/docs/en/cookbook/accessing-private-properties-of-the-same-class-from-different-instance.rst b/docs/en/cookbook/accessing-private-properties-of-the-same-class-from-different-instance.rst
new file mode 100644
index 00000000000..9730a883478
--- /dev/null
+++ b/docs/en/cookbook/accessing-private-properties-of-the-same-class-from-different-instance.rst
@@ -0,0 +1,74 @@
+Accessing private/protected properties/methods of the same class from different instance
+========================================================================================
+
+.. sectionauthor:: Michael Olsavsky (olsavmic)
+
+As explained in the :doc:`restrictions for entity classes in the manual <../reference/architecture>`,
+it is dangerous to access private/protected properties of different entity instance of the same class because of lazy loading.
+
+The proxy instance that's injected instead of the real entity may not be initialized yet
+and therefore not contain expected data which may result in unexpected behavior.
+That's a limitation of current proxy implementation - only public methods automatically initialize proxies.
+
+It is usually preferable to use a public interface to manipulate the object from outside the `$this`
+context but it may not be convenient in some cases. The following example shows how to do it safely.
+
+Safely accessing private properties from different instance of the same class
+-----------------------------------------------------------------------------
+
+To safely access private property of different instance of the same class, make sure to initialise
+the proxy before use manually as follows:
+
+.. code-block:: php
+
+ parent instanceof Proxy) {
+ $this->parent->__load();
+ }
+
+ // Accessing the `$this->parent->name` property without loading the proxy first
+ // may throw error in case the Proxy has not been initialized yet.
+ $this->parent->name;
+ }
+
+ public function doSomethingWithAnotherInstance(self $instance)
+ {
+ // Always initializing the proxy before use
+ if ($instance instanceof Proxy) {
+ $instance->__load();
+ }
+
+ // Accessing the `$instance->name` property without loading the proxy first
+ // may throw error in case the Proxy has not been initialized yet.
+ $instance->name;
+ }
+
+ // ...
+ }
diff --git a/docs/en/reference/architecture.rst b/docs/en/reference/architecture.rst
index 6c005e43298..b41b67445b7 100644
--- a/docs/en/reference/architecture.rst
+++ b/docs/en/reference/architecture.rst
@@ -93,6 +93,7 @@ be any regular PHP class observing the following restrictions:
- An entity cannot make use of func_get_args() to implement variable parameters.
Generated proxies do not support this for performance reasons and your code might
actually fail to work when violating this restriction.
+- Entity cannot access private/protected properties/methods of another entity of the same class or :doc:`do so safely <../cookbook/accessing-private-properties-of-the-same-class-from-different-instance>`.
Entities support inheritance, polymorphic associations, and
polymorphic queries. Both abstract and concrete classes can be
diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php
index 5cb93961cbc..b92a6be5853 100644
--- a/lib/Doctrine/ORM/Tools/SchemaTool.php
+++ b/lib/Doctrine/ORM/Tools/SchemaTool.php
@@ -781,12 +781,18 @@ private function gatherRelationJoinColumns(
*/
private function gatherColumnOptions(array $mapping): array
{
- if (! isset($mapping['options'])) {
+ $mappingOptions = $mapping['options'] ?? [];
+
+ if (isset($mapping['enumType'])) {
+ $mappingOptions['enumType'] = $mapping['enumType'];
+ }
+
+ if (empty($mappingOptions)) {
return [];
}
- $options = array_intersect_key($mapping['options'], array_flip(self::KNOWN_COLUMN_OPTIONS));
- $options['customSchemaOptions'] = array_diff_key($mapping['options'], $options);
+ $options = array_intersect_key($mappingOptions, array_flip(self::KNOWN_COLUMN_OPTIONS));
+ $options['customSchemaOptions'] = array_diff_key($mappingOptions, $options);
return $options;
}
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 3cc10fe3f93..7567e69a26a 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -703,8 +703,9 @@
self::GENERATOR_TYPE_UUID
-
+
canEmulateSchemas
+ canRequireSQLConversion
$this->columnNames
diff --git a/tests/Doctrine/Tests/ORM/Tools/SchemaToolTest.php b/tests/Doctrine/Tests/ORM/Tools/SchemaToolTest.php
index 5faa3146ece..4dd7fcf0ad4 100644
--- a/tests/Doctrine/Tests/ORM/Tools/SchemaToolTest.php
+++ b/tests/Doctrine/Tests/ORM/Tools/SchemaToolTest.php
@@ -35,6 +35,8 @@
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedChildClass;
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedIdentityClass;
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedRootClass;
+use Doctrine\Tests\Models\Enums\Card;
+use Doctrine\Tests\Models\Enums\Suit;
use Doctrine\Tests\Models\Forum\ForumAvatar;
use Doctrine\Tests\Models\Forum\ForumUser;
use Doctrine\Tests\Models\NullDefault\NullDefaultColumn;
@@ -189,6 +191,23 @@ public function testNullDefaultNotAddedToCustomSchemaOptions(): void
self::assertSame([], $customSchemaOptions);
}
+ /**
+ * @requires PHP 8.1
+ */
+ public function testEnumTypeAddedToCustomSchemaOptions(): void
+ {
+ $em = $this->getTestEntityManager();
+ $schemaTool = new SchemaTool($em);
+
+ $customSchemaOptions = $schemaTool->getSchemaFromMetadata([$em->getClassMetadata(Card::class)])
+ ->getTable('Card')
+ ->getColumn('suit')
+ ->getCustomSchemaOptions();
+
+ self::assertArrayHasKey('enumType', $customSchemaOptions);
+ self::assertSame(Suit::class, $customSchemaOptions['enumType']);
+ }
+
/**
* @group DDC-3671
*/