PHP PSR Standards — Complete Guide to PHP-FIG Coding Standards
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 are a set of PHP-FIG (PHP Framework Interop Group) recommendations that define coding styles, autoloading strategies, and common interfaces so PHP libraries and frameworks work together seamlessly. This tutorial covers the most important PSRs: PSR-1 for basic coding style, PSR-4 for autoloading, PSR-12 for extended coding style, PSR-7 for HTTP messages, PSR-11 for container interfaces, and PSR-14 for event dispatching.
What You'll Learn
- Understanding what PHP-FIG is and why PSRs exist
- Following PSR-1 and PSR-12 coding style rules
- Configuring PSR-4 autoloading with Composer
- Implementing PSR-7 HTTP message interfaces
- Using PSR-11 ContainerInterface for dependency injection
- Applying PSR-14 EventDispatcherInterface
- Validating your code with PHP_CodeSniffer
Why It Matters
Without standards, every PHP project and framework invents its own conventions. A class in Laravel is namespaced differently than the same concept in Symfony. PSRs solve this at the ecosystem level: any PSR-4 compliant autoloader can load any PSR-4 compliant class. Any PSR-7 message can be passed between middleware from different frameworks. Following PSRs makes your code predictable, familiar, and reusable across the entire PHP ecosystem.
Real-World Use
Durga Antivirus Pro uses PSR-7 messages for its REST API layer. Because the request and response objects implement the standard interfaces, the team can swap routing libraries without rewriting controllers. The same PSR-7 request that enters through Nginx middleware can be passed to a Symfony controller or a slim PHP script without modification.
Learning Path
flowchart LR A[Composer] --> B[PSR Standards\nYou are here] B --> C[Laravel Basics] style B fill:#f90,color:#fff
PSR-1: Basic Coding Standard
PSR-1 sets the foundation for all other PSRs. Key rules:
- Files must use
<?phptags (or<?=for templates). - Files must use UTF-8 without BOM encoding.
- Namespaces and classes must follow PSR-4 autoloading.
- Class names must be declared in
StudlyCaps. - Method names must be declared in
camelCase. - Class constants must be all uppercase with underscore separators.
Example of compliant code:
<?php
namespace App\Http;
class UserController
{
public const MAX_LOGIN_ATTEMPTS = 5;
public function login(string $email, string $password): bool
{
// Implementation
return false;
}
}
PSR-4: Autoloading
PSR-4 maps namespaces to directory structures. The namespace prefix corresponds to a base directory, and sub-namespaces map to subdirectories.
Consider this directory structure:
src/
App/
Models/
User.php → App\Models\User
Order.php → App\Models\Order
Services/
Mailer.php → App\Services\Mailer
Configure PSR-4 in composer.json:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
A class at src/Models/User.php:
<?php
namespace App\Models;
class User
{
public function __construct(
private string $name,
private string $email
) {}
public function display(): string
{
return "{$this->name} <{$this->email}>";
}
}
Use it without manual requires:
<?php
require 'vendor/autoload.php';
$user = new \App\Models\User('Bob', 'bob@example.com');
echo $user->display();
Output:
Bob <bob@example.com>
PSR-12: Extended Coding Style
PSR-12 extends PSR-1 with detailed formatting rules. Key requirements:
- Use 4 spaces for indentation, no tabs.
- Line length should be under 120 characters (80 preferred).
- One blank line after the namespace declaration.
- Opening braces for classes go on the next line.
- Opening braces for methods go on the next line.
- Control structure keywords have one space after them.
- Visibility (
public,protected,private) must be declared on all properties and methods.
<?php
namespace App\Services;
use App\Models\User;
class AuthService
{
public function __construct(
private User $user
) {}
public function verify(string $password): bool
{
return password_verify($password, $this->user->getPasswordHash());
}
}
Validate with PHP_CodeSniffer:
composer require --dev squizlabs/php_codesniffer
vendor/bin/phpcs --standard=PSR12 src/
PSR-7: HTTP Message Interfaces
PSR-7 defines interfaces for HTTP requests and responses. The key interfaces are:
Psr\Http\Message\RequestInterfacePsr\Http\Message\ResponseInterfacePsr\Http\Message\ServerRequestInterfacePsr\Http\Message\StreamInterfacePsr\Http\Message\UriInterface
Using a PSR-7 implementation like guzzlehttp/psr7:
composer require guzzlehttp/psr7
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\ServerRequest;
$request = new ServerRequest('POST', '/api/login');
$request = $request->withHeader('Content-Type', 'application/json');
$body = json_encode(['email' => 'user@example.com', 'password' => 'secret']);
$request->getBody()->write($body);
$response = new Response(200, ['Content-Type' => 'application/json']);
$response->getBody()->write(json_encode(['token' => 'abc123']));
echo $response->getStatusCode();
echo $response->getHeaderLine('Content-Type');
echo $response->getBody();
Output:
200
application/json
{"token":"abc123"}
PSR-11: Container Interface
PSR-11 defines ContainerInterface with two methods: get and has. This is the standard for dependency injection containers.
<?php
namespace Psr\Container;
interface ContainerInterface
{
public function get(string $id): mixed;
public function has(string $id): bool;
}
A minimal PSR-11 container:
<?php
namespace App\Container;
use Psr\Container\ContainerInterface;
class SimpleContainer implements ContainerInterface
{
private array $services = [];
public function set(string $id, callable $factory): void
{
$this->services[$id] = $factory;
}
public function get(string $id): mixed
{
if (!$this->has($id)) {
throw new \Psr\Container\NotFoundExceptionInterface("Service $id not found");
}
return ($this->services[$id])($this);
}
public function has(string $id): bool
{
return isset($this->services[$id]);
}
}
Usage:
<?php
require 'vendor/autoload.php';
use App\Container\SimpleContainer;
class Logger
{
public function log(string $message): void
{
echo "LOG: $message\n";
}
}
$container = new SimpleContainer();
$container->set(Logger::class, fn() => new Logger());
$logger = $container->get(Logger::class);
$logger->log('Container is working');
Output:
LOG: Container is working
Common Mistakes
- Using tabs for indentation: PSR-12 requires 4 spaces. Most editors can be configured to convert tabs to spaces automatically.
- Mixing naming conventions: PHP allows both camelCase and snake_case for methods, but PSR-1 requires camelCase. Stick with one convention across your project.
- Ignoring visibility on properties: In PHP 8, you can use constructor promotion, but standalone properties must still declare public, protected, or private.
- Not running PHP_CodeSniffer in CI: Style violations accumulate quickly. Add phpcs to your CI pipeline to enforce PSR-12 automatically.
- Implementing PSR-7 from scratch: Always use an existing implementation like
guzzlehttp/psr7ornyholm/psr7. The interfaces are complex and easy to get wrong.
Practice Questions
What does PSR-4 specify?
- PSR-4 maps namespace prefixes to directory paths. The namespace
App\Models\Usermaps tosrc/Models/User.phpwhenApp\maps tosrc/.
- PSR-4 maps namespace prefixes to directory paths. The namespace
What is the difference between PSR-1 and PSR-12?
- PSR-1 covers basic conventions (tags, encoding, naming). PSR-12 extends it with detailed formatting rules for braces, indentation, whitespace, and control structures.
Why is the composer.lock file important for PSR-4 autoloading?
- The lock file ensures every developer uses the same version of the autoloader generator, preventing subtle class-loading differences.
What are the four PSR-7 message interfaces?
- RequestInterface, ResponseInterface, ServerRequestInterface, and MessageInterface (base interface). StreamInterface and UriInterface are also commonly grouped with PSR-7.
Challenge: Create a middleware pipeline that uses PSR-7 messages. Implement a RequestHandlerInterface that chains three middleware functions: authentication, logging, and routing.
Mini Project
Build a PSR-compliant micro-framework:
- Use PSR-7 for request/response handling with
guzzlehttp/psr7. - Implement a PSR-11 container that supports autowiring.
- Create a router that uses PSR-7 ServerRequestInterface and returns PSR-7 ResponseInterface.
- Validate all code against PSR-12 using PHP_CodeSniffer.
- Write a controller and a middleware that uses the framework.
FAQ
What's Next
Now that you understand PHP standards, apply them in a real framework by learning Laravel Basics.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro