Skip to main content

PSR-7 and PSR-17 Implementation

PSR Standards Compliance

This package provides complete implementations of:

WebRequest implements the PSR-7 HTTP Message Interface specification, which provides interfaces for HTTP messages (requests and responses) and their components.

Components​

Message Classes​

ClassDescriptionImplements
MessageBase implementation for requests and responsesPSR-7 MessageInterface
RequestHTTP request implementationPSR-7 RequestInterface
ResponseHTTP response implementationPSR-7 ResponseInterface
ServerRequestServer-side HTTP requestPSR-7 ServerRequestInterface

Headers keep the name in the case it was first given (getHeaders() returns content-type if it was set as content-type), and are still read in any case. A header name must be an RFC 7230 token and a value cannot have line breaks, otherwise MessageException, an InvalidArgumentException, is thrown. The method is case-sensitive and can be any token (PROPFIND, PURGE...), not only the ones in HttpMethod. A URI without a host keeps the Host header the request had.

withHeaders() (not part of PSR-7) sets several headers at once, each as withHeader() would, with one copy of the message instead of one for each header:

<?php
$request = $request->withHeaders([
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . $token,
'Accept-Encoding' => ['gzip', 'deflate'],
]);

// Copy the headers of another request (getHeaders() returns this format)
$serverRequest = $serverRequest->withHeaders($request->getHeaders());

ServerRequest holds only what it is given, as PSR-7 requires: new ServerRequest() and ServerRequestFactory do not read PHP's globals.

Parsed body​

getParsedBody() returns what withParsedBody() set. When nothing was set, it parses the request's own body by its Content-Type:

Content-TypeParsed withResult
application/json, *+jsonjson_decode()associative array
application/x-www-form-urlencodedparse_str()the fields
multipart/form-dataMultiPartItem::parseBody()the fields, as PHP fills $_POST
application/xml, text/xml, *+xmlSerialize::fromXml() (byjg/serializer)array, without the root element
application/yaml, application/x-yaml, text/yaml, text/x-yamlSerialize::fromYaml()array
text/csv, application/csvSerialize::fromCsv()list of rows, keyed by the header row

PSR-7 allows only null, an array or an object as the parsed body, so any other Content-Type (text/plain, application/octet-stream...), an invalid body or a JSON or YAML scalar ("text", abc) gives null; read it with getBody(). parseBody() does the same parsing but throws MessageException, an InvalidArgumentException, saying why there is no parsed body. For a YAML scalar, the serializer's TypeError is its previous exception:

<?php
$request->getParsedBody(); // null for text/plain
$request->parseBody(); // MessageException: The Content-Type 'text/plain' has no parsed body: ...

withParsedBody() accepts null, an array or an object (MessageException otherwise). For JSON, urlencoded, XML, YAML and CSV it also writes the body from the data, so both describe the same request; for any other Content-Type (multipart...) the body stays as it is.

fromGlobals()​

ServerRequest::fromGlobals() builds the current request from the globals: the method, URI, headers and server params from $_SERVER, the cookies from $_COOKIE, the uploaded files from $_FILES, and, for a POST of a form (application/x-www-form-urlencoded or multipart/form-data), $_POST as the parsed body. The body is a copy of php://input, or the one you give, as a stream or a string (a worker in the CLI, where php://input is empty, or a test):

<?php
$request = \ByJG\WebRequest\Psr7\ServerRequest::fromGlobals();
$request = \ByJG\WebRequest\Psr7\ServerRequest::fromGlobals('{"name":"John"}');

UploadedFile::parseFilesGlobal() turns $_FILES into UploadedFile objects whose stream reads the temporary file when used. moveTo() moves a file on disk instead of copying it, and can be called once.

Stream Implementations​

ClassDescriptionUse Case
StreamBaseBase stream implementationBase class for all streams
MemoryStreamIn-memory streamString data, JSON payloads
FileStreamFile-based streamReading from files
TempFileStreamTemporary file streamLarge data that shouldn't stay in memory
ResourceStreamStream over a resource opened elsewherefopen() results, StreamFactory::createStreamFromResource()
NullStreamDiscards all dataTesting, ignoring response bodies
UploadedFileUploaded file handlerProcessing file uploads

Usage Examples​

Creating a Request​

<?php
$uri = \ByJG\Util\Uri::getInstance('http://www.example.com/page');
$request = \ByJG\WebRequest\Psr7\Request::getInstance($uri);

Creating a Response​

<?php
$response = new \ByJG\WebRequest\Psr7\Response(200);
$response = $response->withBody(new \ByJG\WebRequest\Psr7\MemoryStream('{"key":"value"}'));

Working with Streams​

<?php
// Create a memory stream
$stream = new \ByJG\WebRequest\Psr7\MemoryStream('Hello world');
echo $stream->getContents(); // "Hello world"

// Create a file stream
$stream = new \ByJG\WebRequest\Psr7\FileStream('/path/to/file.txt');

Every stream extending StreamBase also implements ByJG\WebRequest\Helper\ExtendedStreamInterface:

<?php
$stream = new \ByJG\WebRequest\Psr7\MemoryStream('Hello ');

// Append the contents of another stream at the end
$stream->appendStream(new \ByJG\WebRequest\Psr7\MemoryStream('world'));

// Attach a PHP stream filter, applied on read ("r", default) or on write ("w")
$stream->addFilter('string.toupper', 'r');

PSR-17 Factory Implementation​

WebRequest also implements PSR-17 HTTP Factories for creating PSR-7 objects:

  • RequestFactory: Creates Request objects
  • ResponseFactory: Creates Response objects
  • ServerRequestFactory: Creates ServerRequest objects
  • StreamFactory: Creates Stream objects. createStreamFromResource() uses the resource itself, at its current position (it is not copied, and closing the stream closes it), and createStreamFromFile() opens the file with the mode given (r by default, so the stream is read only)
  • UploadedFileFactory: Creates UploadedFile objects
  • UriFactory: Creates Uri objects. It is ByJG\Util\UriFactory, from byjg/uri, which WebRequest already requires
tip

PSR-17 factories provide a standardized way to create PSR-7 objects, making your code more interoperable with other PSR-compliant libraries.

Usage example:

<?php
$factory = new \ByJG\WebRequest\Factory\RequestFactory();
$request = $factory->createRequest('GET', 'http://example.com');