PHP PSR-17 HTTP Factories — Complete Guide to Request and Response Factories
In this tutorial, you will learn about PHP PSR. We cover key concepts, practical examples, and best practices to help you master this topic.
PHP PSR-17 defines Factory interfaces for creating PSR-7 HTTP message objects, enabling Dependency Injection of request, response, stream, URI, and uploaded file instances.
What You'll Learn
By the end of this tutorial, you'll use PSR-17 factories to create requests, responses, streams, URIs, and uploaded files, implement custom factories, and integrate with PSR-11 containers.
Why PSR-17 Matters
PSR-7 objects need constructors with many parameters. PSR-17 factories simplify creation and enable testability by allowing factory injection.
Real-World Use
A controller receives a ResponseFactoryInterface via dependency injection. It creates JSON responses with status codes without depending on a specific PSR-7 implementation.
PSR-17 Path
flowchart LR
A[PSR-15 Middleware] --> B[PSR-17 HTTP Factory]
B --> C[PHP-DI Container]
C --> D[PSR-11 Container]
D --> E[Middleware Slim]
B --> F{You Are Here}
style F fill:#f90,color:#fff
Response Factory
Create responses with factory instead of constructor.
<?php
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
class JsonController {
public function __construct(
private ResponseFactoryInterface $responseFactory
) {}
public function success(mixed $data, int $status = 200): ResponseInterface {
$response = $this->responseFactory->createResponse($status);
$body = $response->getBody();
$body->write(json_encode(["data" => $data]));
return $response
->withHeader("Content-Type", "application/json")
->withBody($body);
}
}
Request Factory
Create server requests from HTTP inputs.
<?php
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\ServerRequestInterface;
class RequestBuilder {
public function __construct(
private ServerRequestFactoryInterface $requestFactory,
private UriFactoryInterface $uriFactory,
) {}
public function createFromGlobals(): ServerRequestInterface {
$uri = $this->uriFactory->createUri($_SERVER["REQUEST_URI"]);
$request = $this->requestFactory->createServerRequest(
$_SERVER["REQUEST_METHOD"],
$uri,
$_SERVER
);
return $request
->withParsedBody($_POST)
->withQueryParams($_GET)
->withCookieParams($_COOKIE)
->withUploadedFiles($_FILES);
}
}
Stream Factory
Create streams from strings, files, or resources.
<?php
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
class FileDownloadController {
public function __construct(
private StreamFactoryInterface $streamFactory,
private ResponseFactoryInterface $responseFactory,
) {}
public function download(string $filePath): ResponseInterface {
$stream = $this->streamFactory->createStreamFromFile($filePath, "r");
$response = $this->responseFactory->createResponse(200);
return $response
->withBody($stream)
->withHeader("Content-Type", mime_content_type($filePath))
->withHeader("Content-Disposition", "attachment; filename=\"" . basename($filePath) . "\"");
}
}
URI Factory
Create and manipulate URIs using factory.
<?php
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
class LinkBuilder {
public function __construct(
private UriFactoryInterface $uriFactory
) {}
public function buildLink(string $path, array $params = []): UriInterface {
$uri = $this->uriFactory->createUri("https://example.com");
$uri = $uri->withPath($path);
if (!empty($params)) {
$uri = $uri->withQuery(http_build_query($params));
}
return $uri;
}
}
Uploaded File Factory
Create uploaded file instances from PHP $_FILES arrays.
<?php
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UploadedFileInterface;
class FileUploadHandler {
public function __construct(
private UploadedFileFactoryInterface $uploadedFileFactory,
private StreamFactoryInterface $streamFactory,
) {}
public function processUpload(array $fileInfo): UploadedFileInterface {
$stream = $this->streamFactory->createStreamFromFile($fileInfo["tmp_name"]);
return $this->uploadedFileFactory->createUploadedFile(
$stream,
$fileInfo["size"],
$fileInfo["error"],
$fileInfo["name"],
$fileInfo["type"]
);
}
}
Common Mistakes
1. Creating Factory Instances Manually
The point of factories is to inject them. Creating them manually defeats the purpose.
2. Not Injecting Factories in Controllers
Controllers should receive factory interfaces, not concrete implementations, for testability.
3. Forgetting to Set Body on Response
Responses created with createResponse() have an empty body. Always write data to the body.
4. Confusing ServerRequest and Request
ServerRequestFactory creates ServerRequest with server params. RequestFactory creates general Request objects.
5. Not Using Factory for Body Rewriting
When modifying response body, use the stream factory to create new streams rather than modifying directly.
Practice Questions
1. What is the purpose of PSR-17?
Provides factory interfaces for creating PSR-7 HTTP message objects.
2. Which factory would you inject to create response objects?
ResponseFactoryInterface::createResponse(int $code, string $reasonPhrase = '').
3. How do you create a stream from a string?
StreamFactoryInterface::createStream(string $content) wraps a string in a PSR-7 stream.
4. Why should controllers use factories instead of new Response()?
For testability and decoupling. Factories can be mocked or swapped for different PSR-7 implementations.
5. Challenge: Create a PSR-17 factory implementation for Nyholm PSR-7.
<?php
use Nyholm\Psr7\Factory\Psr17Factory;
$factory = new Psr17Factory();
$response = $factory->createResponse(200);
$stream = $factory->createStream("Hello World");
$uri = $factory->createUri("https://example.com/path");
$request = $factory->createServerRequest("GET", $uri);
FAQ
Mini Project: PSR-17 Based Controller Base Class
Build a base controller using PSR-17 factories.
<?php
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
abstract class BaseController {
public function __construct(
protected ResponseFactoryInterface $responseFactory,
protected StreamFactoryInterface $streamFactory,
) {}
protected function json(mixed $data, int $status = 200): ResponseInterface {
$response = $this->responseFactory->createResponse($status)
->withHeader("Content-Type", "application/json");
$body = $this->streamFactory->createStream(json_encode($data));
return $response->withBody($body);
}
protected function html(string $content, int $status = 200): ResponseInterface {
$response = $this->responseFactory->createResponse($status)
->withHeader("Content-Type", "text/html");
$body = $this->streamFactory->createStream($content);
return $response->withBody($body);
}
protected function redirect(string $url, int $status = 302): ResponseInterface {
return $this->responseFactory->createResponse($status)
->withHeader("Location", $url);
}
}
What's Next
PHP Dependency Injection PHP Middleware Slim PHP PSR-7 HTTP Messages
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro