This library provides an abstraction around PHPs various superglobals as well as controlling the HTTP response. This practice helps reduce coupling to the superglobals by consumers, and encourages and promotes the ability to test request consumers.
This PSR-7 implementation is an abstraction layer over the normal input/output methods and variables like echo,
header(), $_POST, etc. This maskes is different from other implementations. It directly interacts with the global
environment when you're not testing. This means the behaviour of your application will not change by using this
abstraction.
The library only implements those PSR-7 interfaces focussed on handling a received HTTP request. If you want to send HTTP request to other webservices, I recommend using Guzzle.
composer install jasny/http-message
The library implements the following PSR-7 interfaces
ServerRequestimplementsPsr\Http\Message\ServerRequestInterfaceResponseimplementsPsr\Http\Message\ResponseInterfaceStreamimplementsPsr\Http\Message\StreamInterfaceUriimplementsPsr\Http\Message\UriInterface
it defines one interface
The ServerRequest class represents an HTTP request as received by the webserver and processed by PHP.
For the full documentation about the ServerRequest, please see
PSR-7 RequestInterface and
PSR-7 ServerRequestInterface.
To create a ServerRequest object with the $_SERVER, $_COOKIE, $_GET, $_POST and $_FILES superglobals and
with php://input as input stream, use the withGlobalEnvironment() method.
$request = (new Jasny\HttpMessage\ServerRequest())->withGlobalEnvironment();The withGlobalEnvironment() links the superglobals by reference to the object. You SHOULD NOT modify these variables,
but if you do the changes will be reflected in the ServerRequest object. Vise versa, using withQueryParams() will
change $_GET, withServerParams changes $_SERVER, etc.
If you do not want this behaviour and want the request to copy the values of the superglobals instead, set the first
argument to false.
// $_GET is affected
$requestByRef = (new Jasny\HttpMessage\ServerRequest())->withGlobalEnvironment();
$requestByRef = $request->withQueryParams(['foo' => 1]);
var_dump($_GET); // array(1) { ["foo"]=> int(1) }
// $_GET is not affected
$requestByVal = (new Jasny\HttpMessage\ServerRequest())->withGlobalEnvironment(false);
$requestByVal = $request->withQueryParams(['foo' => 1]);
var_dump($_GET); // array(0) { }The Response class allows you to create the outgoing HTTP response.
For the full documentation about the Response class, please see
PSR-7 ResponseInterface.
By default a Response object will stream to php://memory and simply hold a list of all set headers.
$request = new Jasny\HttpMessage\ServerRequest();
To create a Response object which uses the header() method and
with php://output as output stream, use the withGlobalEnvironment() method.
$request = (new Jasny\HttpMessage\ServerRequest())->withGlobalEnvironment();You SHOULD NOT write to php://output directly (by using echo). Still, the changes will be reflected in the
Response object when withGlobalEnvironment() is used.
You SHOULD NOT use the header() and header_remove() functions directly. Still, the changes will be reflected in the
Response object when withGlobalEnvironment() is used.
The Uri class is meant to represent URIs according to RFC 3986. It allows you
to get and change any specific part of an uri.
For the full documentation about the Uri class, please see
PSR-7 UriInterface.
When creating an Uri you can pass the URL as string or pass the URL in parts as associative array. For the URL parts
see the parse_url function.
The Jasny\HttpMessage\Uri object only supports the http and https schemes.
$uri = new Jasny\HttpMessage\Uri("http://www.example.com/foo");The Stream class is a wrapper around php streams implementing the
PSR-7 StreamInterface.
$input = new Jasny\HttpMessage\Stream('php://input', 'r');
$output = new Jasny\HttpMessage\Stream('php://output', 'w');For testing purposes use the php://memory stream.
$input = new Jasny\HttpMessage\Stream('php://memory');
$input->write(json_encode(['foo' => 'bar', 'color' => 'red']));
$output = new Jasny\HttpMessage\Stream('php://memory');After running the test case, cast $output to a string to assert the output.
You can set arbitrary attributes for a ServerRequest using the withAttribute() method. To get an attribute use the
getAttribute() method.
An attribute can be set to any static value, or it can be derived from other values of a ServerRequest object, like a
header or query parameter. The easiest way to create a derived attribute is to use a
Closure.
use Jasny\HttpMessage\ServerRequest;
$request = (new ServerRequest())->withAttribute('accept_json', function(ServerRequest $request) {
$accept = $request->getHeaderLine('Accept');
return strpos($accept, 'application/json') !== false || strpos($accept, '*/*') !== false;
});You can create more sophisticated derived attributes by creating a class that implements the DerivedAttribute
interface. When implementing that interface, implement __invoke(ServerRequest $request).
use Jasny\HttpMessage\ServerRequest;
use Jasny\HttpMessage\DerivedAttribute;
class DetectBot implements DerivedAttribute
{
public static $identifiers = [
'google' => 'googlebot',
'yahoo' => 'yahoobot',
'magpie' => 'magpie-crawler'
];
protected $detect = [];
public function __construct(array $detect)
{
$this->detect = $detect;
}
public function __invoke(ServerRequest $request)
{
$useragent = $request->getHeaderLine('User-Agent');
$detected = false;
foreach ($this->detect as $bot) {
$identifier = static::$identifiers[$bot];
$detected = $detected || stripos($useragent, $bot) !== false;
}
return $detected;
}
}
$request = (new ServerRequest())
->withAttribute('is_friendly_bot', new DetectBot(['google', 'yahoo']))
->withAttribute('is_annoying_bot', new DetectBot(['magpie']));Remember that a ServerRequest method is immutability, so withAttribute() will create a new object.
This library comes with a number of derived attributes, which may be used.
Get the client IP. By default only $_SERVER['REMOTE_ADDR'] is returned.
use Jasny\HttpMessage\ServerRequest;
$request = (new ServerRequest())->withGlobalEnvironment();
$request->getAttribute('client_ip'); // always returns $_SERVER['REMOTE_ADDR']You can specificy an IP or CIDR address for trusted proxies. When used, addresses send as HTTP header through
X-Forwarded-For, Client-Ip or Forwarded are taken into consideration.
use Jasny\HttpMessage\ServerRequest;
use Jasny\HttpMessage\DerivedAttribute\ClientIp;
$request = (new ServerRequest())
->withGlobalEnvironment()
->withAttribute('client_ip', new ClientIp(['trusted_proxy => '10.0.0.0/24']);
$ip = $request->getAttribute('client_ip'); // for a request from the internal network, use the `X-Forwarded-For` headerNote: If more than one of these headers are set, a RuntimeException is thrown. This prevents a user injecting a
Client-Ip address to fake his ip, where your proxy is setting the X-Forwarded-For header. To make sure this
exception doesn't occur, remove all unexpected forward headers.
use Jasny\HttpMessage\ServerRequest;
use Jasny\HttpMessage\DerivedAttribute\ClientIp;
$request = (new ServerRequest())
->withGlobalEnvironment()
->withoutHeader('Client-Ip')
->withoutHeader('Forwarded')
->withAttribute('client_ip', new ClientIp(['trusted_proxy => '10.0.0.0/24']);Test is the request with made using AJAX.
All modern browsers set the X-Requested-With header to XMLHttpRequest when making an AJAX request. This derived
attribute simply checks that header.
use Jasny\HttpMessage\ServerRequest;
$request = (new ServerRequest())->withGlobalEnvironment();
$isXhr = $request->getAttribute('is_xhr'); // true or falseReturn the path of the Referer header, but only if the referer's scheme, host and port matches request's scheme, host
and port.
use Jasny\HttpMessage\ServerRequest;
$request = (new ServerRequest())->withGlobalEnvironment();
$back = $request->getAttribute('local_referer') ?: '/'; // Referer Uri path, defaults to `/` for no or external refererIt is possible to disable the check on scheme and/or port if needed.
use Jasny\HttpMessage\ServerRequest;
use Jasny\HttpMessage\DerivedAttribute\LocalReferer;
$request = (new ServerRequest())
->withGlobalEnvironment()
->withAttribute('local_referer', new LocalReferer(['checkScheme' => false, 'checkPort' => false]));When testing code that is fully PSR-7 compatible, create a ServerRequest with specific headers, parameters and data
and a default Response.
$request = (new ServerRequest())
->withMethod('GET')
->withUri('/foo')
->withQueryParams(['page' => 1]);PSR-7 compatible code MUST NOT access superglobals directly and also MUST NOT output headers and data directly.
This library allows you to test code that isn't fully PSR-7 compatible. It might access the superglobals directly and/or
output using echo and headers().
// Start output buffering, so the output isn't send directly
ob_start();
// Create response with (actual) global enviroment. Modifying it, modifies the superglobals.
$request = (new ServerRequest())->withGlobalEnvironment()
->withServerParams(['REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/foo'])
->withQueryParams(['page' => 1]);
// Create response with (actual) global enviroment.
$response = (new Response())->withGlobalEnvironment();
// Some PSR-7 compatible router handles the request. The code uses `header` and `echo` to output.
$router->route($request, $response);
// Disconnect the global environment, copy the data and headers
$response = $response->withoutGlobalEnvironment();
// Remove all headers and output
header_remove();
ob_end_clean();
// Assert response
...Using this technique allows you to start using PSR-7 without having to rewrite your whole code base. Instead you can refactor your code bit by bit.

