El PHP-FIG (Framework Interoperability Group) es el consorcio que define los estándares de PHP: las PSR (PHP Standards Recommendations). No son obligatorias, pero casi todos los frameworks modernos (Symfony, Laravel, Slim, Laminas) las siguen, lo que permite que librerías de distintos vendors sean compatibles entre sí. Conocer las PSR más importantes hace tu código más interoperable y tu equipo trabaja con convenciones comunes.
PSR-1 y PSR-12: estilo de código
PSR-1 define las reglas básicas: PHP puro en un fichero o HTML mezclado, pero no los dos; nombres de clase en StudlyCaps; constantes en MAYUSCULAS_CON_GUION; métodos en camelCase.
PSR-12 es la guía de estilo extendida que reemplaza a PSR-2. Define indentación (4 espacios), dónde van las llaves, longitud máxima de línea, orden de modificadores, etc.
<?php
// PSR-12: una clase por fichero, namespace en la primera línea
namespace AppServicios;
use AppContratosRepositorioUsuario;
use AppModelosUsuario;
// StudlyCaps para clases, camelCase para métodos
class ServicioRegistro
{
public function __construct(
private readonly RepositorioUsuario $repositorio,
private readonly string $dominio = 'ejemplo.com',
) {
}
public function registrar(string $email, string $password): Usuario
{
// Cuerpo del método con 4 espacios de indentación
if (!str_contains($email, '@')) {
throw new InvalidArgumentException('Email inválido');
}
return $this->repositorio->crear($email, password_hash($password, PASSWORD_BCRYPT));
}
}
PSR-3: Logger Interface
Define una interfaz LoggerInterface con 8 métodos (uno por nivel de severidad RFC 5424). Cualquier librería que acepte un LoggerInterface es compatible con Monolog, el logger de Symfony o cualquier otra implementación:
<?php
use PsrLogLoggerInterface;
use PsrLogLogLevel;
class ProcesadorPedidos {
public function __construct(private LoggerInterface $logger) {}
public function procesar(int $pedidoId): void {
$this->logger->info('Procesando pedido', ['id' => $pedidoId]);
try {
// ... lógica de negocio
$this->logger->debug('Pedido completado', ['id' => $pedidoId]);
} catch (Exception $e) {
// emergency, alert, critical, error, warning, notice, info, debug
$this->logger->error('Error procesando pedido', [
'id' => $pedidoId,
'excepcion' => $e->getMessage(),
'traza' => $e->getTraceAsString(),
]);
throw $e;
}
}
}
// En tests puedes pasar un NullLogger; en producción, Monolog
PSR-7: HTTP Messages
Define interfaces inmutables para peticiones y respuestas HTTP. Todos los frameworks modernos las usan, lo que permite que middlewares escritos para Slim funcionen en Laminas sin modificaciones:
<?php
use PsrHttpMessageServerRequestInterface;
use PsrHttpMessageResponseInterface;
// Un middleware PSR-7: recibe Request, devuelve Response
// Las interfaces son inmutables: withHeader() devuelve una NUEVA instancia
function addCorsHeaders(
ServerRequestInterface $request,
callable $next
): ResponseInterface {
$response = $next($request);
return $response
->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
}
// Crear una respuesta JSON
use NyholmPsr7Response;
use NyholmPsr7Stream;
$body = Stream::create(json_encode(['ok' => true]));
$response = (new Response(200))
->withHeader('Content-Type', 'application/json')
->withBody($body);
PSR-11: Container Interface
Define la interfaz de un contenedor de dependencias con solo dos métodos: get(string $id) y has(string $id). Cualquier contenedor que la implemente (PHP-DI, Symfony DI, Pimple) puede usarse donde se espera un ContainerInterface:
<?php
use PsrContainerContainerInterface;
use PsrContainerNotFoundExceptionInterface;
class MiAccion {
public function __construct(private ContainerInterface $container) {}
public function ejecutar(string $servicio): void {
if (!$this->container->has($servicio)) {
throw new RuntimeException("Servicio '$servicio' no registrado");
}
$obj = $this->container->get($servicio);
$obj->ejecutar();
}
}
PSR-15: HTTP Server Request Handlers
Completa PSR-7 definiendo las interfaces RequestHandlerInterface y MiddlewareInterface. Un middleware PSR-15 recibe la petición y un handler siguiente; puede cortocircuitar la cadena devolviendo una respuesta propia:
<?php
use PsrHttpMessageServerRequestInterface;
use PsrHttpMessageResponseInterface;
use PsrHttpServerMiddlewareInterface;
use PsrHttpServerRequestHandlerInterface;
class AutenticacionMiddleware implements MiddlewareInterface {
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$token = $request->getHeaderLine('Authorization');
if (!$this->verificarToken($token)) {
// Cortocircuitar: no llamar al siguiente handler
return new NyholmPsr7Response(401, [], '{"error":"No autorizado"}');
}
// Añadir datos al request y continuar la cadena
$request = $request->withAttribute('usuario_id', $this->extraerUserId($token));
return $handler->handle($request);
}
private function verificarToken(string $token): bool { /* ... */ return true; }
private function extraerUserId(string $token): int { /* ... */ return 1; }
}
Resumen de las PSR más usadas
- PSR-1: convenciones básicas de nomenclatura y estructura de ficheros.
- PSR-3: interfaz de logger con 8 niveles de severidad.
- PSR-4: autoloading de clases por namespace (la base de Composer).
- PSR-7: interfaces HTTP Request/Response inmutables.
- PSR-11: interfaz de contenedor de dependencias.
- PSR-12: guía de estilo de código extendida.
- PSR-14: interfaz de event dispatcher.
- PSR-15: interfaces de middleware HTTP y request handlers.
