0

PSR-7 overview — MessageInterface, RequestInterface, ResponseInterface

Intermediate5 min read·fw-04-001
psr

Concept

PSR-7 (PHP-FIG standard) defines HTTP message interfaces. It's a standard that allows HTTP client libraries, framework components, and middlewares to interoperate without depending on each other's concrete classes.

Core interfaces:

  • MessageInterface: Base for both request and response. Methods: getProtocolVersion(), getHeaders(), getHeader(string $name), getBody(), withHeader(string $name, string $value), withBody(StreamInterface $body).
  • RequestInterface extends MessageInterface: getMethod(), getUri(), withMethod(string $method), withUri(UriInterface $uri).
  • ServerRequestInterface extends RequestInterface: Adds server-side data: getServerParams(), getCookieParams(), getQueryParams(), getParsedBody(), getUploadedFiles(), getAttributes().
  • ResponseInterface extends MessageInterface: getStatusCode(), getReasonPhrase(), withStatus(int $code, string $reasonPhrase = '').
  • StreamInterface: Represents a body as a stream. read(), write(), seek(), tell(), eof(), getContents().
  • UriInterface: Represents a URI. getScheme(), getHost(), getPath(), getQuery(), withPath(), etc.
  • UploadedFileInterface: Represents a file upload. getStream(), moveTo(), getSize(), getError(), getClientFilename().

Immutability: PSR-7 messages are immutable. with*() methods return a NEW instance with the modification. This prevents shared mutable state bugs in middleware.

Why PSR-7: Middleware that works with any PSR-7 implementation can be shared across Slim, Laminas, and any compliant framework.

Code Example

php
<?php
// PSR-7 in practice — using nyholm/psr7 (lightweight PSR-7 implementation)
// composer require nyholm/psr7 nyholm/psr7-server

use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7Server\ServerRequestCreator;

// Creating a ServerRequest from PHP superglobals
$factory = new Psr17Factory();
$creator = new ServerRequestCreator($factory, $factory, $factory, $factory);
$request = $creator->fromGlobals();

// Reading the request
$method  = $request->getMethod();          // 'GET'
$path    = $request->getUri()->getPath();  // '/users/42'
$query   = $request->getQueryParams();     // ['page' => '2']
$body    = $request->getParsedBody();      // POST data (associative array)
$headers = $request->getHeaders();         // ['Content-Type' => ['application/json']]
$server  = $request->getServerParams();    // $_SERVER equivalent
$attrs   = $request->getAttributes();      // framework-added data (route params, etc.)

// Immutable — with* methods return new instance
$modified = $request->withMethod('POST')
                     ->withHeader('X-Custom', 'value');
// $request unchanged, $modified has the modifications

// Building a response
$response = $factory->createResponse(200)
    ->withHeader('Content-Type', 'application/json');

$body = $factory->createStream(json_encode(['user' => ['id' => 42, 'name' => 'Alice']]));
$response = $response->withBody($body);

// Emitting the response (send to browser)
// Status line
http_response_code($response->getStatusCode());
// Headers
foreach ($response->getHeaders() as $name => $values) {
    foreach ($values as $value) {
        header("{$name}: {$value}", false);
    }
}
// Body
echo $response->getBody()->getContents();