New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Problem with Proxies on PHP 7.4.2 with typed properties #8030
Comments
I don't see any DoctrineBundle issue here, moving to ORM |
@bizley you are wrong though, it is still possible to set the property in However why is sleep called anywys, please provide the whole stack trace for the error. |
I'm not saying it's not possible - I'm saying that it's triggering fatal error for proxy object, and yes, for the reason you mentioned. I just don't understand why it's suddenly happening in 7.4.2. |
Here is public function __sleep()
{
if ($this->__isInitialized__) {
return ['__isInitialized__', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'id', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'order', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'status', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'createdAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'promisedAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'completeAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'assignAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'activeAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'destinationAddress', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'courier', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'type', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'distance', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'isActive', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'endLatitude', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'endLongitude', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'startLatitude', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'startLongitude', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'endLocationAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'primaryEstimatedDistance', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'primaryEstimatedDuration', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'estimatedDuration', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'estimatedDistance', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'lastEstimationAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'ratio', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'courierCostGross', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'fuelCostNet', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'bonusCostGross', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'polyline', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'estimationRatio', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'estimationRatioDate', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'timeFrameStartAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'timeFrameEndAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'vehicle', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'serviceTime', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'orderTaskIssues', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'eta'];
}
return ['__isInitialized__', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'id', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'order', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'status', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'createdAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'promisedAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'completeAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'assignAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'activeAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'destinationAddress', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'courier', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'type', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'distance', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'isActive', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'endLatitude', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'endLongitude', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'startLatitude', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'startLongitude', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'endLocationAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'primaryEstimatedDistance', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'primaryEstimatedDuration', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'estimatedDuration', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'estimatedDistance', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'lastEstimationAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'ratio', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'courierCostGross', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'fuelCostNet', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'bonusCostGross', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'polyline', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'estimationRatio', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'estimationRatioDate', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'timeFrameStartAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'timeFrameEndAt', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'vehicle', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'serviceTime', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'orderTaskIssues', '' . "\0" . 'App\\Entity\\OrderTask' . "\0" . 'eta'];
} |
@dunglas looping you in as this seems to be an interaction between APIPlatfoorm, Symfony Serializer and Doctrine entities. |
I can confirm this. API Platform injects an A possible solution would be to change However it is still unclear to me which property is supposedly uninitialized in that proxy class. I couldn't find any upon inspection with xdebug |
Same problem (non initialized object) exist in If the entity contain typed property with nullable flag, after persist and flush we receive error:
It's can be fixed with next solution:
|
In embedded properties (
The property But in this case we've a critical bug, because this property can't contain null value by default (value set from constructor) and we can't assign null by default. |
Ok, now I understand why it's happening in PHP 7.4.2 - https://bugs.php.net/bug.php?id=79002 Turns out Right now I see 3 possible solutions:
|
@ZhukV are you using the latest versions of doctrine/persistence (1.3.6) and doctrine/reflection (1.1)? Your bug should have been fixed with them. |
Do not use
|
Uhm, yes, entities+private properties is OK. __sleep/__wakeup are kinda
edge cases
…On Fri, Feb 21, 2020, 21:51 Ivan Grigoriev ***@***.***> wrote:
Do not use private properties in entities: Projects/ORM/Documentation/Architecture#Serializing
entities
<https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/architecture.html#serializing-entities>.
Doctrine does not have full support for that.
If you intend to serialize (and unserialize) entity instances that still
hold references to proxy objects you may run into problems with private
properties because of technical limitations. Proxy objects implement
__sleep and it is not possible for __sleep to return names of private
properties in parent classes.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#8030?email_source=notifications&email_token=AABFVEE5AZODMZWGA43LVX3REA5ELA5CNFSM4KXUKBO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEMUBOXI#issuecomment-589829981>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AABFVEDXKZGXXRLCAZTKC2LREA5ELANCNFSM4KXUKBOQ>
.
|
@beberlei , thank! After update to latest versions all correct work.
|
@bizley i would think option 2 is the correct one from my PoV, the other two are not really good and your initial bug report seems to be not true that it happens because of the constructor, but specificially from sleep. |
@beberlei thank you for looking at this case but I believe that you missed something here completely. First of all, if you are referring to my list of possible solutions - number 2 is quite a joke that I hoped is obvious (apparently it is not). You can not initialize immediately every property in every possible case, and what is most important you can not possibly believe that you can force users to do that. Number 1 is a Symfony thing and I believe they should take care of it (as in "catch Error and not just Exception"). So I think number 3 is only viable solution (as in "fix sleep in proxies"). Second - I only described symptoms and the way to reproduce the problem in the bug description so yes - it is possible to live with current state of Doctrine and PHP 7.4.2. Still, the problem with sleep is there and should be fixed. I'm not sure what kind of a reporting policy is working here - should I open another ticket basically referring everything in this one? I'm a bit disappointed how all this is handled right now. |
@bizley I am a bit disappointed as well, that you provide a joke as an option and then feel offended when I pick it. From the description I cannot see how this is a Doctrine bug yet. Maybe if you provide a reproducable test-case using See https://github.com/doctrine/reflection/blob/master/phpunit.xml.dist#L12 how you can create a test in a special directory that only runs on a specific PHP version as this must be 7.4 only. |
My apologies then. I'll try to work on the test case. And I think this issue should be moved to doctrine/common since this is where ProxyGenerator is placed. |
This example given in your first comment: private ?DateTimeInterface $property = null; will delay the issue presenting itself, but as described in the comment by @EmanuelOster using Consequently, even though it was tongue-in-cheek, this won't actually work:
|
@chrBrd you are absolutely right. The problem is with proxy generator creating proxies with uninitialized typed properties in its __sleep and probably object normalizer not catching all errors. |
@bizley - looks like not entirely. While I agree with you this is an issue with the proxy generator, it turns out I hadn't quite initialized everything. I pasted your example above to save time, but the actual code snippet I'm looking into this with is: /**
* @ORM\ManyToOne(targetEntity="App\Entity\Question", inversedBy="answers")
*/
private ?Question $question = null; I noticed I was still getting the error with /**
* @ORM\OneToMany(targetEntity="App\Entity\Answer", mappedBy="question", orphanRemoval=true)
*/
private Collection $answers; Changing that to Immediately initializing every typed property is definitely more of a workaround than a fix though, especially as it necessitates setting @beberlei, I completely understand why you want a reproducible test case for this, but I feel the issue may have been closed prematurely. |
I was wondering why doctrine/common's ProxyLogicTypedPropertiesTest passes when there is at least one uninitialized typed property in tested object and its proxy is serialized in the test. Turns out that every uninitialized field is actually being initialized in the setup phase of that test which is why this issue I've reported goes undetected. You can easily verify it by adding additional
And please don't get me wrong, I know this test is to verify lazy loading and properties should be initialized but still I think it makes my point. Will it be enough for reproducible test case @beberlei ? Once again sorry for sounding harsh before. You guys are doing great job, and I really appreciate it. |
Hi there! I get
with Embeddables when using |
Sorry for the late reply @beberlei. Thanks to @alanpoulain it's gonna be fixed in Symfony: symfony/symfony#36332 |
…hen serializing context for the cache key (alanpoulain) This PR was squashed before being merged into the 3.4 branch. Discussion ---------- [Serializer] Fix unitialized properties (from PHP 7.4.2) when serializing context for the cache key | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #35574 doctrine/orm#8030 | License | MIT | Doc PR | N/A This bug only happens on the following conditions: - A Doctrine entity (`Book`) having a relation with another entity (`Author`) is used; - The `Author` entity uses typed properties (PHP 7.4) not initialized; - The `Serializer` is used with the `Book` in the `OBJECT_TO_POPULATE` key in the context. For instance: ```php <?php declare(strict_types=1); namespace App\Entity; use Doctrine\ORM\Mapping as ORM; /** @Orm\Entity */ class Book { /** * @Orm\ManyToOne(targetEntity="Author") */ public Author $author; public ?string $isbn; } ``` ```php <?php declare(strict_types=1); namespace App\Entity; use Doctrine\ORM\Mapping as ORM; /** @Orm\Entity */ class Author { public ?string $name; } ``` Or even: ```php <?php declare(strict_types=1); namespace App\Entity; use Doctrine\ORM\Mapping as ORM; /** @Orm\Entity */ class Author { private string $name; public function __construct() { $this->name = 'Leo'; } } ``` If the following is done (it's the case for instance in API Platform when a `PUT` is made): ```php $serializer->deserialize('{"isbn":"2038717141"}', Book::class, 'json', ['object_to_populate' => $book]); ``` Then there will be the following error: > Fatal error: Typed property Proxies\__CG__\App\Entity\Author::$ must not be accessed before initialization (in __sleep) It's because of these lines in the `getCacheKey` method of the `AbstractObjectNormalizer`: https://github.com/symfony/symfony/blob/5da141b8d0ec7d71fb0ad637245833cd7fa56164/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php#L405-L409 Since the lazy proxyfied relation has a `__sleep` with unitialized properties, the `serialize` method will throw (since https://bugs.php.net/bug.php?id=79002: php/php-src@846b647). I propose to fix this issue by unsetting the `OBJECT_TO_POPULATE` key in the context because I don't think it's useful for determining the attributes of the object. For the next versions of Symfony, the fix should probably be elsewhere, in the default context. For instance in Symfony 4.4, instead of: https://github.com/symfony/symfony/blob/15edfd39d4b3da4d011a6242238c4b2874eb1ace/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php#L118 It should be: ```php $this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] = [self::CIRCULAR_REFERENCE_LIMIT_COUNTERS, self::OBJECT_TO_POPULATE]; ``` But I'm not sure how it should be merged (another PR maybe?). Commits ------- 1fafff7 [Serializer] Fix unitialized properties (from PHP 7.4.2) when serializing context for the cache key
@dunglas It looks like the problem still here.
I temporarily fixed by adding the Id field:
Also the first version works if remove the Item typed property, like it is in the php 7.3 Symfony 4.4.10 with last packages. |
My PR hasn't been merged in Symfony. Has you can see in the comments, not everybody agree that it's the proper fix. |
This PR was squashed before being merged into the 4.4 branch. Discussion ---------- [Serializer] Fix configuration of the cache key | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | yes | New feature? | no <!-- please update src/**/CHANGELOG.md files --> | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tickets | Fix #35574 doctrine/orm#8030 (partially) | License | MIT | Doc PR | n/a Currently, a bug prevents to configure the context keys to exclude from the cache key computation. The value is always replaced in the constructor. This PR fixes the problem. Commits ------- 3b034cb [Serializer] Fix configuration of the cache key
(I'm sorry if this is wrong place to report that issue, please transfer me to the proper one if this is the case)
We encountered this problem on the Behat testing phase with generated entity proxies. There is fatal error:
This happened when we upgraded PHP to 7.4.2 (everything was ok on 7.4.1). We used typed properties before the upgrade as well. After playing with the entity for a bit we discovered that now every typed object property must be initialized immediately (previously they were initialized in the constructor) like:
because this doesn't work anymore:
This is was already reported in Symfony symfony/symfony#35574 but closed.
The text was updated successfully, but these errors were encountered: