Etiqueta: compatibilidad

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

    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.