Typed property must not be accessed before initialization #6360

Closed
opened 2026-01-22 15:31:44 +01:00 by admin · 21 comments
Owner

Originally created by @oojacoboo on GitHub (Dec 5, 2019).

Bug Report

Q A
BC Break no
Version 2.7.0

Summary

Typed property Foo::$id must not be accessed before initialization

doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:634

Seems to be an issue with reflection attempting to access typed properties.

Originally created by @oojacoboo on GitHub (Dec 5, 2019). ### Bug Report <!-- Fill in the relevant information below to help triage your issue. --> | Q | A |------------ | ------ | BC Break |no | Version | 2.7.0 #### Summary >Typed property Foo::$id must not be accessed before initialization `doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:634` Seems to be an issue with reflection attempting to access typed properties.
admin closed this issue 2026-01-22 15:31:44 +01:00
Author
Owner

@Ocramius commented on GitHub (Dec 5, 2019):

This is relatively normal: the UnitOfWork will almost always try to access properties of entities, so if you declared a property such as private ?int $id;, if you don't want it to be set at __construct, it must have a default value through private ?int $id = null;

@Ocramius commented on GitHub (Dec 5, 2019): This is relatively normal: the `UnitOfWork` will almost always try to access properties of entities, so if you declared a property such as `private ?int $id;`, if you don't want it to be set at `__construct`, it must have a default value through `private ?int $id = null;`
Author
Owner

@oojacoboo commented on GitHub (Dec 5, 2019):

That totally makes sense. However... what's the recommended route forward here? Obviously you don't want the $id (PK) to be nullable. And, when calling new/instantiating the object, you generally won't know the correct value. I guess you could set it to zero, but that's a hack. Also, is this only going to be an issue with the PK/IDENTITY for Entities with typed properties?

@oojacoboo commented on GitHub (Dec 5, 2019): That totally makes sense. However... what's the recommended route forward here? Obviously you don't want the `$id` (PK) to be nullable. And, when calling new/instantiating the object, you generally won't know the correct value. I guess you could set it to zero, but that's a hack. Also, is this only going to be an issue with the PK/IDENTITY for Entities with typed properties?
Author
Owner

@malarzm commented on GitHub (Dec 5, 2019):

@oojacoboo but technically $id is nullable when using any kind of auto increment identifier - its value is literally null before UnitOfWork gets the value from DB and assigns it back to the entity. I'd say it's perfect time to preach using uuid as an identifier :)

@malarzm commented on GitHub (Dec 5, 2019): @oojacoboo but technically `$id` is nullable when using any kind of auto increment identifier - its value is literally `null` before `UnitOfWork` gets the value from DB and assigns it back to the entity. I'd say it's perfect time to preach using `uuid` as an identifier :)
Author
Owner

@oojacoboo commented on GitHub (Dec 5, 2019):

Yea, had a good discussion on Slack about this. I suspect that many other people will run into this issue as PHP 7.4 begins to be adopted more and people start typing their properties.

We'll just use protected ?int $id = null. This has a fringe benefit in allowing you to identify if the Entity has been persisted and makes sense for a default value with auto-increment on most RDBMSes.

@oojacoboo commented on GitHub (Dec 5, 2019): Yea, had a good discussion on Slack about this. I suspect that many other people will run into this issue as PHP 7.4 begins to be adopted more and people start typing their properties. We'll just use `protected ?int $id = null`. This has a fringe benefit in allowing you to identify if the Entity has been persisted and makes sense for a default value with auto-increment on most RDBMSes.
Author
Owner

@pfazzi commented on GitHub (Dec 6, 2019):

I have the same issue, ATM i'm adopting the private ?int $id = null; strategy.

@pfazzi commented on GitHub (Dec 6, 2019): I have the same issue, ATM i'm adopting the `private ?int $id = null;` strategy.
Author
Owner

@kyrrr commented on GitHub (Jan 7, 2020):

Would be nice to have a different solution than setting nullables everywhere.

@kyrrr commented on GitHub (Jan 7, 2020): Would be nice to have a different solution than setting nullables everywhere.
Author
Owner

@oojacoboo commented on GitHub (Jan 7, 2020):

@kyrrr it's totally fixable. There is an open pull request related to this, but it seems more needs to be done on it and there hasn't been much activity recently.

https://github.com/doctrine/orm/pull/7857

@oojacoboo commented on GitHub (Jan 7, 2020): @kyrrr it's totally fixable. There is an open pull request related to this, but it seems more needs to be done on it and there hasn't been much activity recently. https://github.com/doctrine/orm/pull/7857
Author
Owner

@beberlei commented on GitHub (Jan 17, 2020):

Updating doctrine/persistence to 1.3.6 will fix this issue.

@beberlei commented on GitHub (Jan 17, 2020): Updating doctrine/persistence to 1.3.6 will fix this issue.
Author
Owner

@leongersen commented on GitHub (Jan 22, 2020):

Unfortunately, even with the updates to doctrine/persistence, ids must be nullable to be able to remove the entity, as the UnitOfWork will be setting the id value back to null in \Doctrine\ORM\UnitOfWork::executeDeletions:

// Entity with this $oid after deletion treated as NEW, even if the $oid
// is obtained by a new entity because the old one went out of scope.
//$this->entityStates[$oid] = self::STATE_NEW;
if ( ! $class->isIdentifierNatural()) {
	$class->reflFields[$class->identifier[0]]->setValue($entity, null);
}

I've filed this issue: #7999

@leongersen commented on GitHub (Jan 22, 2020): Unfortunately, even with the updates to `doctrine/persistence`, ids must be nullable to be able to remove the entity, as the `UnitOfWork` will be setting the id value back to null in `\Doctrine\ORM\UnitOfWork::executeDeletions`: ```php // Entity with this $oid after deletion treated as NEW, even if the $oid // is obtained by a new entity because the old one went out of scope. //$this->entityStates[$oid] = self::STATE_NEW; if ( ! $class->isIdentifierNatural()) { $class->reflFields[$class->identifier[0]]->setValue($entity, null); } ``` I've filed this issue: #7999
Author
Owner

@artyom-wcd commented on GitHub (Mar 6, 2020):

using this: "name": "doctrine/persistence", "version": "1.3.6",
but the problem (as described before) is still there when trying to flush

@artyom-wcd commented on GitHub (Mar 6, 2020): using this: "name": "doctrine/persistence", "version": "1.3.6", but the problem (as described before) is still there when trying to flush
Author
Owner

@oojacoboo commented on GitHub (Mar 6, 2020):

Can the property just be unset or, by using reflection, check if nullable and optionally set as null?

Deletion is an interesting scenario. Technically, unless using soft-delete, that object shouldn't really exist anymore, and certainly not the ID. I can see how it'd be useful to use an object after deletion though.

@oojacoboo commented on GitHub (Mar 6, 2020): Can the property just be `unset` or, by using reflection, check if nullable and optionally set as null? Deletion is an interesting scenario. Technically, unless using soft-delete, that object shouldn't really exist anymore, and certainly not the ID. I can see how it'd be useful to use an object after deletion though.
Author
Owner

@yapro commented on GitHub (Mar 19, 2020):

My error:

"Typed property Proxies\\__CG__\\App\\Entity\\Organization::$ must not be accessed before initialization (in __sleep)"

My solution - add next method to the class:

public function __sleep()
{
    return [];
}
@yapro commented on GitHub (Mar 19, 2020): My error: ``` "Typed property Proxies\\__CG__\\App\\Entity\\Organization::$ must not be accessed before initialization (in __sleep)" ``` My solution - add next method to the class: public function __sleep() { return []; }
Author
Owner

@VaN-dev commented on GitHub (Jun 23, 2020):

Updating doctrine/persistence to 1.3.6 will fix this issue.

it doesn't

@VaN-dev commented on GitHub (Jun 23, 2020): > Updating doctrine/persistence to 1.3.6 will fix this issue. it doesn't
Author
Owner

@Amunak commented on GitHub (Aug 18, 2020):

What about properties where you can't set the default value, like in associations that use collection? Object cannot be a default value, and nullable makes no sense here.

This example:

class Entity {
    	/**
	 * @ORM\OneToMany(targetEntity="Other")
	 * @var Collection|Other[]
	 */
	protected $friends;
	public function __construct() {
		$this->friends = new ArrayCollection();
	}
}

used to be the proper way to initialize collections, but now if you add PHP7.4 property type hints (protected Collection $friends;) it no longer works, as Doctrine accesses the property through reflection but doesn't run the constructor and then you get the Fatal Error.

And that's not the only example, what about custom Doctrine types that use some object to hold the value?

