Guía rápida de attributes en PHP 8+: uso y ejemplos

Los attributes en PHP 8+ permiten adjuntar metadatos nativos a clases, métodos, propiedades y parámetros. Esta guía muestra cómo definirlos, aplicarlos y leerlos con Reflection, incluyendo ejemplos prácticos.

Introducción

Antes de PHP 8 se usaban docblocks y parsing de comentarios para añadir metadatos (por ejemplo, rutas). Los attributes son nativos: son clases usadas como metadatos mediante sintaxis literal, p. ej. #[Route('/users')].

Prerrequisitos

Necesitas PHP 8.0+ (los attributes son una característica introducida con PHP 8). También conviene familiaridad básica con clases y la API de Reflection para leer atributos en tiempo de ejecución.

Desarrollo

Procedimiento

  1. Crear la clase que representará el attribute y marcarla con #[Attribute].
  2. Aplicar el attribute sobre clases, métodos, propiedades o parámetros según correspondan.
  3. Leer los atributos en tiempo de ejecución usando Reflection y, si hace falta, instanciarlos.

A continuación hay snippets que ilustran cada paso: definición, uso y lectura por Reflection.

<?php
#[Attribute(Attribute::TARGET_METHOD)]
class Route
{
    public function __construct(
        public string $path,
        public array $methods = ['GET']
    ) {}
}
Lenguaje del código: PHP (php)
<?php
class UserController
{
    #[Route('/users', methods: ['GET'])]
    public function index() {}
}
Lenguaje del código: PHP (php)
<?php
$method = new ReflectionMethod(UserController::class, 'index');
$attributes = $method->getAttributes();

foreach ($attributes as $attribute) {
    $instance = $attribute->newInstance();
    var_dump($instance->path); // '/users'
}
Lenguaje del código: PHP (php)

Atributos nativos

PHP incluye varios attributes útiles. Aquí hay ejemplos prácticos tomados de la sintaxis nativa que aparecen en el lenguaje.

Ejemplo: #[ReturnTypeWillChange] — se usa para compatibilidad de firmas en bibliotecas.

<?php
class MyIterator implements Iterator
{
    #[ReturnTypeWillChange]
    public function current() {
        return 'foo';
    }
}
Lenguaje del código: PHP (php)

Ejemplo: #[Deprecated] — marca elementos en desuso y puede incluir un motivo.

<?php
#[Deprecated(reason: 'Use newFunction() instead.')]
function oldFunction() {
    return 'legacy';
}
Lenguaje del código: PHP (php)

Ejemplo: #[AllowDynamicProperties] — re-habilita propiedades dinámicas en clases legadas.

<?php
#[AllowDynamicProperties]
class User {}

$user = new User();
$user->name = 'Lukasz'; // funciona
Lenguaje del código: PHP (php)

Ejemplo: #[SensitiveParameter] — oculta valores en trazas y dumps para parámetros sensibles.

<?php
function login(#[SensitiveParameter] string $password) {
    throw new Exception('Oops!');
}

login('super-secret-password');
Lenguaje del código: PHP (php)

Ejemplo: #[Override] — asegura que un método realmente sobrescribe uno del padre.

<?php
class Base {
    public function greet() {}
}

class Child extends Base {
    #[Override]
    public function greet() {
        echo "Hello!";
    }
}
Lenguaje del código: PHP (php)

Ejemplos

Combinar atributos con frameworks o bibliotecas propias suele reducir la necesidad de parsear docblocks y simplifica extractores de metadatos. Un patrón común: definir attributes para rutas, validar parámetros sensibles y marcar API obsoletas.

Ejemplo práctico rápido: definir una ruta y extraerla desde un bootstrap de aplicación para registrar endpoints.

<?php
// Definición ya mostrada arriba: Route
// En bootstrap de la app:
$rc = new ReflectionClass(UserController::class);
foreach ($rc->getMethods() as $method) {
    foreach ($method->getAttributes() as $attr) {
        $route = $attr->newInstance();
        // registrar $route->path y $route->methods en el router
    }
}
Lenguaje del código: PHP (php)

Checklist

  1. Reemplazar parsing de docblocks por classes-attribute cuando sea posible.
  2. Marcar las classes de attribute con #[Attribute] y ajustar TARGET_* según uso.
  3. Actualizar extractores para usar Reflection::getAttributes().
  4. Probar casos de compatibilidad: #[ReturnTypeWillChange] y #[AllowDynamicProperties] si persisten dependencias legadas.
  5. Marcar parámetros sensibles con #[SensitiveParameter] para proteger trazas.

Conclusión

Los attributes son una mejora clara frente a los docblocks: son nativos, más seguros y fáciles de consumir por código. Migrar a attributes reduce parsing ad hoc y coloca la metadata junto al código que la usa.

Si estás manteniendo una librería o framework en PHP, prueba a implementar un pequeño atributo y su extractor: la reducción de complejidad suele notarse rápido.

Comments

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *