Problem/Motivation
#2459819: Remove CacheableInterface (and no longer let block plugins implement it) introduced RendererInterface::addDependency(array $build, CacheableDependencyInterface $object
). That's great.
But many objects can optionally implement CacheableDependencyInterface
. Which means that in many cases, before calling ::addDependency()
, we need to check that the depended object actually does implement that interface. Which makes the DX atrocious. We absolutely need this DX to be super simple.
This is the case for field definition objects for example: some are config entities (and thus implement CacheableDependencyInterface
), others aren't (and thus don't). But the best example are access results: AccessResultInterface
is the only required interface, but most (though not all!) access result objects actually also implement CacheableDependencyInterface
. For the code to be correct, again, we currently need to do an if-test before calling ::addDependency()
. That's terrible.
Proposed resolution
Remove addDependency()
's typehint and do an instanceof CacheableDependencyInterface
instead.
Remaining tasks
None.
User interface changes
None.
API changes
API relaxation, no breaking API change.
Comment | File | Size | Author |
---|---|---|---|
#14 | renderer_add_dependency-2464877-14.patch | 6.08 KB | Wim Leers |
Comments
Comment #1
Wim LeersExtracted from #2099137-133: Entity/field access and node grants not taken into account with core cache contexts.
Comment #2
Wim LeersThis is problematic, because it doesn't apply to everything.
E.g.:
AccessResultInterface
objects that aren't cacheable.EntityFormDisplay
, we are currently still doing this:We could remove that if-statement too, but then we'd effectively be setting
max-age = 0
on this form, which doesn't make sense. Because in this case, we have e.g.BaseFieldDefinition
, which is a never-changing object. Which meansmax-age = 0
is absolutely not what we want: the form should still remain cacheable!The difference between the two examples is that the access result is dynamically calculated (and is therefore not cacheable without it providing cacheability metadata, i.e. without implementing
CacheableDependencyInterface
). But the field definition is hardcoded, and is thus permanently cacheable.The question is: how do we fix this? The simplest possible solution is to special case
AccessResultInterface
to getmax-age = 0
. The architecturally more sound choice would be to add aDynamicallyCalculatedInterface
orUncacheableByDefaultInterface
and letAccessResultInterface
extend that. But that seems like a whole can of worms.Any ideas?
Comment #3
Fabianx CreditAttribution: Fabianx for Drupal Association commented"- The simplest possible solution is to special case AccessResultInterface to get max-age = 0."
Yes, lets do that. This matches the code that was there before.
( btw. funny side effect: I thought of _exactly_ that scenario when reading the IS, then saw that you had the same thought and my solution was the same as above.)
Lets keep it simple.
Comment #4
Wim LeersHah! :)
Will do that, then.
Comment #5
effulgentsia CreditAttribution: effulgentsia commentedI disagree with special casing AccessResultInterface. If we call addDependency(), it's because we depend on the object, and if that object can't tell us its cacheability metadata, we need to assume it's not cacheable.
If we want to remove the wrapping if statement of #2.2, then let's do it by making BaseFieldDefinition implement CacheableDependencyInterface. Perhaps we can have a trait that implements the 3 methods to return no contexts, no tags, and permanent age?
Comment #6
Wim LeersInteresting. In #2, I only looked at it from one angle. #5 basically presents the inverse angle. #5 basically says that we should set max-age = 0 if
CacheableDependencyInterface
isn't implemented. And it saysBaseFieldDefinition
should simply implementCacheableDependencyInterface
. Which makes a lot of sense.The implication of #5 is: .
It's hard to argue with that, since it just means that all things that can implement
CacheableDependencyInterface
should.Comment #7
Fabianx CreditAttribution: Fabianx for Acquia commentedLove the idea, but the name is debatable.
What about:
PermanentCacheableDependencyTrait?
---
Besides "naming things": This is really great! Lets get this in!
Comment #8
Wim LeersI think "permanent" doesn't capture as many semantics as "unchanging". "permanent" clearly alludes to max-age, but "unchanging" also points out that there are no invalidations (no cache tags) and no variations (no cache contexts), and that it can thus be permanently cached (max-age == permanent).
Comment #9
Fabianx CreditAttribution: Fabianx for Acquia commentedRTBC then, can't think of a better name.
Comment #10
effulgentsia CreditAttribution: effulgentsia at Acquia commentedUnchanging
works for me. RTBC+1. Synonyms for "unchanging" include "static" and "constant", but I'm torn on whether either would be an improvement given that those words already have additional specific meaning in PHP.Comment #11
Wim Leers#10: exactly!
Comment #12
alexpottThis is missing tests afaics
Comment #13
Wim LeersThere is nothing to test. This is just a trait to implement 3 interface methods declaratively, with zero logic.
See #8 and #10 for why this name was chosen. The return values match the semantics of the word "unchanging".
Comment #14
Wim Leers#12 pointed out that we're modifying
BaseFieldDefinition
, but not properly explaining why.CacheableDependencyInterface
, because you can only callRendererInterface::addDependency
with objects that implement that interface. The goal of this issue is to remove that requirement: any object can be passed, but objects that don't implement that interface are automatically considered uncacheable.BaseFieldDefinition
doesn't implementCacheableDependencyInterface
yet is still cacheable. To which #5 said, and what this patch does: let's make that then implement that interface! And add a trait at the same time, because many things in Drupal are defined in code like that and therefore have unchanging cacheability.Clearly, these are simply separate problems! I'm moving the trait and the changes to
BaseFieldDefinition
into its own issue. Did that: #2467627: Field(Storage)DefinitionInterface should implement CacheableDependencyInterface.Re-uploading this patch without the trait and without the changes to
BaseFieldDefinition
, restoring to RTBC state.Comment #15
Fabianx CreditAttribution: Fabianx for Drupal Association commentedRTBC + 1
Comment #16
alexpottThis issue addresses a major bug and is allowed per https://www.drupal.org/core/beta-changes. Committed a4e51c8 and pushed to 8.0.x. Thanks!
Comment #18
Wim LeersYay! This unblocked, now rerolled without this patch included: #2099137-157: Entity/field access and node grants not taken into account with core cache contexts.
Also updated the CR at https://www.drupal.org/node/2460819.
Comment #19
Wim LeersOh, it also unblocked #2467627: Field(Storage)DefinitionInterface should implement CacheableDependencyInterface.