Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
This is a follow-up for #2910682: Mark serializer encoders @internal.
Using encoder/normalizer services directly is not supported, one must use the serializer
service, which will then call the appropriate encoder/normalizer services.
However, it is valid to inject a normalizer into another normalizer. Therefore we should only disallow code that does \Drupal::service('some normalizer')
or $this->container->get('some normalizer')
.
Proposed resolution
Mark all encoder and normalizer services private. Which is what Symfony's serialization component should've been doing all along…
Remaining tasks
None.
User interface changes
None.
API changes
None.
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
#3 | 2920536-3.patch | 4.76 KB | Wim Leers |
Comments
Comment #2
Wim LeersImported relevant information from parent issue.
Comment #3
Wim LeersPlease credit @damiankloip, who wrote most of this at #2910682-21: Mark serializer encoders @internal.
Comment #4
borisson_This has tests that are very explicit and they clearly indicate that the functionality is working. This looks like a good way to make this internal functionality not exposed to the outside.
Comment #6
xjmCan we have Grayside's feedback over here as well on whether this resolves his concern?
Because this does look like a BC break. As such, it'd also need a change record.
Comment #7
Wim LeersThanks for crediting Grayside, whose feedback was indeed instrumental. Please also credit @damiankloip as requested in #3, because the patch itself was actually mostly written by him. I just split it off from #2910682: Mark serializer encoders @internal per the reviewers' request.
Comment #8
Wim Leers#2910682: Mark serializer encoders @internal landed in the mean time, yay!
Comment #9
Grayside CreditAttribution: Grayside at Phase2 commentedI'm not sure if I understand the implications of private services well enough to say whether it's a use case problem.
I've read http://symfony.com/blog/new-in-symfony-3-2-improved-private-services and http://symfony.com/doc/current/service_container.html#public-versus-priv..., and if I understand correctly, our goal in making these services private is primarily to avoid them being accessed by static code, but not to prevent their use and abuse when injected as a service. I guess the intention is to inhibit the misuse of normalizers in procedural code?
The case I raised in the previous issue would have developers that need to do awkward things in custom formats with a pattern such as the following pseudo-code:
my_hal/my_hal.services.yml
my_hal/src/Normalizer/EntityReferenceItemNormalizer
If that is correct, then marking these private is fine from a pure capabilities standpoint for developers to effectively vary serialization formats. However, anyone that's previously used
\Drupal::service('serializer.normalizer.field_item.my_hal')
as an ugly hack will of course be burned by an incompatibility. I don't see a way around that here without either adding a B/C layer or deciding that this is an exception case because clearly using the static wrapper to abuse the normalization process was not intended.Also, as I noted in the other issue, I have seen a developer use a complex contrib encoder/decoder to do some data parsing where all they cared about was correctly deriving an intermediate, unclassed data structure. It's less clear in my mind that encoders/decoders should be locked to the serialization system. Is this primarily to ensure higher priority encoders for a given format will be loaded properly for all calling code? That said, if you do need to use an encoder outside serialization, it doesn't seem that big a problem to insist they register the class as a custom service.
Comment #10
Wim LeersCorrect!
Correct!
These services were never meant to be used directly. Could you? Yes, because it's just another service. Not every service is an actual supported API though.
You're absolutely right this is a hard line to walk: either we make things better/more explicit for the future and break perhaps a few custom pieces of code in the process. Or we choose to not break this in the process, but then we end up never making the future better.
They should use
$this->serialize->decode()
for this purpose. There's no need to call encoders/decoders direclty.Yes.
Comment #11
gabesulliceComment #12
larowlan@Grayside - are you happy with the responses from @Wim at #10
Comment #13
Grayside CreditAttribution: Grayside at Phase2 commentedYes, that response clarifies the goals of this change and confirms my code in #9 is the intended direction for edge case private normalizer use.
Comment #14
Wim LeersThanks, @Grayside!
Comment #15
larowlanCan we get a change record here, I suspect this might cause some issues for people who're using them incorrectly.
Comment #16
Wim LeersDone: https://www.drupal.org/node/2936397.
Comment #19
catchCommitted/pushed to 8.6.x, thanks!
This missed the alpha, so leaving it out of 8.5.x for now, will update the change notice when I publish it.
Comment #20
Wim LeersYay, this also helps pave the path for the #2935370: Mark the JSON API serializer, normalizer and encoder services as private JSON API issue!