Description
Side remark: There seems to be no CHANGELOG.md that I could consult prior to reporting the BC break
BC Break Report
The \Laminas\Cache\Pattern\ObjectCache
class changed the way it creates the cache key between V3 and v4.0.0.
Q | A |
---|---|
Version | 4.0.0 up to 4.1.0 |
Summary
V4.0.0 and up broke apart the inheritance chain of ObjectCache
and CallbackCache
. This introduced a significant behaviour change when using ObjectCache
which may make it impossible to create cache hits anymore.
Previous behavior
At the end of executing ObjectCache::call
, the call is passed to CallbackCache
as parent::call()
, and there the line $key = $this->generateCallbackKey($callback, $args);
does jump back to ObjectCache::generateCallbackKey()
, which returns the name of the cached object class and the method as a string.
Current behavior
Starting with V4.0.0, there is no inheritance chain, and above mentioned line jumps to CallbackCache::generateCallbackKey()
, which does quite some logic.
The most obvious difference is that now the object that is to be cached is passed to \serialize($object)
to create the cache key. This is bad for multiple reasons, as the object tree that is actually to be cached might be quite large.
But the obvious problem we encountered is that inside this object tree that is supposed to execute a SOAP call, we have a profiler class that stores the current time. Said time stamp now is part of the cache key, and will change, effectively disabling the cache.
As a side effect, I discovered that the logic creating the callback key explicitly mentions Closure
as a case in the comment, but does not prevent that Closure
to be serialized later - which PHP still cannot do. Again, closures embedded in the object tree will prevent caching as they cannot be serialized with CallbackCache
, compared to the previous code in ObjectCache
.
How to reproduce
I'll try to create a test case with details.
In summary, any object stack that contains dynamic data in properties will yield changing cache keys.
Injecting this class to be cached may work when executed in the same runtime context, but will fail when used in a web server context.
class Foo {
private int $time;
public function __construct()
{
$this->time = time();
}
public function cachedMethod(): string
{
return "some_cacheable_value";
}
}
If a closure is present in any property, serialization will fail.