diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Session.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Session.php index 421bef1d9514..0779b3149e4d 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Generic/Session.php +++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Session.php @@ -22,6 +22,11 @@ */ class Session implements \TYPO3\CMS\Core\SingletonInterface { + /** + * @var \TYPO3\CMS\Extbase\Object\Container\Container + */ + protected $objectContainer; + /** * Reconstituted objects * @@ -42,8 +47,9 @@ class Session implements \TYPO3\CMS\Core\SingletonInterface /** * Constructs a new Session */ - public function __construct() + public function __construct(\TYPO3\CMS\Extbase\Object\Container\Container $container) { + $this->objectContainer = $container; $this->reconstitutedEntities = new ObjectStorage(); $this->objectMap = new ObjectStorage(); } @@ -128,7 +134,7 @@ public function hasObject($object) */ public function hasIdentifier($identifier, $className) { - return isset($this->identifierMap[strtolower($className)][$identifier]); + return isset($this->identifierMap[$this->getClassIdentifier($className)][$identifier]); } /** @@ -140,7 +146,7 @@ public function hasIdentifier($identifier, $className) */ public function getObjectByIdentifier($identifier, $className) { - return $this->identifierMap[strtolower($className)][$identifier]; + return $this->identifierMap[$this->getClassIdentifier($className)][$identifier]; } /** @@ -168,7 +174,7 @@ public function getIdentifierByObject($object) public function registerObject($object, $identifier) { $this->objectMap[$object] = $identifier; - $this->identifierMap[strtolower(get_class($object))][$identifier] = $object; + $this->identifierMap[$this->getClassIdentifier(get_class($object))][$identifier] = $object; } /** @@ -178,7 +184,7 @@ public function registerObject($object, $identifier) */ public function unregisterObject($object) { - unset($this->identifierMap[strtolower(get_class($object))][$this->objectMap[$object]]); + unset($this->identifierMap[$this->getClassIdentifier(get_class($object))][$this->objectMap[$object]]); $this->objectMap->detach($object); } @@ -192,4 +198,16 @@ public function destroy() $this->objectMap = new ObjectStorage(); $this->reconstitutedEntities = new ObjectStorage(); } + + /** + * Objects are stored in the cache with their implementation class name + * to allow reusing instances of different classes that point to the same implementation + * + * @param string $className + * @return string a unique class identifier respecting configured implementation class names + */ + protected function getClassIdentifier($className): string + { + return strtolower($this->objectContainer->getImplementationClassName($className)); + } } diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/Classes/Domain/Model/A.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/Classes/Domain/Model/A.php new file mode 100644 index 000000000000..84d98cc65440 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/Classes/Domain/Model/A.php @@ -0,0 +1,44 @@ +a; + } + + /** + * @param string $a + */ + public function setA(string $a): void + { + $this->a = $a; + } +} diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/Classes/Domain/Repository/ARepository.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/Classes/Domain/Repository/ARepository.php new file mode 100644 index 000000000000..dcb42178ee3a --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/Classes/Domain/Repository/ARepository.php @@ -0,0 +1,26 @@ + [ + 'label' => 'uid', + 'title' => 'A', + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + ], + 'interface' => [ + 'showRecordFieldList' => 'title' + ], + 'columns' => [ + 'a' => [ + 'label' => 'a', + 'config' => [ + 'type' => 'input', + 'size' => 25, + 'max' => 255, + ] + ], + ], + 'types' => [ + '1' => ['showitem' => '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,a'] + ], +]; diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/ext_emconf.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/ext_emconf.php new file mode 100644 index 000000000000..bdd1dfedd11e --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/ext_emconf.php @@ -0,0 +1,21 @@ + '', + 'description' => '', + 'category' => 'example', + 'author' => '', + 'author_company' => '', + 'author_email' => '', + 'state' => 'stable', + 'uploadfolder' => 0, + 'createDirs' => '', + 'clearCacheOnLoad' => 1, + 'version' => '10.0.0', + 'constraints' => [ + 'depends' => [ + 'typo3' => '10.0.0', + ], + 'conflicts' => [], + 'suggests' => [], + ], +]; diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/ext_localconf.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/ext_localconf.php new file mode 100644 index 000000000000..1d514a659b3d --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/a/ext_localconf.php @@ -0,0 +1,2 @@ +b; + } + + /** + * @param string $b + */ + public function setB(string $b): void + { + $this->b = $b; + } +} diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/Classes/Domain/Repository/BRepository.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/Classes/Domain/Repository/BRepository.php new file mode 100644 index 000000000000..0b60d2d8c117 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/Classes/Domain/Repository/BRepository.php @@ -0,0 +1,26 @@ + [ + 'label' => 'b', + 'config' => [ + 'type' => 'input', + 'size' => 25, + 'max' => 255, + ] + ], + ] +); + +\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes('tx_a_domain_model_a', 'b'); diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_emconf.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_emconf.php new file mode 100644 index 000000000000..bdd1dfedd11e --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_emconf.php @@ -0,0 +1,21 @@ + '', + 'description' => '', + 'category' => 'example', + 'author' => '', + 'author_company' => '', + 'author_email' => '', + 'state' => 'stable', + 'uploadfolder' => 0, + 'createDirs' => '', + 'clearCacheOnLoad' => 1, + 'version' => '10.0.0', + 'constraints' => [ + 'depends' => [ + 'typo3' => '10.0.0', + ], + 'conflicts' => [], + 'suggests' => [], + ], +]; diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_localconf.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_localconf.php new file mode 100644 index 000000000000..aaa0e91b1361 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_localconf.php @@ -0,0 +1,8 @@ +registerImplementation( + \ExtbaseTeam\A\Domain\Model\A::class, + \ExtbaseTeam\B\Domain\Model\B::class +); diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_tables.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_tables.php new file mode 100644 index 000000000000..1d514a659b3d --- /dev/null +++ b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/ext_tables.php @@ -0,0 +1,2 @@ +objectManager = GeneralUtility::makeInstance(ObjectManager::class); + + $this->importCSVDataSet(GeneralUtility::getFileAbsFileName( + 'typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/class_overriding/b/tx_a_domain_model_a.csv' + )); + } + + /** + * @test + */ + public function testARepositoryObjectsAreTakenFromSession(): void + { + $aRepository = $this->objectManager->get(\ExtbaseTeam\A\Domain\Model\ARepository::class); + $a1 = $aRepository->findByUid(1); + $a2 = $aRepository->findByUid(1); + + $this->assertSame($a1, $a2); + } + + /** + * @test + */ + public function testBRepositoryObjectsAreTakenFromSession(): void + { + $bRepository = $this->objectManager->get(\ExtbaseTeam\B\Domain\Model\BRepository::class); + $b1 = $bRepository->findByUid(1); + $b2 = $bRepository->findByUid(1); + + $this->assertSame($b1, $b2); + } +} diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php index 6464742e9d3b..107995265dd2 100644 --- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php @@ -16,6 +16,7 @@ */ use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface; +use TYPO3\CMS\Extbase\Object\Container\Container; use TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException; use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap; use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper; @@ -248,7 +249,7 @@ public function mapObjectToClassPropertyReturnsExistingObjectWithoutCallingFetch $classSchema1 = new ClassSchema(Fixture\DummyParentEntity::class); $identifier = 1; - $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session(); + $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session(new Container()); $session->registerObject($child, $identifier); $mockReflectionService = $this->getMockBuilder(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class) diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/PersistenceManagerTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/PersistenceManagerTest.php index 3aa26b27f620..7954678cb7ee 100644 --- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/PersistenceManagerTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/PersistenceManagerTest.php @@ -16,6 +16,7 @@ */ use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; +use TYPO3\CMS\Extbase\Object\Container\Container; use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; use TYPO3\CMS\Extbase\Persistence\Generic\Backend; use TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface; @@ -288,7 +289,7 @@ class ' . $className . 'Repository extends \\TYPO3\\CMS\\Extbase\\Persistence\\ PersistenceManager::class, ['dummy'] ); - $session = new Session(); + $session = new Session(new Container()); $changedEntities = new ObjectStorage(); $entity1 = new $classNameWithNamespace(); /** @var RepositoryInterface|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $repository */ diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/SessionTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/SessionTest.php index e9d2afebe96e..1fa5881b5fef 100644 --- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/SessionTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/SessionTest.php @@ -16,6 +16,7 @@ */ use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; +use TYPO3\CMS\Extbase\Object\Container\Container; use TYPO3\CMS\Extbase\Persistence\Generic\Session; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; @@ -27,7 +28,7 @@ class SessionTest extends UnitTestCase public function objectRegisteredWithRegisterReconstitutedEntityCanBeRetrievedWithGetReconstitutedEntities() { $someObject = new \ArrayObject([]); - $session = new Session(); + $session = new Session(new Container()); $session->registerReconstitutedEntity($someObject); $ReconstitutedEntities = $session->getReconstitutedEntities(); @@ -40,7 +41,7 @@ public function objectRegisteredWithRegisterReconstitutedEntityCanBeRetrievedWit public function unregisterReconstitutedEntityRemovesObjectFromSession() { $someObject = new \ArrayObject([]); - $session = new Session(); + $session = new Session(new Container()); $session->registerObject($someObject, 'fakeUuid'); $session->registerReconstitutedEntity($someObject); $session->unregisterReconstitutedEntity($someObject); @@ -56,7 +57,7 @@ public function hasObjectReturnsTrueForRegisteredObject() { $object1 = new \stdClass(); $object2 = new \stdClass(); - $session = new Session(); + $session = new Session(new Container()); $session->registerObject($object1, 12345); $this->assertTrue($session->hasObject($object1), 'Session claims it does not have registered object.'); @@ -68,7 +69,7 @@ public function hasObjectReturnsTrueForRegisteredObject() */ public function hasIdentifierReturnsTrueForRegisteredObject() { - $session = new Session(); + $session = new Session(new Container()); $session->registerObject(new \stdClass(), 12345); $this->assertTrue($session->hasIdentifier('12345', 'stdClass'), 'Session claims it does not have registered object.'); @@ -81,7 +82,7 @@ public function hasIdentifierReturnsTrueForRegisteredObject() public function getIdentifierByObjectReturnsRegisteredUUIDForObject() { $object = new \stdClass(); - $session = new Session(); + $session = new Session(new Container()); $session->registerObject($object, 12345); $this->assertEquals($session->getIdentifierByObject($object), 12345, 'Did not get UUID registered for object.'); @@ -93,7 +94,7 @@ public function getIdentifierByObjectReturnsRegisteredUUIDForObject() public function getObjectByIdentifierReturnsRegisteredObjectForUUID() { $object = new \stdClass(); - $session = new Session(); + $session = new Session(new Container()); $session->registerObject($object, 12345); $this->assertSame($session->getObjectByIdentifier('12345', 'stdClass'), $object, 'Did not get object registered for UUID.'); @@ -106,7 +107,7 @@ public function unregisterObjectRemovesRegisteredObject() { $object1 = new \stdClass(); $object2 = new \stdClass(); - $session = new Session(); + $session = new Session(new Container()); $session->registerObject($object1, 12345); $session->registerObject($object2, 67890); @@ -128,7 +129,7 @@ public function unregisterObjectRemovesRegisteredObject() */ public function newSessionIsEmpty() { - $persistenceSession = new Session(); + $persistenceSession = new Session(new Container()); $reconstitutedObjects = $persistenceSession->getReconstitutedEntities(); $this->assertEquals(0, count($reconstitutedObjects), 'The reconstituted objects storage was not empty.'); } @@ -138,7 +139,7 @@ public function newSessionIsEmpty() */ public function objectCanBeRegisteredAsReconstituted() { - $persistenceSession = new Session(); + $persistenceSession = new Session(new Container()); $entity = $this->createMock(AbstractEntity::class); $persistenceSession->registerReconstitutedEntity($entity); $reconstitutedObjects = $persistenceSession->getReconstitutedEntities(); @@ -150,7 +151,7 @@ public function objectCanBeRegisteredAsReconstituted() */ public function objectCanBeUnregisteredAsReconstituted() { - $persistenceSession = new Session(); + $persistenceSession = new Session(new Container()); $entity = $this->createMock(AbstractEntity::class); $persistenceSession->registerReconstitutedEntity($entity); $persistenceSession->unregisterReconstitutedEntity($entity);