Global tracking epic for making z-engine work correctly — or fail loudly and documentedly — wherever opcache is enabled. All opcache tickets are attached as sub-issues; this body carries the goal, the possible/impossible boundary, and the roadmap grouping.
Goal
A consumer running opcache.enable=1 (CLI, FPM, preload) gets one of exactly two behaviours from every z-engine API: it works (via the copy-out model, map-ptr indirection, file-cache patching, …) or it throws a named exception and is documented in the support matrix (docs/hot-swap.md, docs/opcache-binary.md). Silent no-ops — the failure mode of #238 — are treated as bugs.
Current state
Runtime SHM copy-out (classes, methods, global functions) shipped and matrix-tested (docs/hot-swap.md); preloaded classes, enums/interfaces/traits, hooked-property classes and internal ancestors are documented refusals.
File-cache binary API shipped (feat(opcache): binary file API — read, patch and refresh opcache file-cache #111 ): read/patch/save/refresh of .bin payloads on linux/darwin x64 NTS, with loud unsupportedPayload refusals for the not-yet-ported shapes.
CI runs opcache gates on every leg (child-process activation, --fail-on-skipped, ci: enable opcache (incl. shared memory) so OpCache/SHM tests actually run #124 closed) and — since PR feat(opcache): hold the suite under an opcache-active runner — CI job, lazy-linking guard, static-vars and introspection fixes #244 — a dedicated Linux job with opcache active in the runner process itself (composer test:opcache-runner), the environment consumers actually have. Bug-collection runs behind that PR produced the bug(opcache): a write_property handler installed from an interface_gets_implemented hook never fires when opcache is enabled #238 –bug(opcache): registered module entry is not visible when opcache is active in the process (persistent-heap request-cycle scenario) #243 batch below.
Possible vs. impossible
Achievable (tracked here): relocator payload coverage (#112 –#117 ), ZTS relocator (#118 ), macOS/arm64 relocator (#119 ), SHM publication of patched binaries (#121 , long-horizon), hot-swap wiring (#122 ), offset validation + trust model (#123 ), SHM refresh() integration test (#125 ), PHPStan shapes (#126 ), handler installation during lazy linking via inheritance-cache decline (#241 — the real fix for #238 ), first-redefine()-under-opcache (#242 ), module-entry visibility under opcache (#243 ), live static-variables reads (#239 , fixed in PR #244 ), optimizer-aware frame introspection (#240 , fixed in PR #244 ).
Impossible or intentionally out of scope (throw + document):
Windows opcache support — deliberate non-goal. The relocator throws OpCacheException::unsupportedPayload on Windows; feat(opcache): macOS/arm64 support for the relocator (absolute-address oplines) #119 was rescoped to macOS/arm64; docs/opcache-binary.md records the policy.
Sharing process-local FFI trampolines across workers — fundamentally unsound: a handler address published into SHM/the inheritance cache dangles in sibling FPM workers. Handler features must stay process-local (why feat(opcache): decline inheritance-cache publication for classes that receive handlers during linking #241 declines the cache and the default_object_handlers route was rejected).
Preventing the opcache optimizer from rewriting op_arrays — not controllable from userland; introspection reports the optimized truth, documented with the opcache.optimization_level=0 recommendation (feat(reflection): optimizer-aware frame-variable presence/enumeration API on ExecutionData #240 ).
Mutating preloaded classes — refused by design (bucket outlives request memory).
32-bit builds — permanently out for the relocator.
Roadmap grouping
Correctness under active opcache (bugs found by the runner job): bug(opcache): a write_property handler installed from an interface_gets_implemented hook never fires when opcache is enabled #238 → feat(opcache): decline inheritance-cache publication for classes that receive handlers during linking #241 , bug(opcache): the first redefine() does not take effect when opcache is active in the process (RedefineLeakPlateauTest child) #242 , bug(opcache): registered module entry is not visible when opcache is active in the process (persistent-heap request-cycle scenario) #243 ; done: bug(opcache): getStaticVariables() reads the defaults table instead of the live static_variables_ptr table for opcache-shared functions #239 , feat(reflection): optimizer-aware frame-variable presence/enumeration API on ExecutionData #240 (PR feat(opcache): hold the suite under an opcache-active runner — CI job, lazy-linking guard, static-vars and introspection fixes #244 ).
Relocator payload coverage : feat(opcache): relocate intersection/union type lists in cache binaries #112 , feat(opcache): relocate PHP 8.4 property hooks in cache binaries #113 , feat(opcache): relocate trait-using classes in cache binaries #114 , feat(opcache): relocate closures/arrow functions (dynamic_func_defs) in cache binaries #115 , feat(opcache): relocate iterator-aware and ArrayAccess classes in cache binaries #116 — prerequisites for the graph serializer feat(opcache): support graph-growing mutations in the cache serializer #117 .
Platform envelope : feat(opcache): ZTS support for the file-cache relocator #118 (ZTS), feat(opcache): macOS/arm64 support for the relocator (absolute-address oplines) #119 (macOS/arm64); Windows intentionally excluded.
Hardening & tests : opcache: bounds-validate relocation offsets and document the trust model #123 (bounds/trust model), test(opcache): integration-test refresh() under real opcache shared memory #125 (SHM refresh() integration), chore(opcache): replace path-scoped PHPStan ignores with struct shapes #126 (PHPStan shapes).
The headline unlock : feat(opcache): load patched binaries into opcache shared memory (ZCSG) #121 (ZCSG publication) → feat(opcache): wire the binary-patch pipeline to the hot-swap API #122 (hot-swap wiring) — patched binaries applied to live code.
Related (not sub-issues)
Done / history
Global tracking epic for making z-engine work correctly — or fail loudly and documentedly — wherever opcache is enabled. All opcache tickets are attached as sub-issues; this body carries the goal, the possible/impossible boundary, and the roadmap grouping.
Goal
A consumer running
opcache.enable=1(CLI, FPM, preload) gets one of exactly two behaviours from every z-engine API: it works (via the copy-out model, map-ptr indirection, file-cache patching, …) or it throws a named exception and is documented in the support matrix (docs/hot-swap.md,docs/opcache-binary.md). Silent no-ops — the failure mode of #238 — are treated as bugs.Current state
docs/hot-swap.md); preloaded classes, enums/interfaces/traits, hooked-property classes and internal ancestors are documented refusals..binpayloads on linux/darwin x64 NTS, with loudunsupportedPayloadrefusals for the not-yet-ported shapes.--fail-on-skipped, ci: enable opcache (incl. shared memory) so OpCache/SHM tests actually run #124 closed) and — since PR feat(opcache): hold the suite under an opcache-active runner — CI job, lazy-linking guard, static-vars and introspection fixes #244 — a dedicated Linux job with opcache active in the runner process itself (composer test:opcache-runner), the environment consumers actually have. Bug-collection runs behind that PR produced the bug(opcache): a write_property handler installed from an interface_gets_implemented hook never fires when opcache is enabled #238–bug(opcache): registered module entry is not visible when opcache is active in the process (persistent-heap request-cycle scenario) #243 batch below.Possible vs. impossible
Achievable (tracked here): relocator payload coverage (#112–#117), ZTS relocator (#118), macOS/arm64 relocator (#119), SHM publication of patched binaries (#121, long-horizon), hot-swap wiring (#122), offset validation + trust model (#123), SHM refresh() integration test (#125), PHPStan shapes (#126), handler installation during lazy linking via inheritance-cache decline (#241 — the real fix for #238), first-
redefine()-under-opcache (#242), module-entry visibility under opcache (#243), live static-variables reads (#239, fixed in PR #244), optimizer-aware frame introspection (#240, fixed in PR #244).Impossible or intentionally out of scope (throw + document):
OpCacheException::unsupportedPayloadon Windows; feat(opcache): macOS/arm64 support for the relocator (absolute-address oplines) #119 was rescoped to macOS/arm64;docs/opcache-binary.mdrecords the policy.default_object_handlersroute was rejected).opcache.optimization_level=0recommendation (feat(reflection): optimizer-aware frame-variable presence/enumeration API on ExecutionData #240).Roadmap grouping
Related (not sub-issues)
ClassSpecializerSlotTestcases from the runner job).ClassSpecializer::evict()address-keyed cache staleness: same address-keyed-map family as bug(opcache): a write_property handler installed from an interface_gets_implemented hook never fires when opcache is enabled #238; gets the opcache label if confirmed to manifest under opcache.Done / history