PSR-7 — HTTP Messages (Request/Response interfaces)
Concept
PSR-7 defines interfaces for HTTP messages: requests, responses, URIs, streams, and uploaded files. It's the foundation for framework-agnostic HTTP handling in PHP. Guzzle HTTP client, Slim Framework, Diactoros, and middleware libraries all implement PSR-7.
Immutability: PSR-7 messages are immutable value objects. Every with*() method returns a new instance: $response->withStatus(200) does NOT modify $response — it returns a new ResponseInterface. This prevents action-at-a-distance bugs where modifying a request in one place unexpectedly affects another.
Key interfaces:
MessageInterface: Base for request/response. Headers, protocol version, body (asStreamInterface).RequestInterfaceextendsMessageInterface: Client-side request. Method, URI, request target.ServerRequestInterfaceextendsRequestInterface: Server-side.$_SERVERparams, cookies, parsed body, uploaded files, attributes.ResponseInterfaceextendsMessageInterface: Status code, reason phrase, body.UriInterface: URI with individual components — scheme, host, port, path, query, fragment.StreamInterface: Abstraction over a data stream. Can be a file, string, socket. Hasread(),write(),seek(),getContents().UploadedFileInterface: Uploaded file abstraction.
PSR-17: HTTP factories — creates PSR-7 objects. RequestFactoryInterface, ResponseFactoryInterface, StreamFactoryInterface, etc.
PSR-18: HTTP client interface. ClientInterface with a single sendRequest(RequestInterface): ResponseInterface method. Guzzle implements it. Lets you swap HTTP clients without changing application code.
Code Example
<?php
declare(strict_types=1);
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
// PSR-7 request is immutable — with* methods return new instances
function addAuthHeader(RequestInterface $request, string $token): RequestInterface
{
// Returns NEW request — original unchanged
return $request->withHeader('Authorization', 'Bearer ' . $token);
}
// Reading from a PSR-7 server request
function handleRequest(ServerRequestInterface $request): ResponseInterface
{
$method = $request->getMethod(); // "GET", "POST", etc.
$path = $request->getUri()->getPath(); // "/api/users"
$query = $request->getQueryParams(); // ['page' => '2']
$body = $request->getParsedBody(); // decoded POST body
$cookie = $request->getCookieParams();
$header = $request->getHeaderLine('Content-Type');
// Attributes set by middleware
$userId = $request->getAttribute('user_id');
}
// PSR-7 response
use Laminas\Diactoros\Response\JsonResponse;
use Laminas\Diactoros\Response;
use Nyholm\Psr7\Factory\Psr17Factory;
$factory = new Psr17Factory();
$response = $factory->createResponse(200)
->withHeader('Content-Type', 'application/json');
$stream = $factory->createStream(json_encode(['status' => 'ok']));
$response = $response->withBody($stream);
// PSR-18 HTTP client (Guzzle)
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
$client = new Client(['base_uri' => 'https://api.example.com']);
$request = new Request('GET', '/users/1', ['Accept' => 'application/json']);
$response = $client->sendRequest($request); // PSR-18
$data = json_decode((string) $response->getBody(), true);