From 0be9be4e24b0a3a71cd1b4ddd37a2ac65db2842d Mon Sep 17 00:00:00 2001 From: Emiel Nijpels Date: Thu, 24 Sep 2015 10:26:10 +0200 Subject: [PATCH] DDC-3146 remove event listener from event listener in abstract hydrator in cleanup function --- .../Internal/Hydration/AbstractHydrator.php | 3 + .../ORM/Functional/Ticket/DDC3146Test.php | 65 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3146Test.php diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index af232d1ff..e6662b799 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -210,6 +210,9 @@ abstract class AbstractHydrator $this->_rsm = null; $this->_cache = array(); $this->_metadataCache = array(); + + $evm = $this->_em->getEventManager(); + $evm->removeEventListener(array(Events::onClear), $this); } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3146Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3146Test.php new file mode 100644 index 000000000..7e09642c2 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3146Test.php @@ -0,0 +1,65 @@ + + */ +class DDC3146Test extends \Doctrine\Tests\OrmFunctionalTestCase +{ + /** + * Verify that the number of added events to the event listener from the abstract hydrator class is equal to the number of removed events + */ + public function testEventListeners() + { + // Create mock connection to be returned from the entity manager interface + $mockConnection = $this->getMockBuilder('Doctrine\DBAL\Connection')->disableOriginalConstructor()->getMock(); + $mockEntityManagerInterface = $this->getMockBuilder('Doctrine\ORM\EntityManagerInterface')->disableOriginalConstructor()->getMock(); + $mockEntityManagerInterface->expects($this->any())->method('getConnection')->will($this->returnValue($mockConnection)); + + // Create mock event manager to be returned from the entity manager interface + $mockEventManager = $this->getMockBuilder('Doctrine\Common\EventManager')->disableOriginalConstructor()->getMock(); + $mockEntityManagerInterface->expects($this->any())->method('getEventManager')->will($this->returnValue($mockEventManager)); + + // Create mock statement and result mapping + $mockStatement = $this->getMockBuilder('Doctrine\DBAL\Driver\Statement')->disableOriginalConstructor()->getMock(); + $mockStatement->expects($this->once())->method('fetch')->will($this->returnValue(false)); + $mockResultMapping = $this->getMockBuilder('Doctrine\ORM\Query\ResultSetMapping')->disableOriginalConstructor()->getMock(); + + // Create mock abstract hydrator + $mockAbstractHydrator = $this->getMockBuilder('Doctrine\ORM\Internal\Hydration\AbstractHydrator') + ->setConstructorArgs(array($mockEntityManagerInterface)) + ->setMethods(array('hydrateAllData')) + ->getMock(); + + // Increase counter every time the event listener is added and decrease the counter every time the event listener is removed + $eventCounter = 0; + $mockEventManager->expects($this->any()) + ->method('addEventListener') + ->will( + $this->returnCallback( + function () use (&$eventCounter) { + $eventCounter++; + } + ) + ); + + $mockEventManager->expects($this->any()) + ->method('removeEventListener') + ->will( + $this->returnCallback( + function () use (&$eventCounter) { + $eventCounter--; + } + ) + ); + + // Create iterable result + $iterableResult = $mockAbstractHydrator->iterate($mockStatement, $mockResultMapping, array()); + $iterableResult->next(); + + // Number of added events listeners should be equal or less than the number of removed events + $this->assertLessThanOrEqual(0, $eventCounter, 'More events added to the event listener than removed; this can create a memory leak when references are not cleaned up'); + } +}