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

Closed
opened 2026-01-22 12:59:52 +01:00 by admin · 5 comments
Owner

Originally created by @doctrinebot on GitHub (Oct 19, 2010).

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.

Originally created by @doctrinebot on GitHub (Oct 19, 2010). 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.
admin added the Bug label 2026-01-22 12:59:52 +01:00
admin closed this issue 2026-01-22 12:59:53 +01:00
Author
Owner

@doctrinebot commented on GitHub (Oct 19, 2010):

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 commented on GitHub (Oct 19, 2010): 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; } ```
Author
Owner

@doctrinebot commented on GitHub (Oct 21, 2010):

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 commented on GitHub (Oct 21, 2010): 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.
Author
Owner

@doctrinebot commented on GitHub (Oct 22, 2010):

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 commented on GitHub (Oct 22, 2010): 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!
Author
Owner

@doctrinebot commented on GitHub (Oct 24, 2010):

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 commented on GitHub (Oct 24, 2010): 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.
Author
Owner

@doctrinebot commented on GitHub (Oct 24, 2010):

Issue was closed with resolution "Invalid"

@doctrinebot commented on GitHub (Oct 24, 2010): Issue was closed with resolution "Invalid"
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#1043