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+ */
317RequestCache requestCache;
418EventCacheStack 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+ */
629void 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+
1455void 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+ */
3282ScopedEventContext::ScopedEventContext () {
3383 eventCacheStack.Push ();
3484}
0 commit comments