Problem/Motivation
#3566626: Use property hooks for entity reference item and list entity and target_id properties turned $entity and $target_id on EntityReferenceItem and EntityReferenceFieldItemList into PHP property hooks. These are virtual properties: they have no stored value.
DependencySerializationTrait::__sleep() returns array_keys(get_object_vars($this)). Since PHP 8.4, get_object_vars() includes virtual properties. So __sleep() returns entity and target_id, and serialize() then warns:
Warning: serialize(): "entity" returned as member variable from __sleep() but does not exist
.
Steps to reproduce
$items = \Drupal\user\Entity\User::load(1)->get('roles');
serialize($items);
Will result in the warning for both entity and target_id.
Proposed resolution
Leave virtual properties out in DependencySerializationTrait::__sleep(). Their values come from other properties, so nothing is lost. Add a kernel test that serializes an entity reference field item and its list, and checks that there is no warning.
Remaining tasks
- Fix and test.
Generated with the help of an LLM.
Reviewed by a human though.
Issue fork drupal-3634852
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
gábor hojtsyComment #3
gábor hojtsyComment #4
andypostprobably related to #3548971: Replace PHP soft-deprecated __sleep()/__wakeup() with __serialize()/__unserialize()
Comment #6
gábor hojtsySubmitted an MR.
DependencySerializationTrait::__sleep()usedget_object_vars(), which lists virtual properties and runs their get hooks. That is whyserialize()then warned that the property does not exist. If we read the stored values with an array cast instead, that leaves virtual properties out and runs no hooks, so no warning :)Comment #7
godotislateSeems like we might be able to use
get_mangled_object_varsinstead ofget_object_vars: https://stackoverflow.com/a/79410342, but that could cause BC issues with already serialized objects because the key names would be different. (Usingget_mangled_object_varsalso seems like it might make serializing private properties possible?). Or we could normalize the keys fromget_mangled_object_vars.An alternative would be to do reflection while looping through
get_object_vars, and check if the property isVirtual.Also per #4, switching to using __serialize/__unserialize might make this kind of problem go away, (though maybe not if the virtual property does not allow
set) but it probably makes sense to filter out hooked properties regardless. And we can't get #3548971: Replace PHP soft-deprecated __sleep()/__wakeup() with __serialize()/__unserialize() in before 12.1.Comment #8
godotislateMR is failing PHPStan:
The entity storage thing should just be added to baseline.
The private property thing probably needs looking into. Though I touched on it in #7, we probably shouldn't add support for them here. #3110266: Support serialization of private properties is the issue for that.
Comment #9
gábor hojtsyThanks! I am looking into
get_mangled_object_vars(). It looks like it gives the same result as the array cast. No virtual properties, and no get hooks. It is also clearer, and safer forArrayObjectsubclasses. The keys are turned back into plain property names, so serialized data stays the same.If we would loop over
get_object_vars()withisVirtual()that would run every get hook first. For entity reference items, that loads the referenced entity during serialization.Agreed we should not add private property support here and that #3548971: Replace PHP soft-deprecated __sleep()/__wakeup() with __serialize()/__unserialize() is the better long-term fix for serialization. But property hooks are already in 12.0, so this is needed until then.
Comment #10
gábor hojtsy