Reference

Custom Responses

When developing server-side applications, the message type you're most likely to create manually is the response. In such cases, the standard signature can be an obstacle to usability. Let's review:

namespace Laminas\Diactoros;

use Psr\Http\Message\ResponseInterface;

class Response implements ResponseInterface
{
    public function __construct($body = 'php://temp', $status = 200, array $headers = []);
}

Some standard use cases, however, make this un-wieldy:

  • Returning a response containing HTML; in this case, you likely want to provide the HTML to the constructor, not a stream with the HTML injected.
  • Returning a response containing JSON; in this case, you likely want to provide the data to serialize to JSON, not a stream containing serialized JSON.
  • Returning a response with no content; in this case, you don't want to bother with the body at all.
  • Returning a redirect response; in this case, you likely just want to specify the target for the Location header, and optionally the status code.

Starting with version 1.1, Diactoros offers several custom response types for simplifying these common tasks.

Text Responses

Laminas\Diactoros\Response\TextResponse creates a plain text response. It sets the Content-Type header to text/plain by default:

$response = new Laminas\Diactoros\Response\TextResponse('Hello world!');

The constructor accepts two additional arguments: a status code and an array of headers.

$response = new Laminas\Diactoros\Response\TextResponse(
    $text,
    200,
    ['Content-Type' => ['text/csv']]
);

HTML Responses

Laminas\Diactoros\Response\HtmlResponse allows specifying HTML as a payload, and sets the Content-Type header to text/html by default:

$response = new Laminas\Diactoros\Response\HtmlResponse($htmlContent);

The constructor allows passing two additional arguments: a status code, and an array of headers. These allow you to further seed the initial state of the response, as well as to override the Content-Type header if desired:

$response = new Laminas\Diactoros\Response\HtmlResponse(
    $htmlContent,
    200,
    ['Content-Type' => ['application/xhtml+xml']]
);

Headers must be in the same format as you would provide to the Response constructor.

XML Responses

Laminas\Diactoros\Response\XmlResponse allows specifying XML as a payload, and sets the Content-Type header to application/xml by default:

$response = new Laminas\Diactoros\Response\XmlResponse($xml);

The constructor allows passing two additional arguments: a status code, and an array of headers. These allow you to further seed the initial state of the response, as well as to override the Content-Type header if desired:

$response = new Laminas\Diactoros\Response\XmlResponse(
    $xml,
    200,
    ['Content-Type' => ['application/hal+xml']]
);

Headers must be in the same format as you would provide to the Response constructor.

JSON Responses

Laminas\Diactoros\Response\JsonResponse accepts a data structure to convert to JSON, and sets the Content-Type header to application/json:

$response = new Laminas\Diactoros\Response\JsonResponse($data);

If providing an object, we recommend implementing JsonSerializable to ensure your object is correctly serialized.

Just like the HtmlResponse, the JsonResponse allows passing two additional arguments — a status code, and an array of headers — to allow you to further seed the initial state of the response:

$response = new Laminas\Diactoros\Response\JsonResponse(
    $data,
    200,
    ['Content-Type' => ['application/hal+json']]
);

Finally, JsonResponse allows a fourth optional argument, the flags to provide to json_encode(). By default, these are set to JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT (integer 15), providing RFC 4627 compliant JSON capable of embedding in HTML. If you want to specify a different set of flags, use the fourth constructor argument:

$response = new Laminas\Diactoros\Response\JsonResponse(
    $data,
    200,
    [],
    JSON_PRETTY_PRINT | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
);

Empty Responses

Many API actions allow returning empty responses:

  • 201 Created responses are often empty, and only include a Link or Location header pointing to the newly created resource.
  • 202 Accepted responses are typically empty, indicating that the new entity has been received, but not yet processed.
  • 204 No Content responses are, by definition, empty, and often used as a success response when deleting an entity.

Laminas\Diactoros\Response\EmptyResponse is a Laminas\Diactoros\Response extension that, by default, returns an empty response with a 204 status. Its constructor allows passing the status and headers only:

namespace Laminas\Diactoros\Response;

use Laminas\Diactoros\Response;

class EmptyResponse extends Response
{
    public function __construct($status = 204, array $headers = []);
}

An empty, read-only body is injected at instantiation, ensuring no write operations are possible on the response. Usage is typically one of the following forms:

use Laminas\Diactoros\Response\EmptyResponse;

// Basic 204 response:
$response = new EmptyResponse();

// 201 response with location header:
$response = new EmptyResponse(201, [
    'Location' => [ $url ],
]);

// Alternately, set the header after instantiation:
$response = (new EmptyResponse(201))->withHeader('Location', $url);

Redirects

Laminas\Diactoros\Response\RedirectResponse is a Laminas\Diactoros\Response extension for producing redirect responses. The only required argument is a URI, which may be provided as either a string or Psr\Http\Message\UriInterface instance. By default, the status 302 is used, and no other headers are produced; you may alter these via the additional optional arguments:

namespace Laminas\Diactoros\Response;

use Laminas\Diactoros\Response;

class RedirectResponse extends Response
{
    public function __construct($uri, $status = 302, array $headers = []);
}

Typical usage is:

use Laminas\Diactoros\Response\RedirectResponse;

// 302 redirect:
$response = new RedirectResponse('/user/login');

// 301 redirect:
$response = new RedirectResponse('/user/login', 301);

// using a URI instance (e.g., by altering the request URI instance)
$uri = $request->getUri();
$response = new RedirectResponse($uri->withPath('/login'));

Creating custom Responses

PHP allows constructor overloading. What this means is that constructors of extending classes can define completely different argument sets without conflicting with the parent implementation. Considering that most custom response types do not need to change internal functionality, but instead focus on user experience (i.e., simplifying instantiation), this fact can be leveraged to create your custom types.

The general pattern will be something like this:

use Laminas\Diactoros\Response;

class MyCustomResponse extends Response
{
    public function __construct($data, $status = 200, array $headers = [])
    {
        // - Do something with $data, and create a Stream for the body (if necessary).
        // - Maybe set some default headers.

        parent::__construct($body, $status, $headers);
    }
}

Note the call to parent::__construct(). This is particularly relevant, as the implementation at the time of writing has all class properties marked as private, making them inaccessible to extensions; this is done to protect encapsulation and ensure consistency of operations between instances.

If you don't want to go the extension route (perhaps you don't want another ResponseInterface implementation within your object graph) you can instead create a factory. As an example:

$plainTextResponse = function ($text, $status = 200, array $headers = []) {
    $response = new Laminas\Diactoros\Response('php://temp', $status, $headers);
    $response->getBody()->write($text);
    if (! $response->hasHeader('Content-Type')) {
        $response = $response->withHeader('Content-Type', 'text/plain');
    }
    return $response;
};

$response = $plainTextResponse('Hello, world!');

We recommend following the semantic of providing the status and headers as the final two arguments for any factory or custom response extensions.