Diferencias entre return self y return static en PHP

Este es el típico post que lees en plan «uy pero qué interesanteeee», lo dejas a medias y sigues con tu vida. Pero resulta que tiempo después te encuentras con el problema aquí mencionado y dices «¡porras! ¿dónde leí yo esto?».

Dicho esto podemos empezar.

Comencemos por una sencilla clase vehículo:

class Vehiculo
{
    /** @var string */
    private $marca;

    public function __construct(string $marca)
    {
        $this->marca = $marca;
    }
}

Esta clase la usaremos como siempre:

$vehiculo = new Vehiculo('Ferrero');

Para ver el tipo de objeto que tenemos:

echo get_class($vehiculo);

Ahora imaginemos que queremos dar la posibilidad de crear un objeto sin marca. Para eso añadimos un nuevo método que nos meterá una marca por defecto:

    public static function crearSinMarca()
    {
        return new self("Nisu");
    }

Vale, ahora me vendrá el típico listillo que dirá que a ver por qué no simplemente meto en el constructor algo como string $marca = ‘Nisu’. Tres cosas: 1) Esto es un ejemplo y busco la sencillez, 2) ¿Y si en vez de un string fuera un objeto lo que hay que pasar, 3) ¿En serio?

Bueno, pues creamos nuestro objeto usando este nuevo método y miramos a ver de qué tipo es:

$vehiculo = Vehiculo::crearSinMarca();
echo get_class($vehiculo) . PHP_EOL;

Y esto nos dará, una vez, el resultado esperado:

Vehiculo

Ahora resulta que tenemos una clase, Coche, que hereda de Vehiculo:

class Coche extends Vehiculo
{
}

Por cierto, ¿he mencionado alguna vez que la herencia es el mal absoluto? Un día escribiré un post al respecto.

Y con esta clase crearemos objetos de tipo Coche:

$coche = new Coche('Ferrero');
echo get_class($coche) . PHP_EOL; // Esto muestra 'Coche'

Pero ahora vamos a usar el método crearSinMarca() que hemos heredado de Vehiculo:

$vehiculo = Coche::crearSinMarca();
echo get_class($vehiculo) . PHP_EOL;

Y el resultado es:

Vehiculo

¡Toma ya! ¿Qué ha pasado?

Lo que ha sucedido es que el método crearSinMarca() está en Vehiculo y estamos diciendo que devuelva un objeto de la clase self, que en este caso es Vehiculo.

Entra en acción static

Tranquilidad, que hay una solución y es muy sencilla. Basta con usar static en lugar de self:

    public static function crearSinMarca()
    {
        return new static("Nisu");
    }

Ahora sí, ya tenemos el resultado esperado:

$vehiculo = Vehiculo::crearSinMarca();
echo get_class($vehiculo) . PHP_EOL; // Esto ahora devuelve 'Coche'

Esto tiene que ver con una cosa llamada ‘Late Static Bindings’ de PHP. En otro post te lo contaré.

Nuevo tipo ‘static’ como resultado de un método (PHP8)

Ya que hablamos de static creo que es el momento de mencionar que en PHP 8 se va a poder indicar que el tipo devuelto por un método es static.

Deja un comentario