Skip to content

Commit a085a22

Browse files
Add comments for cache management (EventCacheStack) (#388)
1 parent 1fe3f37 commit a085a22

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

lib/php-extension/Cache.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,31 @@
11
#include "Includes.h"
22

3+
/*
4+
Cache objects used by the PHP extension to share state with the Go request processor.
5+
6+
- RequestCache stores data that is scoped to a single incoming HTTP request
7+
(method, route, user id, rate-limit group, ...). The Go side calls into the
8+
extension once per request to populate and later clear this structure
9+
(see context.Init / context.Clear in the Go code).
10+
11+
- EventCacheStack stores data that is scoped to a single *event*, where an event
12+
is one hooked PHP function call (e.g. curl_exec, file_get_contents, exec,
13+
PDO::query, ...). Each hook invocation pushes a new EventCache on the stack
14+
when it starts and pops it when it finishes, so nested hooks have independent
15+
contexts.
16+
*/
317
RequestCache requestCache;
418
EventCacheStack eventCacheStack;
519

20+
/*
21+
Reset helpers:
22+
23+
These functions re-initialize the cache structs to their default state instead
24+
of reallocating them. The PHP extension code runs inside long-lived PHP/Apache/FPM
25+
processes that handle many HTTP requests. Because these cache objects live for
26+
the lifetime of the process, we must explicitly reset them so that no state
27+
from one request or event can leak into the next.
28+
*/
629
void RequestCache::Reset() {
730
*this = RequestCache();
831
}
@@ -11,6 +34,24 @@ void EventCache::Reset() {
1134
*this = EventCache();
1235
}
1336

37+
/*
38+
EventCacheStack implementation:
39+
40+
The stack holds per-hook event context. Each hook invocation pushes a new
41+
EventCache onto the stack, and pops it when the hook scope ends.
42+
43+
This allows nested hooks (one hooked function calling another) to each have
44+
their own independent context without interfering with each other. Code that
45+
needs the current event context always reads from Top().
46+
47+
Example flow:
48+
1. PRE handler: Push() -> Top().outgoingRequestUrl = "http://example.com"
49+
2. curl_exec() runs, follows redirect
50+
3. Callback invokes file_put_contents() -> Push() (new context on stack)
51+
4. Nested hook completes -> Pop() (outer context restored)
52+
5. POST handler: Top().outgoingRequestUrl still valid -> SSRF check runs
53+
*/
54+
1455
void EventCacheStack::Push() {
1556
contexts.push(EventCache());
1657
}
@@ -29,6 +70,15 @@ bool EventCacheStack::Empty() {
2970
return contexts.empty();
3071
}
3172

73+
/*
74+
RAII wrapper for automatic context management.
75+
76+
Ensures proper push/pop even on exceptions. Used at the start of every hook handler:
77+
- Constructor: Pushes new context onto stack
78+
- Destructor: Pops context when leaving scope (automatic cleanup)
79+
80+
This prevents context leaks and ensures stack integrity.
81+
*/
3282
ScopedEventContext::ScopedEventContext() {
3383
eventCacheStack.Push();
3484
}

0 commit comments

Comments
 (0)