Problem/Motivation
I've run into serialization errors when trying to preview a node.
It was a very painful process (https://github.com/EdeMeijer/serialize-debugger helped but I had to make it work with our insane object & array structures first), but I tracked it down to Context objects in $form_state, which use TypedDataTrait.
In a way, this is the same problem as #2893029: EntityType objects cannot be reliably serialized without DependencySerializationTrait, so we could possibly also use the same fix instead of using DependencySerializationTrait. Or both.
Proposed resolution
Add DependencySerializationTrait to the Context class
Remaining tasks
Write tests
User interface changes
N/A
API changes
N/A
Data model changes
N/A
Comment | File | Size | Author |
---|---|---|---|
#25 | 2986735-context-25.patch | 1012 bytes | tim.plunkett |
#18 | interdiff-2986735-13-18.txt | 505 bytes | phenaproxima |
#18 | 2986735-18.patch | 5.59 KB | phenaproxima |
#13 | interdiff-2986735-9-13.txt | 5.06 KB | phenaproxima |
#13 | 2986735-13.patch | 5.6 KB | phenaproxima |
Comments
Comment #2
BerdirComment #3
MegaChriz CreditAttribution: MegaChriz at WebCoo commented@Berdir
What did you need to do to make https://github.com/EdeMeijer/serialize-debugger work? Adjust the library? Adjust code in Drupal?
I get an error when trying the following code in a method called
buildPaneForm()
whereserialize($this)
failed:The error:
Comment #4
BerdirThat is what happens because it manually calls the __sleep(), so this is expected.
The difference is that in serialize(), this recursion happens internally and you can't debug it. Now you have a nice and clear backtrace, all you need to do is set a breakpoint now in Connection::__sleep() and you can look at the object hierarchy that is passed through.
You could also do the same with some print statements in e.g. "RecursiveResolver->resolve" but that will be harder to follow.
Comment #5
MegaChriz CreditAttribution: MegaChriz at WebCoo commented@Berdir
Thanks for your quick reply. I was expecting that the library would stop on an exception and then print where things went wrong.
In my case \Drupal\Core\Entity\EntityTypeManager apparently is the culprit. When inspecting each \EdeMeijer\SerializeDebugger\Node in the trace, I got a chain of the following objects:
Comment #6
BerdirEntityTypeManager isn't the culprit, that is a service, that already should not be serialized.
> Drupal\ukkbaccount\Account\Profile
What is that object? Is it a service or a custom object you create that has values? Possibly tht one should use the DependencySerializationTrait to drop the reference to EntityTypeManager on serialize.
Comment #7
MegaChriz CreditAttribution: MegaChriz at WebCoo commentedAh, thus services should generally not be serialized at all.
\Drupal\ukkbaccount\Account\Profile is a custom object with several account related methods. Its constructor asks for the entity type manager and an account. Adding a DependencySerializationTrait there solves the issue in my case! An ajaxified form element now gets properly updated.
Would it be a good idea for a better developer experience to throw exceptions in
__sleep()
in all service classes as well? This way more developers will be informed that you should avoid serializing these.Comment #8
phenaproximaThis will need tests...
Comment #9
tim.plunkettThis blocks #2976148: Layout-based entity rendering should delegate to the correct section storage instead of hardcoding to either defaults or overrides
Comment #10
phenaproximaHere, have a patch with a test!
Comment #11
tim.plunkettIf this moves from a unit to kernel test, it should move to
core/tests/Drupal/KernelTests/Core/Plugin
as well (or just make a new class)Weird using an object property for this instead of a local variable.
This comment is a little odd because it references how code used to be instead of how things are.
I think we need something asserted here.
Comment #12
phenaproximaFor the record, we could solve all future occurrences of this problem if we would never make service getters into implicit setters.
This is an anti-pattern: it's not a getter, it's a setter. And we wonder why we run into sudden serialization failures. We should reject this anti-pattern forever (in fact, we should deprecate it in Drupal 8, and remove it in Drupal 9). Of course, it'd still be possible to cause a serialization failure by explicitly setting the service (setTypedDataManager()) and serializing the consuming object, but at least then it would be traceable to an explicit method call somewhere, and could be addressed by module authors rather than the onus being on core.
In any case, the method says get, but it actually sets. We need to not do that. Not in this issue, necessarily, but we should not allow any more instances of this anti-pattern to land in core. Looks like I found the hill I'm willing to die on!
Comment #13
phenaproximaAll feedback from #11 addressed here. I moved the test to the KernelTests namespace and rewrote the mock object to use Prophecy.
Comment #15
tim.plunkettLooks great, thanks!
Comment #16
tim.plunkettComment #18
phenaproximaWhoops! I had the test in the wrong namespace. Kicking directly back to RTBC on the assumption that it will go green this time.
Comment #19
larowlanAdding issue credits
Comment #22
larowlanCommitted b1ad4a8 and pushed to 8.7.x. Thanks!
C/p as c0c662c427 and pushed to 8.6.x
Thanks
Comment #23
phenaproximaThanks, @larowlan!
For anyone who's interested, I opened an issue pleading with the framework managers to deprecate the "implicit setter" anti-pattern which caused this bug: #3011620: Deprecate the "implicit setter" anti-pattern
Comment #24
tacituseu CreditAttribution: tacituseu commentedThis introduced test failure on PHP 5.5:
https://www.drupal.org/pift-ci-job/1113676
https://www.drupal.org/pift-ci-job/1113681
Comment #25
tim.plunkettThat's
shouldBeCalledTimes(1)
in older PHPSpec.But it's unnecessary here anyway.
Comment #26
alexpottCommitted and pushed bb9fb66dd7 to 8.7.x and d935fc4871 to 8.6.x. Thanks!
Ran tests locally on PHP5.5,5.6 and 7 and they pass.