API Index — Namespaces, Packages, Reports, Indices
ConditionalRequest
in package
RFC 9110 conditional-request evaluator — a pure, server-free port of
Apache httpd's ap_meets_conditions() (modules/http/http_protocol.c).
The class is intentionally a bag of static, side-effect-free functions: it
reads only the request method, the request headers (If-Match,
If-Unmodified-Since, If-None-Match, If-Modified-Since), and the
representation metadata that a server already computed (ETag,
Last-Modified, plus the current request time). It returns the HTTP status
the caller should emit — nothing more. This keeps it exhaustively unit
testable without an OpenSwoole server.
Apache's precedence is mirrored EXACTLY:
- If-Match NOMATCH -> 412
- If-Unmodified-Since modified -> 412 (NOMATCH just blocks a later 304)
- If-None-Match match -> 304 (GET/HEAD) or 412 (other methods)
- If-Modified-Since not-modified -> 304 (GET/HEAD only)
(Apache's fifth step, If-Range, lives in the Range layer and is not part of this evaluator; the Range middleware owns that comparison.)
ETag comparison follows Apache's ap_find_etag_weak / ap_find_etag_strong
(server/util.c): If-None-Match on GET/HEAD without a Range header uses WEAK
comparison (W/"x" matches "x"); If-Match, and If-None-Match on any other
method or when a Range header is present, use STRONG comparison (a weak tag
on either side never matches).
Table of Contents
Constants
- COND_NOMATCH : mixed = 1
- The condition was present and did not match.
- COND_NONE : mixed = 0
- No conditional header of this kind was present.
- COND_STRONG : mixed = 3
- The condition matched using strong comparison semantics.
- COND_WEAK : mixed = 2
- The condition matched using weak comparison semantics.
Methods
- evaluate() : int
- Evaluate the conditional-request preconditions for a representation.
- findEtagStrong() : bool
- Strong ETag comparison — port of
ap_find_etag_strong/find_list_itemwithAP_ETAG_STRONG. The stored ETag must be strong (start with"), and any weak entries in the request list are skipped. Returns true when the strong stored tag appears in the comma-separated request list. - findEtagWeak() : bool
- Weak ETag comparison — port of
ap_find_etag_weak/find_list_itemwithAP_ETAG_WEAK. Both the stored tag and each request-list entry have anyW/prefix stripped before the opaque quoted-strings are compared, soW/"x"matches"x"and vice versa. - ifMatch() : int
ap_condition_if_match— strong comparison only; '*' matches any tag.- ifModifiedSince() : int
ap_condition_if_modified_since.- ifNoneMatch() : int
ap_condition_if_none_match.- ifUnmodifiedSince() : int
ap_condition_if_unmodified_since.- header() : string|null
- Case-insensitive request-header lookup. Returns the raw value, or null when the header is absent (an empty-string value is treated as present).
- isGetOrHead() : bool
- Whether the method takes the GET/HEAD conditional path (304-eligible).
- isWildcard() : bool
- True when a conditional header value is the
*wildcard (allowing only surrounding whitespace), matching Apache'svalue[0] == '*'test against an already-trimmed header. - parseHttpDate() : int|null
- Parse an HTTP-date into a Unix timestamp, or null when unparseable.
- splitList() : array<int, string>
- Split a comma-separated ETag list header into trimmed, non-empty tokens.
- stripWeak() : string|null
- Strip a leading
W/weak marker from an ETag token. Returns the bare quoted-string, or null when the token is not a valid (quoted) ETag.
Constants
COND_NOMATCH
The condition was present and did not match.
public
mixed
COND_NOMATCH
= 1
COND_NONE
No conditional header of this kind was present.
public
mixed
COND_NONE
= 0
COND_STRONG
The condition matched using strong comparison semantics.
public
mixed
COND_STRONG
= 3
COND_WEAK
The condition matched using weak comparison semantics.
public
mixed
COND_WEAK
= 2
Methods
evaluate()
Evaluate the conditional-request preconditions for a representation.
public
static evaluate(string $method, array<string, string> $reqHeaders, string $etag[, int|null $lastModified = null ][, int|null $requestTime = null ]) : int
Mirrors ap_meets_conditions(). Only invoked for otherwise-successful
(2xx) responses — callers must skip error responses, exactly as Apache
guards on ap_is_HTTP_SUCCESS(r->status).
Parameters
- $method : string
-
The request method (e.g. "GET", "HEAD", "PUT").
- $reqHeaders : array<string, string>
-
Lowercased-or-mixed request header map; only the four conditional headers and "range" are consulted. Lookups are case-insensitive.
- $etag : string
-
The representation's current ETag (may be weak
W/"..."or strong"..."), or '' when none is known. - $lastModified : int|null = null
-
Representation mtime as a Unix timestamp, or null when unknown.
- $requestTime : int|null = null
-
The server's request-receipt time as a Unix timestamp; defaults to
time(). Used as the upper bound that makes future-dated If-Modified-Since / If-Unmodified-Since values invalid.
Return values
int —The HTTP status the caller should emit: 200 (proceed), 304 (Not Modified), or 412 (Precondition Failed).
findEtagStrong()
Strong ETag comparison — port of ap_find_etag_strong / find_list_item
with AP_ETAG_STRONG. The stored ETag must be strong (start with "),
and any weak entries in the request list are skipped. Returns true when
the strong stored tag appears in the comma-separated request list.
public
static findEtagStrong(string $list, string $etag) : bool
Parameters
- $list : string
- $etag : string
Return values
boolfindEtagWeak()
Weak ETag comparison — port of ap_find_etag_weak / find_list_item
with AP_ETAG_WEAK. Both the stored tag and each request-list entry have
any W/ prefix stripped before the opaque quoted-strings are compared, so
W/"x" matches "x" and vice versa.
public
static findEtagWeak(string $list, string $etag) : bool
Parameters
- $list : string
- $etag : string
Return values
boolifMatch()
ap_condition_if_match — strong comparison only; '*' matches any tag.
public
static ifMatch(array<string, string> $reqHeaders, string $etag) : int
Parameters
- $reqHeaders : array<string, string>
- $etag : string
Return values
intifModifiedSince()
ap_condition_if_modified_since.
public
static ifModifiedSince(array<string, string> $reqHeaders, int|null $lastModified, int $requestTime, bool $hasRange) : int
Returns WEAK/STRONG (not-modified -> candidate 304) only when the IMS date
is within [mtime, requestTime]; a future-dated IMS is invalid (NOMATCH).
A Range header forbids the weak (within-60s) outcome.
Parameters
- $reqHeaders : array<string, string>
- $lastModified : int|null
- $requestTime : int
- $hasRange : bool
Return values
intifNoneMatch()
ap_condition_if_none_match.
public
static ifNoneMatch(array<string, string> $reqHeaders, string $etag, bool $isGetOrHead, bool $hasRange) : int
'*' matches unconditionally (STRONG). GET/HEAD without a Range header use weak comparison; any other method, or a Range request, requires strong comparison.
Parameters
- $reqHeaders : array<string, string>
- $etag : string
- $isGetOrHead : bool
- $hasRange : bool
Return values
intifUnmodifiedSince()
ap_condition_if_unmodified_since.
public
static ifUnmodifiedSince(array<string, string> $reqHeaders, int|null $lastModified, int $requestTime, bool $hasRange) : int
NOMATCH means the resource was NOT modified (precondition met). WEAK or STRONG means it WAS modified after the supplied date (precondition fails -> 412). A Range header forbids the weak (within-60s) outcome.
Parameters
- $reqHeaders : array<string, string>
- $lastModified : int|null
- $requestTime : int
- $hasRange : bool
Return values
intheader()
Case-insensitive request-header lookup. Returns the raw value, or null when the header is absent (an empty-string value is treated as present).
private
static header(array<string, string> $reqHeaders, string $name) : string|null
Parameters
- $reqHeaders : array<string, string>
- $name : string
Return values
string|nullisGetOrHead()
Whether the method takes the GET/HEAD conditional path (304-eligible).
private
static isGetOrHead(string $method) : bool
Parameters
- $method : string
Return values
boolisWildcard()
True when a conditional header value is the * wildcard (allowing only
surrounding whitespace), matching Apache's value[0] == '*' test against
an already-trimmed header.
private
static isWildcard(string $value) : bool
Parameters
- $value : string
Return values
boolparseHttpDate()
Parse an HTTP-date into a Unix timestamp, or null when unparseable.
private
static parseHttpDate(string $value) : int|null
Accepts the three RFC 9110 §5.6.7 formats (strtotime covers IMF-fixdate,
RFC 850, and asctime). Empty / malformed values yield null, mirroring
Apache's APR_DATE_BAD.
Parameters
- $value : string
Return values
int|nullsplitList()
Split a comma-separated ETag list header into trimmed, non-empty tokens.
private
static splitList(string $list) : array<int, string>
Commas only ever separate ETags here — they cannot appear unescaped inside the opaque quoted-string of an entity-tag (RFC 9110 §8.8.3).
Parameters
- $list : string
Return values
array<int, string>stripWeak()
Strip a leading W/ weak marker from an ETag token. Returns the bare
quoted-string, or null when the token is not a valid (quoted) ETag.
private
static stripWeak(string $tag) : string|null
Parameters
- $tag : string