php-traits-en-php

Traits en PHP

  • 4 min

Un trait es un mecanismo para reutilizar métodos y propiedades entre clases sin crear una jerarquía de herencia.

Hasta ahora hemos visto la Herencia (extends). Es una relación Vertical: El abuelo pasa cosas al padre, y el padre al hijo. Pero, ¿qué pasa cuando quieres compartir código entre clases que no son familia?

Imagina que tienes una clase Usuario y una clase Pedido. No tienen nada que ver (un Usuario es una persona, un Pedido es un documento). Sin embargo, quieres que ambos tengan un sistema para generar un ID único (UUID).

  • ¿Creas una clase padre GeneradorID y haces que hereden de ella? Mal. Usuario ya hereda de Persona. PHP no te deja tener dos padres.
  • ¿Copias y pegas el código en ambos archivos? Mal. Violas el principio DRY.

Para esto nacieron los Traits. Son mecanismos de reutilización Horizontal.

¿Qué es un trait?

Piensa en un Trait como un bloque de código “copiable”. Es un fragmento de clase que puedes inyectar dentro de cualquier otra clase, sin importar su jerarquía.

Puedes pensar que PHP compone los miembros del trait dentro de la clase. No se crea una instancia separada del trait.

Sintaxis básica

Definimos el trait con la palabra trait y lo usamos dentro de la clase con use.

trait Identificable {
    public function generarId(): string {
        return uniqid('ID_');
    }
}

class Usuario {
    use Identificable; // ¡Inyección de código!

    public string $nombre;
}

class Producto {
    use Identificable; // ¡Reutilización!

    public float $precio;
}

// Probamos
$user = new Usuario();
echo $user->generarId(); // Salida: ID_65a4...

$prod = new Producto();
echo $prod->generarId(); // Salida: ID_65a4...
Copied!

Fíjate: Usuario y Producto no son familia, pero ambos comparten la capacidad Identificable.

El orden de precedencia (¿quién manda?)

Si tengo un método en el Padre, otro en el Trait y otro en la Clase… ¿cuál se ejecuta?

La jerarquía es esta:

La clase actual: Si escribes el método en la clase, sobreescribe todo.

El trait: Si la clase no lo tiene, se usa el del Trait.

La clase padre: Es el último recurso.

Trait > Herencia. El Trait “pisa” lo que venga del extends.

class Base {
    public function saludar() { echo "Hola desde Base"; }
}

trait SaludoTrait {
    public function saludar() { echo "Hola desde Trait"; }
}

class Hijo extends Base {
    use SaludoTrait;
    // Si no escribo nada aquí, ganará el Trait.
}

$obj = new Hijo();
$obj->saludar(); // Salida: "Hola desde Trait"
Copied!

Múltiples traits y conflictos

Una clase puede usar todos los traits que quiera.

class SuperClase {
    use Loggeable, Identificable, Autenticable;
}
Copied!

uniqid() sirve para este ejemplo, pero no genera identificadores impredecibles. Para tokens o valores de seguridad usa random_bytes() u otra API criptográficamente segura.

Si dos traits tienen un método con el mismo nombre, PHP lanzará un error fatal. Debes resolver el conflicto mediante insteadof (en lugar de) o as (alias). Siempre que puedas, usa nombres específicos y diseña los traits para reducir estas colisiones.

Traits modernos (PHP 8.2+)

Los Traits han evolucionado. Hoy en día son casi tan potentes como las clases.

Constantes en traits

Desde PHP 8.2, puedes definir constantes dentro de un trait.

trait Configuracion {
    public const VERSION = '1.0';
}

class App {
    use Configuracion;
}

echo App::VERSION;
Copied!

Métodos abstractos en traits

Puedes obligar a la clase que usa el trait a implementar un método.

trait Publicable {
    // La clase que me use DEBE tener este método
    abstract public function getContenido(): string;

    public function publicar(): void {
        echo "Publicando: " . $this->getContenido();
    }
}
Copied!

Esto es brutal para crear “mixins” que añaden funcionalidad pero dependen de datos de la clase anfitriona.

Cuándo NO usar traits

Los Traits son muy potentes, pero peligrosos. Se les llama “GOTO con esteroides” porque hacen que el código sea difícil de seguir. Si abres una clase y ves use A, B, C, D, E..., no tienes ni idea de qué métodos tiene esa clase ni de dónde vienen.

Buenas Prácticas:

  1. Usa Traits para capacidades transversales (Logging, Caching, IDs).
  2. No uses Traits para simular herencia múltiple de lógica de negocio compleja.
  3. Si un Trait es gigante, probablemente debería ser una Clase o un Servicio inyectado.