Problem/Motivation
When you install the locale module, the core string_translation service will receive locale's translator class, which has an indirect dependency on the database.
If you then try to serialize anything, directly or indirectly, which:
a) uses StringTranslationTrait; and
b) does not use DependencySerializationTrait;
...you may get a "database connection is not serializable" exception, if the consumer of StringTranslationTrait has a reference to the string_translation service, set either explicitly by setStringTranslation() or implicitly by getStringTranslation().
I'm marking this as a major issue because it seems to me that it is squarely an architectural conflict between DependencySerializationTrait and StringTranslationTrait, and may need to be resolved in several different places in core. However, the EntityType class, and its descendants, are maybe the most visible and pervasive manifestation of this problem, so this issue fixes the problem in EntityType.
Original issue report, filed against Lightning:
Steps to reproduce using Lightning:
- Install Drupal 8 with fresh copy of lightning profile using composer.
- Add entity reference field to reference media entity type to basic page content type (doesn't work for any content type).
- Make sure the form widget is Entity Browser with Media Browser selector, the selector shows the Rendered Entity in Current selection area.
- Enable Language and Interface translation modules. Optionally (maybe required) add one more language. In our case it is German.
- Create a new node/edit existing node.
- Try to add/change the media in the newly created field.
- Observe fatal error 500 after clicking on "Place" button when it is trying the render the preview of the Media in for Current selection area.
Proposed resolution
Add DependencySerializationTrait to EntityType class.
There are a couple of longer-term solutions which can be discussed and worked on in other issues:
- getStringTranslation() should not implicitly set a reference to the string translation manager. To summarize #7 and quote #16:
Without this patch, that condition could happen essentially by accident if you, or anything you are referencing (explicitly or implicitly) uses StringTranslationTrait without DependencySerializationTrait. With this patch, the only way you will encounter the serialization exception is if you (or a parent class you are extending) have explicitly injected the service. Which means you are in a position to actually fix the problem, either by removing the reference to the service, or by importing DependencySerializationTrait.
- Or, once Drupal core requires PHP 7, make StringTranslationTrait use DependencySerializationTrait.
Remaining tasks
Commit the patch.
User interface changes
None.
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #51 | 38-51-interdiff.txt | 9.04 KB | alexpott |
| #51 | 2893029-51.patch | 11.3 KB | alexpott |
| #38 | 2893029-38.patch | 2.1 KB | sam152 |
| #38 | 2893029-38_TEST_ONLY.patch | 1.35 KB | sam152 |
| #34 | 2893029-34.patch | 1.87 KB | phenaproxima |
Comments
Comment #2
phenaproximaCritical priority is for bugs that are actively causing data loss or other catastrophic failures. This is certainly a big issue, but it's not critical :) I'll look into it.
Comment #3
phenaproximaCongratulations, @a.dmitriiev -- you've turned up a bug in Drupal core!
I am updating the IS with the appropriate information and attaching a failing test which proves the problem.
Comment #4
phenaproximaBit of cleanup.
Comment #5
phenaproximaI said this was a major issue, and yet I left it with normal priority. Derp.
Comment #6
phenaproximaComment #7
phenaproximaHere is an improved (failing) test to illustrate what I think the real problem is here -- namely, the god-awful DX of implicitly creating an unserializable dependency, which happens because StringTranslationTrait::getStringTranslation() sets $this->stringTranslation if it's not set already.
IMHO, the correct fix here -- even though it might constitute an API break -- is that getStringTranslation() should never set $this->stringTranslation. It should use it if it's there, but no more than that.
This approach will not prevent people from hitting the database serialization exception if setStringTranslation() has been called, but that's OK, I think -- the point is that it will no longer be possible to run into that condition by accident. The implicitness is what makes this such an insidious land mine, and we need to kill it dead. With fire.
EDIT: Also, it is not good practice for a getter to be setting things. Such a thing would be unacceptable in a core patch, so why are we OK with it being done by a trait that is used by half of core??
Comment #8
berdirDiscussed a bit in IRC. I initially suggested to copy the __sleep() but that actually also doesn't work as you can't inherit the same method from two traits, fails in a different way.
I'm +1 on not doing the set anymore in getStringTranslation(), probably with a @todo to use DST instead/anyway in 9.x, to also help with those that explicitly set the value.
Those that do that will likely be in a class that actually does use DST, e.g. a controller or form.
Comment #9
phenaproximaOkay -- this oughta do the trick.
Comment #10
phenaproximaRe-titling for clarity.
Comment #11
phenaproximaWe'll need a change record, as per this point @Berdir raised on IRC:
Comment #12
a.dmitriiev commentedWow, I was not expecting the issue to be so deep in the core :)
I would like to know whether there is any workaround now for Drupal 8.3.x? Because now it is impossible to use preview of the entity in the widget and even more.
Comment #13
phenaproximaYes, there is: apply the patch in #9 and you should be OK.
Comment #14
phenaproximaChange record added: https://www.drupal.org/node/2893876.
Comment #15
dawehnerWhat confuses me, don't you have the same problem when you have something actually injecting the service, which is sort of the recommendation?
Comment #16
phenaproximaYes. If you explicitly inject the service without DependencySerializationTrait, you will encounter the exception during serialization (if locale is enabled, of course).
As I said in #7, though: without this patch, that condition could happen essentially by accident if you, or anything you are referencing (explicitly or implicitly) uses StringTranslationTrait without DependencySerializationTrait. With this patch, the only way you will encounter the serialization exception is if you (or a parent class you are extending) have explicitly injected the service. Which means you are in a position to actually fix the problem, either by removing the reference to the service, or by importing DependencySerializationTrait.
That is why this is tagged as a DX improvement. The point isn't to prevent the exception entirely -- just to prevent it from happening by accident.
Comment #17
dawehner@phenaproxima
Please put these kind of information into the issue summary, that's where they are really helpful :)
Comment #18
tim.plunkettThe test change from #3 to #7 (no interdiff!) hides the actual bug as discovered: \Drupal\Core\Entity\EntityType needs to use DependencySerializationTrait.
Instead of fixing that, this slightly mitigates the landmine of serialization by preventing it in the implicit case but not the explicit case of string translation. Furthermore, this is a pretty needless BC break, and removes a micro-optimization.
My recommendation in IRC was to introduce a new version of STT that incorporates DST. It cannot be added to the existing trait due to PHP's collision handling logic. That part of the conversation didn't make it to the issue.
TL;DR why not fix the bug instead of hiding it deeper?
Comment #19
phenaproximaClarifying the IS a bit.
Comment #20
phenaproximaIn my view, this is acceptable from a DX standpoint. Because, again, with the way things currently are, you may have no way to fix the problem if you encounter it. With the proposed fix, you do. That makes a world of difference.
StringTranslationTrait::getStringTranslation() is a protected non-API method, so as far as I know, this is an acceptable change under the BC policy. And yes, it removes a micro-optimization...that causes a bug. Why would we want to keep weird, buggy, sometimes-unfixable behavior in the name of micro-optimization?
I'm not against having a new trait (I imagine it could be called SerializableStringTranslationTrait), but that is a messier solution, in my opinion, and introduces more BC concerns down the line.
Comment #21
tim.plunkettOpened #2893917: Create a version of StringTranslationTrait that uses DependencySerializationTrait as the follow-up.
Here's the fix from #9 but with the test from #3 (which is more correct).
And here's the correct short-term fix, until the above issue is resolved.
Comment #22
phenaproximaAdding #2893917: Create a version of StringTranslationTrait that uses DependencySerializationTrait as a related issue.
Comment #25
s_leu commentedTested the patch with the latest lightning release and it works fine. Also created this followup to get the patch into lightning: #2902229: Add patch for "StringTranslationTrait consumers cannot be reliably serialized"
Comment #26
dawehnerI went through all classes using StringTranslationTrait, but don't have DependencySerializationTrait ...
Given that I'm wondering whether it would be just better to solve just #2902229: Add patch for "StringTranslationTrait consumers cannot be reliably serialized" and commit a patch there.
Comment #28
seanbThe patch
2893029-full-fix-test-from-3.patchfixed a fatal error for me during the upgrade to Drupal 8.5. This is quite possibly related to a contrib module, but I can imagine there are going to be more people having this issue. Is there a possibility to fix this before the 8.5 release?Comment #29
dunebl#21 (2893029-full-fix-test-from-3.patch) doesn't solve my problem.
I still get:
LogicException: The database connection is not serializable. This probably means you are serializing an object that has an indirect reference to the database connection. Adjust your code so that is not necessary. Alternatively, look at DependencySerializationTrait as a temporary solution. in Drupal\Core\Database\Connection->__sleep() (line 1472 of /var/www/html/drupal8/core/lib/Drupal/Core/Database/Connection.php).I get this error when clicking on the edit button of a paragraphs widget (when editing a node)
Comment #30
qzmenkoChanged status because of problem in #29
Comment #32
berdirJust because it didn't fix your problem doesn't mean that this isn't working.
There are many possible causes for such a serialization error, a related issue that's basically the same as this with TypedDataTrait is #2986735: Drupal\Core\Plugin\Context\Context needs DependencySerializationTrait, so maybe that will help with your specific problem. But it could be anything, try tracking it down a specific widget/field type if my patch over there doesn't help here.
We have several confirmed reports that this is helping and there are few things more annoying and harder to debug & fix than those kind of serialization errors.
Comment #33
catchNeeds a re-roll.
Comment #34
phenaproximaRerolled against 8.7.x.
Comment #35
phenaproximaRestoring RTBC since this was a straight re-roll and I expect all backends to be green.
Comment #36
sam152 commentedQuick review of this.
Is this really in the locale group/module? The 'string_translation' service is in core and so is
StringTranslationTrait.Edit: re-read the issue summary, locale is required to be installed to reproduce this. Fine for this to stay here.
The test passes/failes without any of these modules installed.
Missing a docblock, not sure if that's intentional.
Any reason we don't immediately also unserialize this in the test as well? Would be good to see the string translation operable after the unserialization too.
Comment #37
sam152 commentedRethinking #36.1, this is really a bug in the
EntityTypeclass, it's only exposed bylocale, but would be triggered with any non-serializeablestring_translationservice. For that reason, we can move the test to a more appropriate place in core and decouple it from locale entirely with a unit test:I understand this is late feedback on a long-standing/RTBC issue which has limited time to get into 8.6, so please take this as observations/suggestions. If the preferred approach is #34 for now, please disregard.
Comment #38
sam152 commentedImplementation of #37.
Comment #39
amateescu commentedThe test in #38 looks great to me.
Comment #41
alexpottAdding credit
Comment #42
alexpottHopefully once we're on PHP7 (as is likely for 8.7.x) - we can add DependencySerializationTrait to StringTranslationTrait.
Before we commit this the issue title (I've had a go) and the issue summary need to be brought into line with the final patch. Imo we should get this into 8.6.x because it does improve and fix things for people.
Comment #43
phenaproximaGroomed the IS a bit.
Comment #44
alexpottAs a major bug fix committed to 8.6.x.
Committed f4e81e0 to 8.7.x and a367e2c to 8.6.x. Thanks!
I used http://grep.xnddx.ru/search?text=extends+EntityType&filename= to ensure nothing in contrib actually extends EntityType
Comment #47
tacituseu commentedThis introduced test failures on PHP 7.2:
- https://www.drupal.org/pift-ci-job/1036370
- https://www.drupal.org/pift-ci-job/1036469
Comment #50
alexpott@tacituseu thanks! reverted this - very strange though that only 2 tests cause this.
Comment #51
alexpottLolz... so core/modules/system/tests/fixtures/update/drupal-8.language-enabled.php has references to a class that was removed before 8.0.0 - nice. Changed
Drupal\Core\StringTranslation\TranslatableStringtoDrupal\Core\StringTranslation\TranslatableMarkup.It is interesting that only PHP 7.2 found this bug. I'm guessing this is due to performance improvements and when cache clearing occurs - but this is just a guess.
Comment #52
sam152 commentedNice sleuthing. Also did a quick grep, this looks like the only left over occurrence.
Comment #53
megachrizNitpick: the description of the test method could be formulated better:
Tests if the EntityType object is serializable.
Could be changed on commit.
Comment #54
alexpottLet's try this again. Committed and pushed 02330aaa20 to 8.7.x and a58773c021 to 8.6.x. Thanks!
Fixed test comment.
Comment #57
idebr commented@alexpott I updated the 'Version' to the earliest branch this commit is available in. This information relevant for projects that are built with patches from Drupal.org and that update to a new minor version (8.5.x -> 8.6.x), since it indicates the issue has been fixed in the project itself and the patch can be removed.
Comment #59
joseph.olstadfor D7:
#3093110: D7 - EntityType objects cannot be reliably serialized without DependencySerializationTrait
using php 7.3.8.1 I noticed an issue in one of my clients sites on a particular serialized data which is included in a test php file in the aforementioned issue. all you need is a command line to test. I think the issue is actually in the serialize() function. php 7.2.x and previous releases didn't throw an error , I tested the same client site on earlier versions of php prior to 7.3.8.1 and no issue what so ever (that I detected anyway).
Comment #60
joseph.olstadConfused, why is this issue marked as "Fixed"?
patch 9 still applies cleanly to the head of 9.1.x and has tests!?
Comment #61
quietone commentedpublish change record