In PHP, what is PSR-7, and which interfaces does it define to represent HTTP requests, responses and their parts?
answer
- a PHP-FIG standard, not a library
- MessageInterface is the common parent
- RequestInterface vs ServerRequestInterface
- StreamInterface for bodies, UriInterface for URIs
- UploadedFileInterface normalises $_FILES
basics
~10 sPSR-7 is the PHP-FIG standard for HTTP messages: interfaces in Psr\Http\Message for requests, server requests, responses, bodies (StreamInterface), URIs and uploaded files. Code typed against them works with any conforming implementation.
solid answer
~40 sPSR-7 is a PHP-FIG recommendation that defines interfaces, not an implementation, published as the `psr/http-message` package in the `Psr\Http\Message` namespace. `MessageInterface` holds what requests and responses share: protocol version, headers and a body. `RequestInterface` adds the method, the request target and a `UriInterface`; `ServerRequestInterface` extends it with what PHP's SAPI provides on the server side: server params, cookies, query params, the parsed body, uploaded files and attributes. `ResponseInterface` adds the status code and reason phrase. The body is a `StreamInterface`, and each uploaded file is an `UploadedFileInterface`. Messages and URIs are immutable value objects changed through `with*()` methods. The payoff is interoperability: a library typed against these interfaces works with whichever implementation the application installs.
code
php · 14 lines<?php
declare(strict_types=1);
use Psr\Http\Message\ServerRequestInterface;
function describe(ServerRequestInterface $request): string
{
$method = $request->getMethod();
$path = $request->getUri()->getPath();
$accept = $request->getHeaderLine('accept'); // case-insensitive lookup
$page = $request->getQueryParams()['page'] ?? '1';
return sprintf('%s %s (accept: %s, page %s)', $method, $path, $accept, $page);
}go deeper
Name PSR-7 as the shared HTTP message interfaces and list the main ones: request, server request, response, stream, URI and uploaded file. Knowing it is interfaces only is the first check.
Explain why ServerRequestInterface extends RequestInterface, how it wraps the superglobals, and how getHeader() and getHeaderLine() differ, including their empty results for a missing header.
Show that you type library code against the PSR-7 interfaces rather than an implementation, and know the untyped 1.0, typed 1.1 and 2.0 package lines when you set Composer constraints.
Weigh standardising a codebase on PSR-7 messages against a framework's own request objects: portability and testability versus the convenience helpers and ecosystem a framework layer adds.
## What PSR-7 is **PSR-7** is a **PHP Standards Recommendation** accepted by PHP-FIG, the group of PHP project maintainers that publishes shared interfaces. It defines how HTTP messages are represented as PHP objects. It ships as the Composer package `psr/http-message`, which contains **interfaces only**: no class you can instantiate, no parser and no emitter. You install a separate implementation package that provides concrete classes, and your own code types its parameters and return values against the interfaces. Before PSR-7, every framework and HTTP client had its own request and response classes, so a piece of middleware, an OAuth library or an API SDK had to be written once per framework. With a shared set of interfaces, one package can accept and return messages from any conforming implementation. ## The interfaces All of them live in the `Psr\Http\Message` namespace. | Interface | Models | Key methods | |---|---|---| | `MessageInterface` | what requests and responses share | `getProtocolVersion()`, `getHeaders()`, `getHeader()`, `getHeaderLine()`, `getBody()`, `withHeader()`, `withBody()` | | `RequestInterface` | an outgoing, client-side request | `getMethod()`, `getRequestTarget()`, `getUri()`, `withMethod()`, `withUri()` | | `ServerRequestInterface` | an incoming request as the server sees it | `getServerParams()`, `getCookieParams()`, `getQueryParams()`, `getParsedBody()`, `getUploadedFiles()`, `getAttribute()` | | `ResponseInterface` | a response | `getStatusCode()`, `getReasonPhrase()`, `withStatus()` | | `StreamInterface` | a message body | `read()`, `write()`, `getContents()`, `rewind()`, `isSeekable()` | | `UriInterface` | a URI | `getScheme()`, `getHost()`, `getPath()`, `getQuery()`, `withPath()` | | `UploadedFileInterface` | one uploaded file | `getStream()`, `moveTo()`, `getError()`, `getClientFilename()` | `RequestInterface` and `ResponseInterface` both extend `MessageInterface`, and `ServerRequestInterface` extends `RequestInterface`. ## Why a server request is separate A request that a client builds and sends has a method, a URI, headers and a body. A request that arrives at a PHP application carries more: PHP has already decoded parts of it into the superglobals `$_SERVER`, `$_GET`, `$_POST`, `$_COOKIE` and `$_FILES`. `ServerRequestInterface` wraps that data behind methods, which has two effects: - code that reads the request no longer touches global state, so it can be unit-tested by building a request object; - `getUploadedFiles()` returns a tree of `UploadedFileInterface` objects, which fixes the awkward nested layout `$_FILES` produces for array inputs. It also adds **attributes**, a bag for values derived from the request (a matched route parameter, an authenticated user) that one piece of code can attach for the next one to read. ## Headers Header names are **case-insensitive** on lookup, but an implementation must preserve the original case in `getHeaders()`. Each header can hold several values: - `getHeader('Accept')` returns an array of values, or an empty array if the header is missing; - `getHeaderLine('Accept')` returns the values joined with a comma, or an empty string if the header is missing; - `withAddedHeader()` appends a value, `withHeader()` replaces all values, `withoutHeader()` removes the header. Some headers, notably `Set-Cookie`, cannot be joined with commas, so the specification says to read them with `getHeader()`. ## Immutability in one paragraph Messages, URIs and uploaded-file objects are **value objects**: every method that would change state is named `with...()` or `without...()` and returns a message carrying the change, leaving the original untouched. The one exception is `StreamInterface`, which wraps a PHP stream and therefore cannot be made immutable. ## Related standards built on PSR-7 1. **PSR-15** defines `RequestHandlerInterface` and `MiddlewareInterface` for server-side request processing. 2. **PSR-17** defines factories such as `ResponseFactoryInterface` and `StreamFactoryInterface`, so a library can create messages without naming an implementation class. 3. **PSR-18** defines `ClientInterface::sendRequest()` for sending a request and receiving a response. ## Versions of the package The original `psr/http-message` 1.0 declared no parameter or return types. Release 1.1 added parameter types and 2.0 added return types; the methods and their meaning did not change. An implementation declares which of these it supports, and Composer picks compatible versions. ## Misconceptions interviewers listen for - **"PSR-7 is a framework component."** It is a set of interfaces; frameworks and libraries implement or consume them. - **"The request object has setters."** There are none; every change goes through `with*()` and yields a new instance. - **"`RequestInterface` is what a controller receives."** Server-side code receives a `ServerRequestInterface`, which adds the SAPI-derived data; `RequestInterface` alone is what an HTTP client sends. - **"Headers are a plain associative array."** Lookups ignore case and each header maps to a list of values, which is why two getters exist. A good junior answer names the package, the main interfaces and the idea of coding against them; everything else builds on that.
- Does installing psr/http-message give you a Request class you can instantiate?No. `psr/http-message` contains only interfaces. To create objects you install an implementation package that provides concrete classes, and ideally you create them through PSR-17 factory interfaces, so your code still names only standard interfaces.
- In PSR-7, what do getHeader() and getHeaderLine() return for a header the message does not have?`getHeader()` returns an empty array and `getHeaderLine()` returns an empty string; neither returns null or throws. Use `hasHeader()` when you need to tell a missing header apart from one that is present with an empty value.
saying these in an interview costs you the question
- Believes PSR-7 is a library with concrete Request and Response classes.
- Thinks RequestInterface already exposes query params, cookies and uploaded files.
- Assumes header lookup in PSR-7 is case-sensitive.
- Says getHeaderLine() returns null for a missing header.
- Calls PSR-7 messages mutable objects with setters.