Skip to content

Loading…

DDC-842: spl_object_hash tries to get hash from array - fails #5363

Closed
doctrinebot opened this Issue · 5 comments

1 participant

@doctrinebot

Jira issue originally created by user sebastian.hoitz:

I have a model which looks like this:

Class A
protected $collectionB = array(); // Holds many Class B instances
protected $collectionC = array(); // Holds many Class C instances

Class B
protected $classC; // Holds one Class C instance

Class C
protected $attributes; // Holds many attributes which are stored in another entitty with SINGLE_TABLE discriminator

When I try to persist my object to the database, Doctrine causes this error:

Message: spl*object*hash() expects parameter 1 to be object, array given
File: C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt_api\trunk\library\Doctrine\ORM\UnitOfWork.php
Line: 2043
Trace:

#0 [internal function]: PHPUnit*Util_ErrorHandler::handleError(2, 'spl_object*hash...', 'C:\Users\Sebast...', 2043, Array)
#1 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt*api\trunk\library\Doctrine\ORM\UnitOfWork.php(2043): spl_object*hash(Array)
#2 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt_api\trunk\library\Doctrine\ORM\Persisters\ManyToManyPersister.php(110): Doctrine\ORM\UnitOfWork->getEntityIdentifier(Array)
#3 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt*api\trunk\library\Doctrine\ORM\Persisters\ManyToManyPersister.php(92): Doctrine\ORM\Persisters\ManyToManyPersister->*collectJoinTableColumnParameters(Object(Doctrine\ORM\PersistentCollection), Array)
#4 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt*api\trunk\library\Doctrine\ORM\Persisters\AbstractCollectionPersister.php(124): Doctrine\ORM\Persisters\ManyToManyPersister->*getInsertRowSQLParameters(Object(Doctrine\ORM\PersistentCollection), Array)
#5 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt_api\trunk\library\Doctrine\ORM\Persisters\AbstractCollectionPersister.php(104): Doctrine\ORM\Persisters\AbstractCollectionPersister->insertRows(Object(Doctrine\ORM\PersistentCollection))
#6 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt_api\trunk\library\Doctrine\ORM\UnitOfWork.php(303): Doctrine\ORM\Persisters\AbstractCollectionPersister->update(Object(Doctrine\ORM\PersistentCollection))
#7 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt_api\trunk\library\Doctrine\ORM\EntityManager.php(320): Doctrine\ORM\UnitOfWork->commit()
#8 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt_models\trunk\library\App\Model\Service\Dao\Doctrine.php(26): Doctrine\ORM\EntityManager->flush()
#9 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt*models\trunk\library\App\Model\Service\Abstract.php(54): App_Model_Service_Dao_Doctrine->save(Object(App_Model*Ticket))
#10 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt*models\trunk\library\App\Model\Abstract.php(264): App_Model_Service_Abstract->save(Object(App_Model*Ticket))
#11 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt*models\trunk\library\App\Model\Ticket.php(75): App_Model*Abstract->save()
#12 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt*api\trunk\application\controllers\TicketController.php(69): App_Model*Ticket->save()
#13 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt_api\trunk\library\Zend\Controller\Action.php(513): TicketController->putAction()
#14 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt*api\trunk\library\Zend\Controller\Dispatcher\Standard.php(295): Zend_Controller*Action->dispatch('putAction')
#15 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt*api\trunk\library\Zend\Controller\Front.php(954): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_HttpTestCase), Object(Zend_Controller_Response*HttpTestCase))
#16 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt*api\trunk\library\Zend\Test\PHPUnit\ControllerTestCase.php(199): Zend_Controller*Front->dispatch()
#17 C:\Users\Sebastian Hoitz\Documents\Entwicklung\kt*api\trunk\tests\application\controllers\TicketControllerTest.php(220): Zend_Test_PHPUnit*ControllerTestCase->dispatch('/ticket/1')
#18 [internal function]: TicketControllerTest->testUpdateTicketWithoutInvolvedContactsDoesNotCauseException()
#19 C:\Program Files (x86)\PHP\PEAR\pear\PHPUnit\Framework\TestCase.php(769): ReflectionMethod->invokeArgs(Object(TicketControllerTest), Array)
#20 C:\Program Files (x86)\PHP\PEAR\pear\PHPUnit\Framework\TestCase.php(659): PHPUnit*Framework*TestCase->runTest()
#21 C:\Program Files (x86)\PHP\PEAR\pear\PHPUnit\Framework\TestResult.php(617): PHPUnit*Framework*TestCase->runBare()
#22 C:\Program Files (x86)\PHP\PEAR\pear\PHPUnit\Framework\TestCase.php(607): PHPUnit*Framework*TestResult->run(Object(TicketControllerTest))
#23 C:\Program Files (x86)\PHP\PEAR\pear\PHPUnit\Framework\TestSuite.php(751): PHPUnit*Framework_TestCase->run(Object(PHPUnit_Framework*TestResult))
#24 C:\Program Files (x86)\PHP\PEAR\pear\PHPUnit\Framework\TestSuite.php(727): PHPUnit*Framework_TestSuite->runTest(Object(TicketControllerTest), Object(PHPUnit_Framework*TestResult))
#25 C:\Program Files (x86)\PHP\PEAR\pear\PHPUnit\Framework\TestSuite.php(687): PHPUnit*Framework_TestSuite->run(Object(PHPUnit_Framework*TestResult), false, Array, Array, false)
#26 C:\Program Files (x86)\PHP\PEAR\pear\PHPUnit\TextUI\TestRunner.php(305): PHPUnit*Framework_TestSuite->run(Object(PHPUnit_Framework*TestResult), false, Array, Array, false)
#27 C:\Program Files (x86)\PHP\PEAR\pear\PHPUnit\TextUI\Command.php(188): PHPUnit*TextUI_TestRunner->doRun(Object(PHPUnit_Framework*TestSuite), Array)
#28 C:\Program Files (x86)\PHP\PEAR\pear\PHPUnit\TextUI\Command.php(129): PHPUnit*TextUI*Command->run(Array, true)
#29 C:\Program Files (x86)\PHP\PEAR\phpunit(53): PHPUnit*TextUI*Command::main()
#30 {main}

I assume that this might happen, because of the Class B holding one Class C, and Class A also holding some Class C instances.

Those are, in some cases, the same. So when a new Class B is added, together with a new Class C, this new Class C is also added to Class A.
And somehow internally the already persisted Class C (Class B is getting persisted first) transforms the Class C instance into an array, and Doctrine can't save it anymore.

This is my guess of what happens. When I skip adding all the Class C instances to Class A, I don't get this error.

@doctrinebot

Comment created by mjh_ca:

Do you get the same error if the collections are defined as ArrayCollections instead of PHP arrays ? Not sure if it is supported to use pure PHP arrays for collections.

See [http://www.doctrine-project.org/projects/orm/2.0/docs/reference/association-mapping/en#collections]

/*** @Entity **/
class A {
   /** ... **/
   public function **construct() {
        $this->collectionB = new \Doctrine\Common\Collections\ArrayCollection;
   }

   /*** @OneToMany(targetEntity="B", mappedBy="a") **/
   protected $collectionB;
}

/*** @Entity **/
class B {
    /** ... **/
    /*** @ManyToOne(targetEntity="A", inversedBy="collectionB") **/
    protected $a;
}  
@doctrinebot

Comment created by sebastian.hoitz:

I change the definition inside of my constructor method to ArrayCollection.

It's just that I write array() when I define the properties to that it's easier for the developers to tell which properties are collections or not.

So they actually are ArrayCollections.

@doctrinebot

Comment created by shurakai:

Please post your entity mappings and some code.

It would be highly appreciated if you could also provide a test case so that we can confirm easily. Thank you!

@doctrinebot

Comment created by sebastian.hoitz:

This was a very stupid issue on my side which I wasn't even thinking about:

I have my own "ModelCollection" class in which I store my models. This class extends Doctrines ArrayCollection class and overwrites the toArray function. It changed its behavior so that the toArray was called recursively also on all child elements.
This caused the PersistentCollection to return an array on the getInsertDiff method.

Long story short: Maybe we should think about making the toArray method on the ArrayCollection class final, since Doctrines ORM relies on its behavior.

@doctrinebot

Issue was closed with resolution "Invalid"

@doctrinebot doctrinebot closed this
@doctrinebot doctrinebot added the Bug label
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.