This extension provides server-side request and response objects for PHP.
These are not HTTP message objects proper. They are more like wrappers for existing global PHP variables and functions.
This extension defines four classes and one interface in the global namespace:
-
SapiRequest, composed of read-only copies of PHP superglobals and some other commonly-used values.
-
SapiUpload, a value-object style descriptor of each uploaded file.
-
SapiResponse and SapiResponseInterface, essentially a wrapper around (and buffer for) response-related PHP functions.
-
SapiResponseSender, for sending a SapiResponse.
In most cases, it is easiest to install via PECL:
# pecl install request
Alternatively, clone or otherwise download this repository; then, in the repository directory, issue these commands to compile and install the extension:
$ phpize
$ ./configure
$ make
# make install
Finally, enable the request.so
extension in your php.ini
file, and restart your web server.
An object of public read-only properties representing the PHP request received
by the server. Use it in place of the $_GET
, $_POST
, etc. superglobals. It
provides:
-
non-session superglobals as public, immutable, read-only properties;
-
other public, immutable, read-only properties calculated from the superglobals (
$method
,$headers
,$accept
,$uploads
, etc.);
Note that SapiRequest can be extended to provide other userland functionality; however, the public properties cannot be modified or overridden.
Instantiation of SapiRequest is straightforward:
$request = new SapiRequest($GLOBALS);
If you want to provide custom superglobal values to the object, pass an array
that mimics $GLOBALS
to the constructor:
$request = new SapiRequest([
'_SERVER' => [
'foo' => 'bar',
],
]);
By default, the $content
property will read from php://input
on-the-fly. If
you want to provide a custom $content
string instead, pass it as the second
constructor argument:
$request = new SapiRequest(
$GLOBALS,
'custom-php-input-string'
);
N.b.: It is up to you to make sure the various content-related header values in
$GLOBALS
match the custom$content
string.
SapiRequest has these public properties.
These properties are public, immutable, read-only, and cannot be modified or overridden.
?array $cookie
: A copy of$_COOKIE
.?array $files
: A copy of$_FILES
.?array $input
: A copy of$_POST
.?array $query
: A copy of$_GET
.?array $server
: A copy of$_SERVER
.?array $uploads
: A copy of$_FILES
, restructured to look more like$_POST
; instead of array descriptors, the elements are instances of SapiUpload.
The SapiRequest $files
property is an identical copy of $_FILES
. Normally,
$_FILES
looks like this with multi-file uploads:
// $_FILES ...
[
'images' => [
'name' => [
0 => 'image1.png',
1 => 'image2.gif',
2 => 'image3.jpg',
],
'type' => [
0 => 'image/png',
1 => 'image/gif',
2 => 'image/jpeg',
],
'tmp_name' [
0 => '/tmp/path/phpABCDEF',
1 => '/tmp/path/phpGHIJKL',
2 => '/tmp/path/phpMNOPQR',
],
'error' => [
0 => 0,
1 => 0,
2 => 0,
],
'size' =>[
0 => 123456,
1 => 234567,
2 => 345678,
],
],
];
However, that structure is not at all what we expect when we are used to
working with $_POST
. Therefore, the SapiRequest $uploads
property
restructures the data in $_FILES
to look more like $_POST
does ...
// $request->uploads ...
[
'images' => [
0 => [
'name' => 'image1.png',
'type' => 'image/png',
'tmp_name' => '/tmp/path/phpABCDEF',
'error' => 0,
'size' => 123456,
],
1 => [
'name' => 'image2.gif',
'type' => 'image/gif',
'tmp_name' => '/tmp/path/phpGHIJKL',
'error' => 0,
'size' => 234567,
],
2 => [
'name' => 'image3.jpg',
'type' => 'image/jpeg',
'tmp_name' => '/tmp/path/phpMNOPQR',
'error' => 0,
'size' => 345678,
],
],
];
... and then replaces each array-based descriptor with a SapiUpload instance.
These properties are public, immutable, read-only, and cannot be modified or overridden.
?array $accept
: An array of arrays computed from$_SERVER['HTTP_ACCEPT']
.?array $acceptCharset
: An array of arrays computed from$_SERVER['HTTP_ACCEPT_CHARSET']
.?array $acceptEncoding
: An array of arrays computed from$_SERVER['HTTP_ACCEPT_ENCODING']
.?array $acceptLanguage
: An array of arrays computed from$_SERVER['HTTP_ACCEPT_LANGUAGE']
.?array $forwarded
: An array of arrays computed from$_SERVER['HTTP_FORWARDED']
.?array $forwardedFor
: An array computed from treating$_SERVER['HTTP_X_FORWARDED_FOR']
as comma-separated values.?string $forwardedHost
: The$_SERVER['HTTP_X_FORWARDED_HOST']
value.?string $forwardedProto
: The$_SERVER['HTTP_X_FORWARDED_PROTO']
value.?array $headers
: An array of key/value pairs computed from$_SERVER
using allHTTP_*
header keys, plus RFC 3875 headers not prefixed withHTTP_
. Note that the header keys are retained in lower-case. This is to comply with HTTP/2 requirements; while HTTP/1.x has no such requirement, lower-case is also recognized as valid.?string $method
: The$_SERVER['REQUEST_METHOD']
value, or the$_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']
value when appropriate.?array $url
: The result from applyingparse_url()
to a a URL string constructed from various$_SERVER
elements.
Each element of the $accept*
arrays is an array with these keys:
'value' => The "main" value of the accept specifier
'quality' => The 'q=' parameter value
'params' => A key-value array of all other parameters
In addition, each $acceptLanguage
array element has two additional keys:
'type'
and 'subtype'
.
The $accept*
array elements are sorted by highest q
value to lowest.
Cf. the Forwarded HTTP Extension
Each element of the $forwarded
array is an array composed of one or more of
the following keys:
'by' => The interface where the request came in to the proxy server.
'for' => Discloses information about the client that initiated the request.
'host' => The original value of the Host header field.
'proto' => The value of the used protocol type.
SapiRequest attempts to build a string of the full request URL of the using the following:
- If
$_SERVER['HTTPS'] === 'on'
, the scheme is 'https'; otherwise, it is 'http'. - If
$_SERVER['HTTP_HOST']
is present, it is used as the host name; otherwise,$_SERVER['SERVER_NAME']
is used. - If a port number is present on the host name, it is used as the port;
otherwise,
$_SERVER['SERVER_PORT']
is used. $_SERVER['REQUEST_URI']
is used for the path and query string.
SapiRequest then passes that string through parse_url()
and retains the resulting array as $url
.
You can then retrieve the array elements using the PHP_URL_*
constants:
$scheme = $request->url[PHP_URL_SCHEME];
$host = $request->url[PHP_URL_HOST];
$port = $request->url[PHP_URL_PORT];
$path = $request->url[PHP_URL_PATH];
$queryString = $request->url[PHP_URL_QUERY];
If parse_url()
fails, the $url
property will remain null
.
These properties are public, immutable, read-only, and cannot be modified or overridden.
string $content
: The value offile_get_contents('php://input')
, or the custom content string provided at construction time.?string $contentCharset
: Thecharset
parameter value of$_SERVER['CONTENT_TYPE']
.?int $contentLength
: The value of$_SERVER['CONTENT_LENGTH']
.?string $contentMd5
: The value of$_SERVER['HTTP_CONTENT_MD5']
.?string $contentType
: The value of$_SERVER['CONTENT_TYPE']
, minus any parameters.
These properties are public, immutable, read-only, and cannot be modified or overridden.
?array $authDigest
: An array of digest values computed from$_SERVER['PHP_AUTH_DIGEST']
.?string $authPw
: The value of$_SERVER['PHP_AUTH_PW']
.?string $authType
: The value of$_SERVER['PHP_AUTH_TYPE']
.?string $authUser
: The value of$_SERVER['PHP_AUTH_USER']
.
The SapiRequest object has no public methods other than its constructor:
__construct(array $globals, [?string $content = null])
Although it is easy and convenient to extend this class, the authors recommend decoration and composition over extension in all but the most trivial of cases.
SapiRequest has a constructor. Child classes overriding __construct()
should be sure to call parent::__construct()
, or else the public read-only
properties will not be set (defaulting to null
in all cases).
The public read-only properties cannot be overridden; however, child classes may add new properties as desired.
SapiRequest has no methods; child classes may add methods as desired, and SapiRequest does not anticipate adding new methods of its own.
A read-only object describing an individual file upload.
SapiUpload has these public properties; they are immutable, read-only, and cannot be modified or overridden:
-
?string $name
: The original name of the file on the client machine. -
?string $type
: The mime type of the file, if the client provided this information. -
?int $size
: The size, in bytes, of the uploaded file. -
?string $tmpName
: The temporary filename of the file in which the uploaded file was stored on the server. -
?int $error
: The error code associated with this file upload.
SapiUpload has these public methods:
-
__construct(?string $name, ?string $type, ?int $size, ?string $tmpName, ?int $error)
: The constructor; once constructed, it cannot be constructed again. -
move(string $destination) : bool
: The equivalent ofmove_uploaded_file
.
A mutable object representing the PHP response to be sent from the server; use
it in place of the header()
, setcookie()
, setrawcookie()
, etc. functions.
It provides a retention space for the HTTP response version, code, headers,
cookies, and content, so they can be inspected before sending.
Note that SapiResponse can be extended to provide other userland functionality. However, its public methods are final; they cannot be modified or overridden.
Instantation is straightforward:
$response = new SapiResponse();
SapiResponse has no public properties.
SapiResponse implements SapiResponseInterface, which has these public
methods; all of them are declared final
and so may not be overridden.
-
setVersion(?string $version) : SapiResponseInterface
: Sets the protocol version for the response (typically '1.0', '1.1', or '2'). -
getVersion() : ?string
: Returns the protocol version for the response.
-
setCode(?int $code) : SapiResponseInterface
: Sets the status code for the response; a buffered equivalent ofhttp_response_code($code)
. -
getCode() : ?int
: Gets the status code for the response.
-
setHeader(string $label, string $value) : SapiResponseInterface
: Overwrites an HTTP header; a buffered equivalent ofheader("$label: $value", true)
. -
addHeader(string $label, string $value) : SapiResponseInterface
: Appends to an HTTP header, comma-separating it from the existing value; a buffered equivalent ofheader("$label: $value", false)
. -
unsetHeader(string $label) : SapiResponseInterface
: Removes a header from the buffer. -
unsetHeaders() : SapiResponseInterface
: Removes all headers from the buffer. -
getHeaders() : ?array
: Returns the array of headers to be sent. -
getHeader(string $label) : ?string
: Returns a header from the buffer. -
hasHeader(string $label) : bool
: Returns true if a header exists in buffer.
The header field labels are retained internally in lower-case, and are sent as lower-case. This is to comply with HTTP/2 requirements; while HTTP/1.x has no such requirement, lower-case is also recognized as valid.
-
setCookie(...) : SapiResponseInterface
: A buffered equivalent ofsetcookie()
with identical arguments. -
setRawCookie(...) : SapiResponseInterface
: A buffered equivalent ofsetrawcookie()
with identical arguments. -
unsetCookie(string $name) : SapiResponseInterface
: Removes a cookie from the buffer. -
unsetCookies() : SapiResponseInterface
: Removes all cookies from the buffer. -
getCookies() : ?array
: Returns the array of cookies to be sent. -
getCookie(string $name) : ?array
: Returns a cookie from the buffer. -
hasCookie(string $name) : bool
: Returns true if a cookie exists in buffer.
-
setHeaderCallbacks(array $callbacks) : SapiResponseInterface
: Sets an array of callbacks to be invoked just before headers are sent. It replaces any existing callbacks. This is similar toheader_register_callback()
, except that multiple callbacks may be registered with the Response. -
addHeaderCallback(callable $callback) : SapiResponseInterface
: Appends one callback to the current array of header callbacks. -
getHeaderCallbacks() : ?array
: Returns the array of header callbacks.
The header callback signature should be function (SapiResponseInterface $response)
;
any return value is ignored.
-
setContent(mixed $content) : SapiResponseInterface
: Sets the content of the response. This may be null, a string, resource, object, or anything else. -
getContent() : mixed
: Returns the content of the response. This may be null, a string, resource, object, or anything else.
Although it is easy and convenient to extend this class, the authors recommend decoration and composition over extension in all but the most trivial of cases.
SapiResponse is constructorless, which means you can add any constructor you like and not have to call a parent constructor.
The properties on SapiResponse are private, which means you may not access them, except through the existing SapiResponse methods.
The methods on SapiResponse are public and final, which means you cannot extend or override them in child classes. This keeps their behavior consistent.
However, the class itself is not final, which means you can add any other properties and methods you like.
The combination of a non-final class with private properties and public final methods keeps SapiResponse open for extension, but closed for modification.
An object to send a SapiResponse.
Note that SapiResponseSender methods can be extended and overridden.
Instantiation is straightforward:
$sender = new SapiResponseSender();
This class has no properties of any kind.
SapiResponseSender has these public methods:
-
send(SapiResponseInterface $response) : void
: Calls the following methods in order; that is: runHeaderCallbacks(), sendStatus(), sendHeaders(), sendCookies(), and sendContent(). -
runHeaderCallbacks(SapiResponseInterface $response) : void
: Invokes each callback returned by SapiResponse::getHeaderCallbacks(). -
sendStatus(SapiResponseInterface $response) : void
: Sends the HTTP status line using header(). The line is composed of SapiResponse::getVersion() and SapiResponse::getCode(). If the version isnull
it defaults to1.1
; if the code is null is defaults to200
. -
sendHeaders(SapiResponseInterface $response) : void
: Sends each header returned by SapiResponse::getHeaders() using header(). -
sendCookies(SapiResponseInterface $response) : void
: Sends each cookie returned by SapiResponse::getCookies() using setcookie() or setrawcookie(). -
sendContent(SapiResponseInterface $response) : void
: Sends the content returned by SapiResponse::getContent().-
If the content is a resource, it is sent using
rewind()
and thenfpassthru()
; there is no further handling thereafter. -
If the content is a callable object or closure, it is invoked, and its return value (if any) is passed along to be handled by the next step.
-
If the content or returned value is iterable, it is
foreach()
-ed through, and each value is echoed as a string; note that object values will be cast to string at this point, invoking their__toString()
method if present. -
Otherwise, the content or returned value is echoed as a string; note that an object will be cast to string at this point, invoking its
__toString()
method if present.
-
Although it is easy and convenient to extend this class, the authors recommend decoration and composition over extension in all but the most trivial of cases.
SapiResponseSender is constructorless, which means you can add any constructor you like and not have to call a parent constructor.
The SapiResponseSender methods are public but not final, which means you can extend and override them as you see fit. Doing so for any method other than sendContent() might not make sense. There is pretty much only one way to send headers, cookies, etc., but different kinds of content might well deserve sending logic that differs from the default sendContent() logic.