Am 18.05.2026, 13:00:02 schrieb Go Kudo <[email protected]>: > > > 2026年5月17日(日) 5:14 Larry Garfield <[email protected]>: > >> On Sat, May 16, 2026, at 10:19 AM, Go Kudo wrote: >> > Hi internals, >> > >> > I'd like to start the discussion for a new RFC, OPcache Static Cache. >> > >> > RFC: https://wiki.php.net/rfc/opcache_static_cache >> > Implementation: https://github.com/php/php-src/pull/22052 >> > >> > The proposal adds an OPcache-managed shared-memory cache for explicit >> > userland values and for selected PHP static state. It introduces >> > explicit functions under the OPcache namespace (volatile_* and >> > persistent_*) and two attributes, #[OPcache\VolatileStatic] and >> > #[OPcache\PersistentStatic], that let selected static properties and >> > method static variables survive across requests. The feature is >> > disabled by default and only activates once memory is allocated through >> > the new INI directives. >> > >> > The RFC covers the motivation, the deliberate split between the two >> > backends, the trust model (one PHP runtime = one trust domain; this is >> > not a tenant isolation boundary), and benchmarks against APCu on NTS >> > php-fpm and ZTS FrankenPHP. The PR is the full implementation, with >> > PHPT coverage summarized in the Validation section. >> > >> > One thing to flag on the implementation status: the Windows build is >> > currently broken. I don't have a Windows development environment >> > available yet — one is being arranged through work, and I'll get the >> > Windows side fixed once that's in place. >> > >> > Feedback welcome. >> > >> > Best Regards, >> > Go Kudo >> >> Interesting! I can definitely see uses for it, and I appreciate the >> level of detail in the RFC. >> >> Some thoughts, though: >> >> - atomic-decrement throwing if a value doesn't exist sounds like a >> footgun. For something like an up/down voting widget, I could very easily >> see someone hitting the Down Vote button first, which would cause it to >> crash. Having to always check _exists() on decrement but not on increment >> is inconsistent and likely to confuse people. >> >> - Are either of these stores purged on reboot? >> >> - "persistent cache and returns void on success." - No, returns null. >> You can't return void. A function can have a void return type, whether >> it's successful or not. Not quite the same thing. >> >> - Having the locks automatically self-unlock sure sounds elegant at >> first, but the lack of symmetry in the API feels very error prone. People >> will want to use it like a transaction, but since it unlocks on the first >> write there's no way to make it one. It just silently unlocks if certain >> functions are called. But if they're not called, there's no way to unlock >> it. That's even more of an issue in a persistent-process use case, where >> you could easily not hit the process-end for minutes or hours, so the lock >> never automatically clears. >> >> Use case: You need to update some lookup table, so you lock the stored >> key, compute the new table, then write the new table. But if the compute >> step fails for some reason, you now have a locked value with no way to >> unlock it, but no new value to write to it. It's better in many cases to >> leave the stale data there rather than delete it, but this API doesn't >> offer a way to do that. >> >> - It's not made clear: Do objects have their __serialize() methods called >> when storing (and vice versa on load), or no? "They have to be >> serializable" is not something that can be otherwise determined. >> >> - Status API: Uh, what are the keys? No arrays here please. Please make >> it an object with defined readonly properties. Please. >> >> - You realize you're effectively adding a Memoize attribute to PHP by >> another name, right? Just making sure. :-) >> >> - A property using the volatile-tracking strategy, if run in a persistent >> process, seems like it would never get written. That feels like a problem. >> >> - The section on write times is rather abstract and academic, so a bit >> hard to follow. If I read correctly, though, it means that writing to a >> sub-property of an array/object on a cached property won't trigger a >> resave? That feels like another footgun waiting to happen. >> >> - It's not clear if there's a way to clear an attribute-cached value >> other than nuking the entire volatile/persistent cache. Is there not? It >> feels like there should be one... Especially for "persistent," I don't >> want to have to nuke my entire "persistent" cache from orbit because one >> value got corrupted somehow. >> >> - Defaulting off... I can see the argument for that, but that means it >> will be off for most users. That means I, as a library author of, say, a >> routing system or a DI container, cannot use it, because I have to assume >> most users won't have it. That kneecaps the usefulness of this feature >> dramatically. I would strongly recommend setting at least some default-on >> amount, even if it's only the minimum 8 MB, if we want this feature to >> actually be used. (And I can already think of a few places where I'd want >> to use it myself.) >> >> - Related, what happens if they're disabled but someone tries to use this >> functionality? Does it operate like every read is a cache miss, or does it >> error? If the latter, that means any code that uses the attributes >> REQUIRES that the ini directives be turned on. We generally try to avoid >> this kind of "your code may or may not work depending on ini settings" >> issues. (Hello, magic_quotes!) >> >> - What's the development experience with this? Frequently, in dev mode >> frameworks will disable caches. If the volatile cache just misses silently >> that would work there, but not for the persistent cache. How would I have >> a persistent route cache that is automatically rebuilt on every request >> during development while I'm messing with routes? >> >> - I understand the value of keeping it simple by making it >> single-tenant. However, I can very easily see different 3rd party >> libraries wanting to make use of the cache at the same time. That poses a >> risk of key-space collision, though that's resolvable by a convention to >> use a key prefix. What it does not resolve is cases where one library >> wants to wipe-and-rebuild a dynamic list of keys, but some other library >> isn't expecting a total purge. There's a high risk of libraries stepping >> on each other here. >> >> - Currently, this is just a basic key/value store. That's great for many >> things, but not very queryable. This is absolutely scope creep, but would >> there be some way to extend this (in a future RFC, I'm sure) to allow, say, >> a persistent memory-resident SQLite database? Currently you can write one >> to disk, but then you have to deal with disk permissions. A memory >> resident database now is request-specific, so not useful outside of >> testing. It would be lovely if there were some way to extend in that >> direction. >> >> --Larry Garfield >> > > Hi Larry, > > Thank you for the detailed feedback. > > I updated the RFC and the implementation to address the concrete API and > documentation issues you pointed out. > > https://wiki.php.net/rfc/opcache_static_cache > > The main changes are: > > - `persistent_atomic_decrement()` now creates a missing key with -$step, > matching `persistent_atomic_increment()`. > - `volatile_lock()` and `persistent_lock()` now have matching `*_unlock()` > functions. > - The lock APIs also accept an optional lease value, so abandoned builder > reservations can expire even in persistent-worker environments. > - The status APIs now return a read-only `OPcache\StaticCacheInfo` object > instead of arrays. > - The RFC now documents that both static-cache backends are scoped to the > lifetime of the current OPcache static-cache shared-memory segment, and > are > not durable storage. > - The wording around persistent_store() was corrected to describe the void > return type rather than "returning void". > - The RFC now describes when `__serialize()` and `__unserialize()` are > called, and > that userland serialization keeps those object graphs off the fastest > direct/shared-graph path. > - The publication rules for `VolatileStatic immediate` mode, > `VolatileStatic > tracking` mode, and `PersistentStatic` have been expanded. > - `VolatileStatic tracking` is now documented as publishing at PHP request > shutdown, not process shutdown, even under FPM/FrankenPHP/persistent > workers. > - Attribute-backed entries can now be deleted either by loaded class name > or by > documented exact static-property/method-static keys, without clearing the > whole backend. > - I also reran the benchmark matrix from clean NTS FPM, NTS/ZTS CLI, and > ZTS > FrankenPHP builds, and updated the RFC tables. > > Some of the broader points are open questions or design tradeoffs rather > than > direct fixes. > > On the default-off setting, I want to be more direct about where I actually > stand. I would prefer this feature to be default-on. Administrator opt-in > noticeably limits library adoption, and that significantly reduces the > value > of the API as a portable primitive that libraries can rely on being > present. > > What is holding me back is the shared-hosting case. The cache is a single > shared-memory trust domain, so on a host where multiple tenants share one > OPcache segment, default-on without an isolation story could expose those > tenants to each other through the static cache. I do not yet have a design > I > am confident makes default-on safe in that environment, and that is the > only > reason the RFC currently ships with the feature disabled. >
> I would genuinely appreciate your input here. If you (or anyone on the > list) > see a viable path — per-pool / per-SAPI segments, a trust-domain or > namespace > mechanism enforced by the engine, a configuration model where the host opts > in per vhost rather than per server, or something I have not considered — I > would be very happy to rework the proposal around it. If we can land on a > model that is safe for shared hosting, I would gladly flip the default in > this RFC rather than defer it to a follow-up. > There was a post on Reddit recently from someone modifying APCu to be FPM pool based. Maybe thats a helpful reference? https://www.reddit.com/r/PHP/comments/1sg9rln/clevel_apcu_key_isolation_based_on_fpm_pool_names/ https://github.com/Samer-Al-iraqi/apcu-fpm-pool-isolation > > For disabled backends, the explicit APIs fail rather than silently > pretending > to be a miss-only cache. Attribute-backed state falls back to ordinary > request > local static behavior when the corresponding backend is unavailable. I can > add > a short FAQ entry for this development-mode behavior if that would make > the RFC > clearer. > > For multi-library key collisions, the explicit cache remains a shared > namespace > and applications/libraries still need key prefixes, similar to APCu. The > new > attribute deletion and exact-key deletion support should reduce the need > for > whole-backend clears, but this RFC does not add a separate namespace > mechanism. > I left the broader trust-domain/namespace question as an Open Issue, and > it is > closely related to the shared-hosting question above. > > The memory-resident SQLite idea is interesting, but I think it is outside > the > scope of this RFC. This proposal is intentionally a small key/value and > static > state facility first. > > Thanks again. The feedback helped make several parts of the API much more > explicit. > > Best Regards, > Go Kudo >