@Amunak commented on GitHub (Aug 18, 2020): What about properties where you can't set the default value, like in associations that use collection? Object cannot be a default value, and nullable makes no sense here. This example: ```php class Entity { /** * @ORM\OneToMany(targetEntity="Other") * @var Collection|Other[] */ protected $friends; public function __construct() { $this->friends = new ArrayCollection(); } } ``` used to be the proper way to initialize collections, but now if you add PHP7.4 property type hints (`protected Collection $friends;`) it no longer works, as Doctrine accesses the property through reflection but doesn't run the constructor and then you get the Fatal Error. And that's not the only example, what about custom Doctrine types that use some object to hold the value?
Author
Owner

@Ocramius commented on GitHub (Aug 18, 2020):

but now if you add PHP7.4 property type hints (protected Collection $friends;) it no longer works, as Doctrine accesses the property through reflection but doesn't run the constructor and then you get the Fatal Error.

Proxy initialization does set collections?

What doesn't work (and also didn't work in the past) in the 2.x series is "friend objects", but otherwise proxy initialization does populate their associations.

This is the broken behavior (also pre-php-7.4):

class Thing
{
    private $id;
    private $lazyField;
    function equals(self $other) {
        return $other->lazyField->equals($this->lazyField); // call to `equals` on `null` happening here
    }
}
@Ocramius commented on GitHub (Aug 18, 2020): > but now if you add PHP7.4 property type hints (`protected Collection $friends;`) it no longer works, as Doctrine accesses the property through reflection but doesn't run the constructor and then you get the Fatal Error. Proxy initialization does set collections? What doesn't work (and also didn't work in the past) in the 2.x series is "friend objects", but otherwise proxy initialization does populate their associations. This is the broken behavior (also pre-php-7.4): ```php class Thing { private $id; private $lazyField; function equals(self $other) { return $other->lazyField->equals($this->lazyField); // call to `equals` on `null` happening here } } ```
Author
Owner

@oojacoboo commented on GitHub (Aug 18, 2020):

@Ocramius That's partially right. But, you don't need the call to equals there. You only need the call on lazyField, which is the issue. It's of particular concern for auto incremented IDs. You make it sound like this has always been an issue and it's not an important one.

@oojacoboo commented on GitHub (Aug 18, 2020): @Ocramius That's partially right. But, you don't need the call to `equals` there. You only need the call on `lazyField`, which is the issue. It's of particular concern for auto incremented IDs. You make it sound like this has always been an issue and it's not an important one.
Author
Owner

@Amunak commented on GitHub (Aug 18, 2020):

Proxy initialization does set collections?

Interesting, for some reason I have this issue only in our Jenkins test build but can't replicate it locally. I'll investigate further.

@Amunak commented on GitHub (Aug 18, 2020): > Proxy initialization does set collections? Interesting, for some reason I have this issue only in our Jenkins test build but can't replicate it locally. I'll investigate further.
Author
Owner

@Ocramius commented on GitHub (Aug 18, 2020):

@Ocramius That's partially right. But, you don't need the call to equals there. You only need the call on lazyField, which is the issue. It's of particular concern for auto incremented IDs. You make it sound like this has always been an issue and it's not an important one.

@oojacoboo yes, I made the example to show that the method call will lead to a crash: much harder to notice if I used === there :)

@Ocramius commented on GitHub (Aug 18, 2020): > @Ocramius That's partially right. But, you don't need the call to `equals` there. You only need the call on `lazyField`, which is the issue. It's of particular concern for auto incremented IDs. You make it sound like this has always been an issue and it's not an important one. @oojacoboo yes, I made the example to show that the method call will lead to a crash: much harder to notice if I used `===` there :)
Author
Owner

@Amunak commented on GitHub (Aug 19, 2020):

Alright, investigation concluded; when clearing cache on Jenkins no APP_ENV was defined so it cleared the default, but we needed other envs cleared. This lead to old metadata being used from cache and in those Doctrine didn't even know about some of the fields, skipping the initialization.

Sorry, my mistake.

@Amunak commented on GitHub (Aug 19, 2020): Alright, investigation concluded; when clearing cache on Jenkins no APP_ENV was defined so it cleared the default, but we needed other envs cleared. This lead to old metadata being used from cache and in those Doctrine didn't even know about some of the fields, skipping the initialization. Sorry, my mistake.
Author
Owner

@Bguignard commented on GitHub (Jan 29, 2021):

I had exactly the same problem, only happening on the web server and not in local environment, and the cause was :

  • I had objects in the Symfony session
  • I copied (cloned) one object from the session in a controller to fill it in order to send it to the template
  • Since cloning in PHP is simply a shallow and not a deep copy, all attribute which are also objects are NOT copied but only passed by reference to the new clone.
  • Since Doctrine attribute of One to Many collections are ArrayCollection and not array, they are passed by reference when the owning object is cloned.
  • Since it uses the __sleep() method when its taken from the session, when I tried to set this collection in the clonned object, this ArrayCollection was not initialized which caused the error.

The worst thing was, after triggering the error, I had to delete the session manually in the server in the /var/lib/php/sessions folder because Symfony couldn't access to the session anymore which completely stuck the website for the current browser.

I first tried to "override" the __sleep() method as answered before, but even if the error was not triggered, my attribute was empty and that's not what I wanted.

My solution was to clone, then empty the collection with a ->clear() method after cloning the object, then settting it with the add() method.

I think the heart of the problem is this __sleep() method when objects with ArrayCollection attributes are cloned from the session.

I still don't understand why the bug didn't happen with php 7.4.9 and 7.4.1 in a local environment but happened with 7.4.3 on the server...

The error is the same as described here but they closed it : https://github.com/symfony/symfony/issues/35574

@Bguignard commented on GitHub (Jan 29, 2021): I had exactly the same problem, only happening on the web server and not in local environment, and the cause was : - I had objects in the Symfony session - I copied (cloned) one object from the session in a controller to fill it in order to send it to the template - Since cloning in PHP is simply a shallow and not a deep copy, all attribute which are also objects are NOT copied but only passed by reference to the new clone. - Since Doctrine attribute of One to Many collections are ArrayCollection and not array, they are passed by reference when the owning object is cloned. - Since it uses the __sleep() method when its taken from the session, when I tried to set this collection in the clonned object, this ArrayCollection was not initialized which caused the error. The worst thing was, after triggering the error, I had to delete the session manually in the server in the /var/lib/php/sessions folder because Symfony couldn't access to the session anymore which completely stuck the website for the current browser. I first tried to "override" the __sleep() method as answered before, but even if the error was not triggered, my attribute was empty and that's not what I wanted. My solution was to clone, then empty the collection with a ->clear() method after cloning the object, then settting it with the add() method. I think the heart of the problem is this __sleep() method when objects with ArrayCollection attributes are cloned from the session. I still don't understand why the bug didn't happen with php 7.4.9 and 7.4.1 in a local environment but happened with 7.4.3 on the server... The error is the same as described here but they closed it : https://github.com/symfony/symfony/issues/35574
Author
Owner

@cbichis commented on GitHub (Dec 15, 2021):

I am getting the same issue with quite new libraries...

doctrine/annotations 1.13.2
doctrine/cache 1.12.1
doctrine/collections 1.6.8
doctrine/common 3.2.0
doctrine/dbal 2.13.6
doctrine/deprecations v0.5.3
doctrine/doctrine-laminas-hydrator 2.2.1
doctrine/doctrine-module 4.4.0
doctrine/doctrine-orm-module 4.2.x-dev 26f2270
doctrine/event-manager 1.1.1
doctrine/inflector 2.0.4
doctrine/instantiator 1.4.0
doctrine/lexer 1.2.1
doctrine/migrations 3.3.2
doctrine/orm 2.10.3
doctrine/persistence 2.2.3

@cbichis commented on GitHub (Dec 15, 2021): I am getting the same issue with quite new libraries... doctrine/annotations 1.13.2 doctrine/cache 1.12.1 doctrine/collections 1.6.8 doctrine/common 3.2.0 doctrine/dbal 2.13.6 doctrine/deprecations v0.5.3 doctrine/doctrine-laminas-hydrator 2.2.1 doctrine/doctrine-module 4.4.0 doctrine/doctrine-orm-module 4.2.x-dev 26f2270 doctrine/event-manager 1.1.1 doctrine/inflector 2.0.4 doctrine/instantiator 1.4.0 doctrine/lexer 1.2.1 doctrine/migrations 3.3.2 doctrine/orm 2.10.3 doctrine/persistence 2.2.3
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6360