Clases anónimas en PHP 7+: new class{} y sus casos de uso prácticos

PHP 7 introdujo las clases anónimas: clases que se definen y se usan en el mismo punto del código, sin nombre y sin fichero propio. Se crean con new class { ... } y pueden extender clases, implementar interfaces y recibir argumentos en el constructor, igual que cualquier clase normal.

Sintaxis básica

<?php
$saludo = new class('Mundo') {
    public function __construct(private string $nombre) {}

    public function saludar(): string {
        return "Hola, {$this->nombre}!";
    }
};

echo $saludo->saludar(); // Hola, Mundo!

Caso de uso 1: implementar una interfaz puntualmente

Cuando necesitas pasar un objeto que implemente una interfaz a una función y no vas a reutilizar esa implementación en ningún otro lugar, una clase anónima evita crear un fichero entero para ese único uso:

<?php
interface Logger {
    public function log(string $mensaje): void;
}

function procesarPedido(int $id, Logger $logger): void {
    $logger->log("Procesando pedido $id");
    // ... lógica del pedido
    $logger->log("Pedido $id completado");
}

// En producción usarías un logger real (Monolog, etc.)
// Para una prueba rápida o script de consola:
procesarPedido(42, new class implements Logger {
    public function log(string $mensaje): void {
        echo date('H:i:s') . " $mensajen";
    }
});

Caso de uso 2: test doubles rápidos

Las clases anónimas son perfectas para crear mocks o stubs inline en tests sin depender de un framework de mocking:

<?php
use PHPUnitFrameworkTestCase;

interface Mailer {
    public function enviar(string $destinatario, string $asunto, string $cuerpo): bool;
}

class RegistroService {
    public function __construct(private Mailer $mailer) {}

    public function registrar(string $email): bool {
        // ... guarda en BD ...
        return $this->mailer->enviar($email, 'Bienvenido', 'Cuenta creada');
    }
}

class RegistroServiceTest extends TestCase {
    public function testRegistrarEnviaEmail(): void {
        $enviados = [];

        $mailerFalso = new class($enviados) implements Mailer {
            public function __construct(private array &$enviados) {}

            public function enviar(string $dest, string $asunto, string $cuerpo): bool {
                $this->enviados[] = compact('dest', 'asunto');
                return true;
            }
        };

        $service = new RegistroService($mailerFalso);
        $service->registrar('[email protected]');

        $this->assertCount(1, $enviados);
        $this->assertSame('[email protected]', $enviados[0]['dest']);
    }
}

Caso de uso 3: callbacks con estado

Cuando necesitas una closure que mantenga estado mutable entre llamadas, una clase anónima puede ser más clara que una closure con use (&$var):

<?php
// Con closure y referencia: funciona pero menos legible
$contador = 0;
$incrementar = function() use (&$contador) { return ++$contador; };

// Con clase anónima: el estado está encapsulado
$contador = new class {
    private int $valor = 0;

    public function incrementar(): int { return ++$this->valor; }
    public function valor(): int       { return $this->valor; }
    public function reset(): void      { $this->valor = 0; }
};

echo $contador->incrementar(); // 1
echo $contador->incrementar(); // 2
echo $contador->valor();       // 2
$contador->reset();
echo $contador->valor();       // 0

Caso de uso 4: extender una clase concreta

Las clases anónimas pueden extender clases y sobreescribir métodos, lo que permite crear variantes puntuales sin herencia formal:

<?php
class BaseController {
    protected function render(string $vista, array $datos = []): string {
        return "Renderizando $vista con " . json_encode($datos);
    }

    public function responder(): string {
        return $this->render('base');
    }
}

// Variante anónima que sobreescribe render para tests
$controller = new class extends BaseController {
    protected function render(string $vista, array $datos = []): string {
        return "TEST:$vista:" . implode(',', array_keys($datos));
    }
};

echo $controller->responder(); // TEST:base:

El error típico: intentar acceder a variables externas sin pasarlas

A diferencia de las closures, las clases anónimas no pueden usar use para capturar variables del scope. Debes pasarlas por el constructor:

<?php
$prefijo = 'ERROR';

// INCORRECTO: las clases anónimas no tienen use
// $obj = new class { public function log($m) { echo "$prefijo: $m"; } }; // Error

// CORRECTO: pasar por el constructor
$obj = new class($prefijo) {
    public function __construct(private string $prefijo) {}
    public function log(string $m): void { echo "{$this->prefijo}: $mn"; }
};

$obj->log('Conexión fallida'); // ERROR: Conexión fallida

COMPARTE ESTE ARTÍCULO

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