API Index — Namespaces, Packages, Reports, Indices
RequestContext
in package
Per-request state container. Lives on Coroutine::getContext() in
coroutine mode (recommended default) so each request gets isolated
state freed automatically when the coroutine ends. In legacy
superglobals mode it's a process-wide singleton bridging declared
properties to PHP's $_GET / $_POST / $_SESSION etc.
Previously named G — that name remains available via class_alias
at the bottom of this file for backward compatibility. New code
should reference RequestContext.
Table of Contents
Properties
- $_error_handler_in_flight : bool
- Re-entry guards: set true while inside the native dispatcher closure so a nested error/exception fired while running the user-supplied callable falls back to PHP's default handler instead of recursing through our handler until the call stack is exhausted.
- $_exception_handler_in_flight : bool
- $_ob_floor : int|null
- OB level of the framework's capture buffer for the current include — app buffers ABOVE it keep native ob_end_flush()/ob_flush() semantics (pop into parent); at/below it the overrides stream to the client.
- $_session_started : bool|null
- $_streaming : bool|null
- $apacheContext : ApacheContext|null
- $cache_expire : int|null
- $cache_limiter : string|null
-
$cgi_backend_override
: array{mode: string, interpreter?: string, address?: string, fcgi_params?: array
}|null - Per-request CGI backend override set by the matched route's
backend:option inResponseMiddleware::dispatchRoute(), read byApp::include()to pick the dispatch strategy (pool/proc/fork/fcgi+ interpreter/ address) for THIS request's includes.null= no override (fall back toApp::resolveCgiBackend()/ the globalApp::cgiMode()). - $cookie : array<string, mixed>
- $error_exception : Throwable|null
- $error_handlers_stack : array<int, array{0: callable, 1: int}>
- $error_render_depth : int
- $error_reporting_level : int|null
- $error_status : int|null
- $exception_handlers_stack : array<int, callable>
- $files : array<string, mixed>
- $get : array<string, mixed>
- $ignore_user_abort_state : int
- $memo : array<string, mixed>
- $openswoole_request : Request|null
- $openswoole_response : Response|null
- $post : array<string, mixed>
- $psr_request : ServerRequestInterface|null
- $raw_status_code : int|null
- Raw status-line override (#327): set ONLY by the `header("HTTP/1.1
<code> <reason>")` form, which Apache mod_php forwards verbatim —
code AND reason — even for codes outside 100–599. The vendor PSR-7
withStatus()throws on out-of-table codes, so the raw pair rides these side-channel fields andApp::emitEffectiveStatus()overrides the wire status at the emit chokepoints. Any later explicit status set (http_response_code(),Status:header, int return) clears them viaresponse_set_status()— last write wins, like mod_php. - $raw_status_reason : string|null
- $request : array<string, mixed>
- $server : array<string, scalar|null>
- $session : array<string, mixed>
- $session_loaded_keys : array<int, string>
- Keys present in
$g->sessionat session-load time. Letszeal_session_write_close()distinguish an in-requestunset()(key was loaded then removed → must be deleted from the store) from a concurrent add (key never loaded here → must be preserved through the merge).#21. - $session_module_name : string|null
- $session_params : array<string, mixed>
-
$shutdown_functions
: array<int, array{0: callable, 1: array
}> - $status : int|null
- $zealphp_request : Request|null
- $zealphp_response : Response|null
- $instance : self|null
Methods
- __get() : mixed
- Read by reference is only required in superglobals mode, where the
proxy must hand back
$GLOBALS['_SESSION']etc. so legacy code that mutates$_SESSION['k'] = $vcarries the write through. In coroutine mode (recommended default) all reads go through the typed properties declared above; returning by value avoids the autovivification footgun where&$g->nonexistentwould create a property on first read. - __isset() : bool
- ext-zealphp#42 (residual) — keep
isset($g->server)truthful when the slot is an unset-and-proxied superglobal alias. PHP only consults__issetfor inaccessible/uninitialized properties; without it,isset($g->server)reported FALSE in coroutine-legacy and under the #346 Apache bridge even though__getreturned the fully-populated$_SERVER. App-level defensive code reacting to that false negative (if (!isset($g->server)) { $g->server = []; }) then wiped the live$GLOBALS['_SERVER']through__setfor the rest of the request. - __set() : mixed
__setfires for undeclared properties AND for declared typed properties that have beenunset()(the slot is "uninitialized" so direct access routes through__seton assignment). In superglobals mode we keep the legacy bridge to$GLOBALS[$key]so pre-coroutine code that stashed values via$g->custom = $valkeeps working. In coroutine mode the typed properties are the contract; we re-initialize the declared slot (preserves PHP's type-check via direct property assignment) and reject any other write loudly so typos still surface.- __unset() : void
- For the seven proxied superglobal names this is deliberately a NO-OP,
not the symmetric inverse of
__set. The framework itself usesunset($g->server)as "detach the typed slot so the__getproxy takes over" —bridgeSuperglobalSlots(), the per-request populate inApp::run(), and the session managers all do it.__unsetonly fires when the slot is ALREADY detached, so re-running any of those paths (bridge re-entry, request 2+ on a reused context) must not escalate intounset($GLOBALS['_SERVER'])and destroy live request state — exactly the class of wipe this fix exists to prevent. Deleting a whole superglobal through$gis not a real use case; code that means it canunset($GLOBALS['_SESSION'])directly (aszeal_session_write_close()does). Legacy CUSTOM keys keep__setsymmetry:unset($g->custom)drops$GLOBALS['custom']. Coroutine mode is a no-op —__unsetfiring at all means the slot is already uninitialized, andunset()is idempotent by contract. - forget() : void
- Discard the memoized value for
$keyin this request. The nextonce()call with the same key will recompute. - get() : mixed
- has() : bool
- True if
once($key, ...)has been computed in this request. - instance() : self
- once() : mixed
- Compute once per request, cache for the rest of the request.
- set() : void
- __construct() : mixed
- bridgeSuperglobalSlots() : void
- #346 — Apache/mod_php (and any non-OpenSwoole SAPI) bridge: a DECLARED,
default-initialized typed property ("public array $server = []") is
"set", so reads resolve from the empty slot and the __get superglobals
proxy NEVER runs — $g->server / $g->get / $g->request were always []
under plain Apache even though the SAPI populated $_SERVER/$_GET
correctly. Unsetting the request-input slots makes reads AND writes
route through __get/__set, which proxy to $GLOBALS['_SERVER'] etc. by
reference — the same live-alias contract the ZealPHP server's OnRequest
populate establishes per request (there this unset is simply
idempotent; under Apache it is the only place that can establish the
bridge, because no ZealPHP request lifecycle ever runs). Applied to the
process-wide singleton at construction when
App::$superglobalsis on.
Properties
$_error_handler_in_flight
Re-entry guards: set true while inside the native dispatcher closure so a nested error/exception fired while running the user-supplied callable falls back to PHP's default handler instead of recursing through our handler until the call stack is exhausted.
public
bool
$_error_handler_in_flight
= false
$_exception_handler_in_flight
public
bool
$_exception_handler_in_flight
= false
$_ob_floor
OB level of the framework's capture buffer for the current include — app buffers ABOVE it keep native ob_end_flush()/ob_flush() semantics (pop into parent); at/below it the overrides stream to the client.
public
int|null
$_ob_floor
= null
Set/restored by App::executeFile().
$_session_started
public
bool|null
$_session_started
= null
$_streaming
public
bool|null
$_streaming
= null
$apacheContext
public
ApacheContext|null
$apacheContext
= null
$cache_expire
public
int|null
$cache_expire
= null
$cache_limiter
public
string|null
$cache_limiter
= null
$cgi_backend_override
Per-request CGI backend override set by the matched route's backend:
option in ResponseMiddleware::dispatchRoute(), read by App::include()
to pick the dispatch strategy (pool/proc/fork/fcgi + interpreter/
address) for THIS request's includes. null = no override (fall back to
App::resolveCgiBackend() / the global App::cgiMode()).
public
array{mode: string, interpreter?: string, address?: string, fcgi_params?: array}|null
$cgi_backend_override
= null
$cookie
public
array<string, mixed>
$cookie
= []
$error_exception
public
Throwable|null
$error_exception
= null
$error_handlers_stack
public
array<int, array{0: callable, 1: int}>
$error_handlers_stack
= []
stack of [callable, levels]
$error_render_depth
public
int
$error_render_depth
= 0
$error_reporting_level
public
int|null
$error_reporting_level
= null
$error_status
public
int|null
$error_status
= null
$exception_handlers_stack
public
array<int, callable>
$exception_handlers_stack
= []
stack of callables
$files
public
array<string, mixed>
$files
= []
$get
public
array<string, mixed>
$get
= []
$ignore_user_abort_state
public
int
$ignore_user_abort_state
= 0
$memo
public
array<string, mixed>
$memo
= []
$openswoole_request
public
Request|null
$openswoole_request
= null
In tests, this slot may hold a mock — see tests/Unit/RestTest.php
$openswoole_response
public
Response|null
$openswoole_response
= null
In tests, this slot may hold a mock — see tests/Unit/RestTest.php
$post
public
array<string, mixed>
$post
= []
$psr_request
public
ServerRequestInterface|null
$psr_request
= null
The PSR-7 request for the current dispatch; set by ResponseMiddleware::process() so ZealAPI (and other inner layers) can reach the same object the middleware stack used.
$raw_status_code
Raw status-line override (#327): set ONLY by the `header("HTTP/1.1
<code> <reason>")` form, which Apache mod_php forwards verbatim —
code AND reason — even for codes outside 100–599. The vendor PSR-7
withStatus() throws on out-of-table codes, so the raw pair rides
these side-channel fields and App::emitEffectiveStatus() overrides
the wire status at the emit chokepoints. Any later explicit status
set (http_response_code(), Status: header, int return) clears
them via response_set_status() — last write wins, like mod_php.
public
int|null
$raw_status_code
= null
$raw_status_reason
public
string|null
$raw_status_reason
= null
$request
public
array<string, mixed>
$request
= []
$server
public
array<string, scalar|null>
$server
= []
$session
public
array<string, mixed>
$session
= []
$session_loaded_keys
Keys present in $g->session at session-load time. Lets
zeal_session_write_close() distinguish an in-request unset() (key was
loaded then removed → must be deleted from the store) from a concurrent
add (key never loaded here → must be preserved through the merge). #21.
public
array<int, string>
$session_loaded_keys
= []
$session_module_name
public
string|null
$session_module_name
= null
$session_params
public
array<string, mixed>
$session_params
= []
$shutdown_functions
public
array<int, array{0: callable, 1: array}>
$shutdown_functions
= []
queue of [callable, args]
$status
public
int|null
$status
= null
$zealphp_request
public
Request|null
$zealphp_request
= null
In tests, this slot may hold a mock — see tests/Unit/RestTest.php
$zealphp_response
public
Response|null
$zealphp_response
= null
In tests, this slot may hold a mock — see tests/Unit/RestTest.php
$instance
private
static self|null
$instance
= null
Methods
__get()
Read by reference is only required in superglobals mode, where the
proxy must hand back $GLOBALS['_SESSION'] etc. so legacy code that
mutates $_SESSION['k'] = $v carries the write through. In coroutine
mode (recommended default) all reads go through the typed properties
declared above; returning by value avoids the autovivification footgun
where &$g->nonexistent would create a property on first read.
public
& __get(string $key) : mixed
Parameters
- $key : string
__isset()
ext-zealphp#42 (residual) — keep isset($g->server) truthful when the
slot is an unset-and-proxied superglobal alias. PHP only consults
__isset for inaccessible/uninitialized properties; without it,
isset($g->server) reported FALSE in coroutine-legacy and under the
#346 Apache bridge even though __get returned the fully-populated
$_SERVER. App-level defensive code reacting to that false negative
(if (!isset($g->server)) { $g->server = []; }) then wiped the live
$GLOBALS['_SERVER'] through __set for the rest of the request.
public
__isset(string $key) : bool
Superglobals mode mirrors isset($_X) for the seven proxied names —
Apache parity, so isset($g->session) stays FALSE until session_start
creates $_SESSION — and isset($GLOBALS[$key]) for legacy custom
keys (symmetric with __get/__set). Coroutine mode reports FALSE:
__isset firing at all means the typed slot is uninitialized (or the
key undeclared), and zeal_session_status() depends on an unset
$g->session reading as inactive.
Parameters
- $key : string
Return values
bool__set()
__set fires for undeclared properties AND for declared typed properties
that have been unset() (the slot is "uninitialized" so direct access
routes through __set on assignment). In superglobals mode we keep the
legacy bridge to $GLOBALS[$key] so pre-coroutine code that stashed
values via $g->custom = $val keeps working. In coroutine mode the
typed properties are the contract; we re-initialize the declared slot
(preserves PHP's type-check via direct property assignment) and reject
any other write loudly so typos still surface.
public
__set(string $key, mixed $value) : mixed
Parameters
- $key : string
- $value : mixed
__unset()
For the seven proxied superglobal names this is deliberately a NO-OP,
not the symmetric inverse of __set. The framework itself uses
unset($g->server) as "detach the typed slot so the __get proxy
takes over" — bridgeSuperglobalSlots(), the per-request populate in
App::run(), and the session managers all do it. __unset only fires
when the slot is ALREADY detached, so re-running any of those paths
(bridge re-entry, request 2+ on a reused context) must not escalate
into unset($GLOBALS['_SERVER']) and destroy live request state —
exactly the class of wipe this fix exists to prevent. Deleting a whole
superglobal through $g is not a real use case; code that means it
can unset($GLOBALS['_SESSION']) directly (as
zeal_session_write_close() does). Legacy CUSTOM keys keep __set
symmetry: unset($g->custom) drops $GLOBALS['custom']. Coroutine
mode is a no-op — __unset firing at all means the slot is already
uninitialized, and unset() is idempotent by contract.
public
__unset(string $key) : void
Parameters
- $key : string
forget()
Discard the memoized value for $key in this request. The next once()
call with the same key will recompute.
public
static forget(string $key) : void
Parameters
- $key : string
get()
public
static get(string $key) : mixed
Parameters
- $key : string
has()
True if once($key, ...) has been computed in this request.
public
static has(string $key) : bool
Parameters
- $key : string
Return values
boolinstance()
public
static instance() : self
Return values
selfonce()
Compute once per request, cache for the rest of the request.
public
static once(string $key, callable $fn) : mixed
Safe alternative to static $cache = [] inside a function. Computes
$fn() the first time it's called with $key in this request, caches
the result on the per-coroutine RequestContext, returns the cached
value on subsequent calls. The cache is freed automatically when the
coroutine ends — no state survives to the next request.
Mirrors Laravel 11's once() helper. Use this anywhere you'd reach
for static $foo = ... for request-scoped memoization but want to
avoid leaking state into worker process memory.
$user = RequestContext::once('current_user', fn() => Auth::loadUser($id));
Parameters
- $key : string
- $fn : callable
set()
public
static set(string $key, mixed $value) : void
Parameters
- $key : string
- $value : mixed
__construct()
private
__construct() : mixed
bridgeSuperglobalSlots()
#346 — Apache/mod_php (and any non-OpenSwoole SAPI) bridge: a DECLARED,
default-initialized typed property ("public array $server = []") is
"set", so reads resolve from the empty slot and the __get superglobals
proxy NEVER runs — $g->server / $g->get / $g->request were always []
under plain Apache even though the SAPI populated $_SERVER/$_GET
correctly. Unsetting the request-input slots makes reads AND writes
route through __get/__set, which proxy to $GLOBALS['_SERVER'] etc. by
reference — the same live-alias contract the ZealPHP server's OnRequest
populate establishes per request (there this unset is simply
idempotent; under Apache it is the only place that can establish the
bridge, because no ZealPHP request lifecycle ever runs). Applied to the
process-wide singleton at construction when App::$superglobals is on.
private
static bridgeSuperglobalSlots(self $instance) : void
Parameters
- $instance : self