Skip to content

PHP PSR Standards — Complete Guide to PHP-FIG Recommendations

DodaTech Updated 2026-06-28 4 min read

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

What is PHP-FIG?

PHP Framework Interop Group. A group of PHP project representatives that establish PSR standards.

Are PSRs mandatory?

No. PSRs are recommendations. Following them improves interoperability.

Which PSR is most commonly used?

PSR-4 (autoloading) is adopted by virtually all modern PHP projects.

Do I need to follow all PSRs?

No. Pick the relevant ones for your project type (library, framework, application).

What is the latest PSR?

PSR-20 (Clock interface) is the most recent accepted PSR as of 2026.

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