W3docs

Programación orientada a objetos en PHP: comprender los traits

Aprende los traits de PHP: declararlos, combinar varios, resolver conflictos con insteadof y as, y usar miembros abstractos y estáticos.

Un trait es un bloque reutilizable de métodos (y propiedades) que puedes mezclar en cualquier clase. Los traits resuelven un problema específico en PHP: una clase solo puede extender un padre, por lo que cuando dos clases no relacionadas necesitan compartir el mismo comportamiento, la herencia simple no es suficiente. Los traits permiten compartir ese comportamiento de forma horizontal, entre clases que no tienen ninguna relación padre-hijo.

Este capítulo cubre qué son los traits, cómo declararlos y usarlos, cómo combinar varios traits, cómo PHP resuelve los conflictos de nombres de métodos y los problemas (métodos abstractos, miembros estáticos, propiedades) que suelen causar confusión.

¿Qué es un trait?

Un trait tiene un aspecto casi idéntico al de una clase, pero se declara con la palabra clave trait en lugar de class. Las diferencias clave son:

  • Un trait no puede ser instanciado por sí solo — no existe new MyTrait().
  • Un trait se usa dentro de una clase con la palabra clave use. Sus métodos se copian entonces en la clase como si los hubieras escrito allí.
  • Una clase puede usar cualquier número de traits, y un trait puede usar otros traits.

Piensa en un trait como un copiar-y-pegar asistido por el compilador: el código del trait se incorpora literalmente en cada clase que lo usa.

Declarar y usar un trait

Declara un trait con trait, luego inclúyelo en una clase con use:

<?php

trait Greetable
{
    public function greet(): string
    {
        return "Hello, my name is {$this->name}.";
    }
}

class User
{
    public function __construct(public string $name) {}

    use Greetable;
}

$user = new User("Ada");
echo $user->greet();
// Output: Hello, my name is Ada.

Observa que el trait hace referencia a $this->name aunque no lo define. Un trait se ejecuta en el contexto de la clase que lo usa, por lo que puede depender de propiedades y métodos que proporciona esa clase.

Usar múltiples traits

Una clase puede usar varios traits a la vez. Sepáralos con comas o lista cada uno con su propia instrucción use:

<?php

trait Loggable
{
    public function log(string $message): string
    {
        return "[LOG] " . $message;
    }
}

trait Jsonable
{
    public function toJson(): string
    {
        return json_encode(get_object_vars($this));
    }
}

class Order
{
    use Loggable, Jsonable;

    public function __construct(public int $id, public float $total) {}
}

$order = new Order(7, 49.99);
echo $order->log("created") . PHP_EOL;
echo $order->toJson();
// Output:
// [LOG] created
// {"id":7,"total":49.99}

Resolver conflictos con insteadof y as

Si dos traits definen un método con el mismo nombre, PHP genera un error fatal a menos que le indiques cuál conservar. Usa insteadof para elegir el ganador y as para mantener el perdedor bajo un alias:

<?php

trait FileStorage
{
    public function save(): string
    {
        return "Saved to a file.";
    }
}

trait DatabaseStorage
{
    public function save(): string
    {
        return "Saved to the database.";
    }
}

class Report
{
    use FileStorage, DatabaseStorage {
        DatabaseStorage::save insteadof FileStorage;
        FileStorage::save as saveToFile;
    }
}

$report = new Report();
echo $report->save() . PHP_EOL;       // DatabaseStorage wins
echo $report->saveToFile();           // still reachable via the alias
// Output:
// Saved to the database.
// Saved to a file.

La palabra clave as también puede cambiar la visibilidad de un método, por ejemplo protected reset as private — útil cuando deseas que un método del trait esté disponible internamente pero no como parte de la API pública.

Miembros abstractos y estáticos

Los traits pueden hacer más que contener métodos de instancia concretos.

  • Los métodos abstractos permiten que un trait exija que la clase que lo usa implemente algo. Así es como un trait declara una dependencia de su clase anfitriona.
  • Los métodos y propiedades estáticos se comportan como miembros estáticos normales, pero cada clase que usa el trait obtiene su propia copia de cualquier propiedad estática.
<?php

trait Counter
{
    private static int $count = 0;

    public static function increment(): int
    {
        return ++self::$count;
    }

    // The using class MUST provide this:
    abstract public function label(): string;
}

class PageView
{
    use Counter;

    public function label(): string
    {
        return "views";
    }
}

echo PageView::increment() . PHP_EOL; // 1
echo PageView::increment() . PHP_EOL; // 2
echo (new PageView())->label();       // views
// Output:
// 1
// 2
// views

Traits vs. herencia e interfaces

Elige la herramienta adecuada:

  • La herencia (extends) modela una relación "es-un" y te proporciona un único padre. Úsala cuando las clases comparten genuinamente una jerarquía de tipos. Ver PHP Inheritance.
  • Las interfaces definen un contrato — qué métodos existen — pero no contienen implementación. Ver PHP Interfaces.
  • Los traits proporcionan implementación que puedes mezclar en clases no relacionadas. Un patrón común es una interfaz + un trait: la interfaz anuncia la capacidad, el trait proporciona el código predeterminado.

Si eres nuevo en estos conceptos, comienza con PHP Classes and Objects y PHP Abstract Classes.

Problemas comunes

  • Las colisiones de nombres son silenciosas hasta que no lo son. Combinar traits que definen el mismo método produce un error fatal; resuélvelo siempre con insteadof/as.
  • Los traits se aplanan, no se heredan. Un método definido directamente en la clase anula la versión del trait, y la versión del trait anula cualquier cosa heredada de una clase padre.
  • Las propiedades estáticas son por clase. Dos clases que usan el mismo trait no comparten su estado estático — cada una obtiene una copia separada.
  • No abuses de ellos. Un trait que necesita muchas propiedades de su clase anfitriona suele ser una señal de que la composición (un objeto real) o la herencia encajan mejor.

Conclusión

Los traits ofrecen a PHP una forma limpia de compartir implementaciones de métodos entre clases no relacionadas, sorteando la limitación de herencia simple. Declararlos con trait, incluirlos con use, resolver conflictos con insteadof y as, y recordar que el código del trait se aplana en cada clase que lo usa. Usados con prudencia, los traits mantienen el comportamiento transversal — registro, serialización, contadores — en un solo lugar en lugar de copias dispersas.

Práctica

Práctica
¿Cuáles son las características de los traits de PHP?
¿Cuáles son las características de los traits de PHP?
Was this page helpful?