Traits en PHP: reutilizar código entre clases sin herencia múltiple

Los traits de PHP resuelven el problema de querer compartir código entre clases que no tienen una relación de herencia lógica. Un trait es un conjunto de métodos que se puede incluir en cualquier clase con use, sin crear una jerarquía artificial.

Sintaxis básica de un trait

<?php
trait Saludable
{
    public function saludar(): string
    {
        return "Hola, soy {$this->nombre}";
    }

    public function despedir(): string
    {
        return "Hasta pronto, {$this->nombre}";
    }
}

class Persona
{
    use Saludable;

    public function __construct(public string $nombre) {}
}

class Robot
{
    use Saludable;

    public function __construct(public string $nombre) {}
}

$p = new Persona('Ana');
$r = new Robot('HAL-9000');
echo $p->saludar();  // Hola, soy Ana
echo $r->despedir(); // Hasta pronto, HAL-9000
?>

Traits con métodos abstractos

Un trait puede declarar métodos abstractos para forzar a la clase que lo use a implementarlos:

<?php
trait Validable
{
    abstract protected function reglasValidacion(): array;

    public function esValido(array $datos): bool
    {
        foreach ($this->reglasValidacion() as $campo => $tipo) {
            if (!isset($datos[$campo])) return false;
            if ($tipo === 'email' && !filter_var($datos[$campo], FILTER_VALIDATE_EMAIL)) {
                return false;
            }
            if ($tipo === 'int' && !is_int($datos[$campo])) {
                return false;
            }
        }
        return true;
    }
}

class FormularioContacto
{
    use Validable;

    protected function reglasValidacion(): array
    {
        return [
            'nombre' => 'string',
            'email'  => 'email',
            'edad'   => 'int',
        ];
    }
}

$form = new FormularioContacto();
var_dump($form->esValido(['nombre' => 'Ana', 'email' => '[email protected]', 'edad' => 30]));
// bool(true)
?>

Resolver conflictos entre traits con insteadof y as

Cuando dos traits tienen métodos con el mismo nombre, PHP obliga a resolver el conflicto explícitamente:

<?php
trait LoggerA
{
    public function log(string $msg): void
    {
        echo "[A] $msgn";
    }
}

trait LoggerB
{
    public function log(string $msg): void
    {
        echo "[B] $msgn";
    }
}

class Servicio
{
    use LoggerA, LoggerB {
        LoggerA::log insteadof LoggerB; // usar LoggerA::log
        LoggerB::log as logB;           // renombrar LoggerB::log
    }
}

$s = new Servicio();
$s->log('evento');  // [A] evento
$s->logB('evento'); // [B] evento
?>

Caso real: patrón Timestampable

El uso más extendido de los traits es añadir campos de auditoría (created_at, updated_at) a varios modelos sin repetir el mismo código en cada uno:

<?php
trait Timestampable
{
    private DateTimeImmutable $creadoEn;
    private DateTimeImmutable $actualizadoEn;

    public function initTimestamps(): void
    {
        $this->creadoEn      = new DateTimeImmutable();
        $this->actualizadoEn = new DateTimeImmutable();
    }

    public function touch(): void
    {
        $this->actualizadoEn = new DateTimeImmutable();
    }

    public function getCreadoEn(): DateTimeImmutable
    {
        return $this->creadoEn;
    }

    public function getActualizadoEn(): DateTimeImmutable
    {
        return $this->actualizadoEn;
    }
}

class Articulo
{
    use Timestampable;

    public function __construct(
        public readonly string $titulo,
        public readonly string $cuerpo,
    ) {
        $this->initTimestamps();
    }
}

class Comentario
{
    use Timestampable;

    public function __construct(
        public readonly string $texto,
    ) {
        $this->initTimestamps();
    }
}

$art = new Articulo('PHP 8.4', 'Novedades...');
echo $art->getCreadoEn()->format('Y-m-d H:i:s');
// 2026-06-26 11:00:00
?>

La documentación oficial de traits en PHP describe las reglas de precedencia, el manejo de conflictos y las restricciones sobre propiedades estáticas y constantes dentro de traits.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP