Skip to content

ObjectCache fails to create consistent cache keys in V4 #355

Open
@SvenRtbg

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.

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions