PHP PSR Standards — Complete Guide to PHP-FIG Recommendations
In this tutorial, you will learn about PHP PSR Standards. We cover key concepts, practical examples, and best practices to help you master this topic.
PHP PSR standards from PHP-FIG define coding style, autoloading, HTTP messaging, container interfaces, and middleware patterns that enable interoperable PHP packages and frameworks.
What You'll Learn
By the end of this tutorial, you'll understand major PSR standards, implement PSR-4 autoloading, use PSR-7 HTTP messages, PSR-11 containers, PSR-15 middleware, and choose the right PSR for your project.
Why PSR Matters
PSR standards make PHP packages work together. Following PSRs ensures your code integrates with any framework, autoloader, or HTTP client that follows the same standards.
Real-World Use
A custom framework uses PSR-4 autoloading, PSR-7 request/response, PSR-11 dependency injection, and PSR-15 middleware. It works with Symfony, Laravel, and Slim packages unchanged.
PSR Path
flowchart LR
A[PHP 8 Features] --> B[PSR Overview]
B --> C[PSR-7 HTTP]
B --> D[PSR-11 Container]
C --> E[PSR-15 Middleware]
B --> F{You Are Here}
style F fill:#f90,color:#fff
PSR-1 and PSR-12 Coding Standards
PSR-1 defines basic coding style, PSR-12 extends it with modern PHP syntax rules.
<?php
declare(strict_types=1);
namespace Vendor\Package;
use Psr\Http\Message\ResponseInterface;
class UserController
{
public function __construct(
private ResponseInterface $response
) {}
public function show(int $id): ResponseInterface
{
return $this->response;
}
}
PSR-4 Autoloading
PSR-4 maps namespaces to directory structures without complex directory trees.
// composer.json
{
"autoload": {
"psr-4": {
"App\\": "src/",
"Vendor\\Package\\": "src/Package/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Test\\": "tests/"
}
}
}
PSR-7 HTTP Messages
PSR-7 defines Request, Response, Uri, and Stream interfaces for HTTP.
<?php
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
function handleRequest(ServerRequestInterface $request): ResponseInterface {
$method = $request->getMethod();
$path = $request->getUri()->getPath();
$headers = $request->getHeaders();
$body = $request->getBody()->__toString();
$response = new \GuzzleHttp\Psr7\Response();
$response->getBody()->write("Received {$method} {$path}");
return $response;
}
PSR-11 Container Interface
PSR-11 defines a standard container interface for dependency injection.
<?php
use Psr\Container\ContainerInterface;
class ServiceProvider {
public function __construct(
private ContainerInterface $container
) {}
public function getUserService(): UserService {
return $this->container->get(UserService::class);
}
}
PSR-14 Event Dispatcher
PSR-14 defines a standard event dispatching system.
<?php
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\EventDispatcher\ListenerProviderInterface;
use Psr\EventDispatcher\StoppableEventInterface;
class OrderPlaced {
public bool $propagationStopped = false;
}
class SendEmailListener {
public function __invoke(OrderPlaced $event): void {
echo "Sending email for order\n";
}
}
Common Mistakes
1. Ignoring PSR Standards
Non-standard code limits interoperability with the PHP ecosystem. Follow PSRs for package compatibility.
2. Mixing Autoloading Strategies
PSR-0 and PSR-4 work differently. Choose PSR-4 for new projects.
3. Mutating PSR-7 Messages Incorrectly
PSR-7 messages are immutable. Methods like withHeader() return new instances.
4. Not Using Type Hints for PSR Interfaces
Type hints ensure PSR Compliance. Always type-hint against PSR interfaces, not implementations.
5. Over-Engineering for Standards Compliance
PSRs are recommendations, not laws. Use them where they add value, not dogmatically.
Practice Questions
1. What does PSR-4 define?
Autoloading standard mapping namespaces to directory paths without deep nesting.
2. What is the purpose of PSR-7?
Standard HTTP message interfaces for requests and responses, enabling framework interoperability.
3. What does PSR-11 define?
A standard ContainerInterface with get() and has() methods for dependency injection containers.
4. Are PSR-7 messages mutable or immutable?
Immutable. Methods like withHeader() return new instances with the modification applied.
5. Challenge: Create a PSR-7 compatible middleware using PSR-15.
<?php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class JsonBodyParser implements MiddlewareInterface {
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
$contentType = $request->getHeaderLine("Content-Type");
if (str_contains($contentType, "application/json")) {
$body = json_decode((string) $request->getBody(), true);
$request = $request->withParsedBody($body ?? []);
}
return $handler->handle($request);
}
}
FAQ
Mini Project: PSR-4 Autoloader Implementation
Build a simple PSR-4 autoloader to understand how it works.
<?php
class Psr4Autoloader {
protected array $prefixes = [];
public function addNamespace(string $prefix, string $baseDir): void {
$this->prefixes[$prefix] = rtrim($baseDir, "/") . "/";
}
public function register(): void {
spl_autoload_register([$this, "loadClass"]);
}
public function loadClass(string $class): void {
if ($prefix = $this->findPrefix($class)) {
$relativeClass = substr($class, strlen($prefix));
$file = $this->prefixes[$prefix] . str_replace("\\", "/", $relativeClass) . ".php";
if (file_exists($file)) require $file;
}
}
protected function findPrefix(string $class): ?string {
foreach ($this->prefixes as $prefix => $dir) {
if (str_starts_with($class, $prefix)) return $prefix;
}
return null;
}
}
What's Next
PHP PSR-7 HTTP Messages PHP PSR-11 Container PHP PSR-15 Middleware
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro