Static en PHP: métodos y propiedades estáticas, self:: vs static::

Las propiedades y métodos estáticos en PHP pertenecen a la clase, no a una instancia concreta. Se accede a ellos con :: y permiten compartir estado entre objetos o crear métodos utilitarios sin necesidad de instanciar. La diferencia entre self:: y static:: es crucial cuando hay herencia.

Propiedades y métodos estáticos básicos

<?php
class Contador
{
    private static int $total = 0;

    public function __construct()
    {
        self::$total++;
    }

    public static function getTotal(): int
    {
        return self::$total;
    }

    public static function reiniciar(): void
    {
        self::$total = 0;
    }
}

new Contador();
new Contador();
new Contador();

echo Contador::getTotal(); // 3
Contador::reiniciar();
echo Contador::getTotal(); // 0
?>

self:: vs static:: (Late Static Binding)

self:: siempre hace referencia a la clase donde se declaró el método. static:: se resuelve en tiempo de ejecución según la clase que invocó el método, lo que resulta esencial para patrones con herencia:

<?php
class ModeloBase
{
    protected static string $tabla = 'base';

    public static function getTabla(): string
    {
        return self::$tabla;  // siempre devuelve 'base'
    }

    public static function getTablaDinamica(): string
    {
        return static::$tabla; // devuelve la tabla de la clase llamante
    }
}

class Usuario extends ModeloBase
{
    protected static string $tabla = 'usuarios';
}

class Producto extends ModeloBase
{
    protected static string $tabla = 'productos';
}

echo ModeloBase::getTabla();           // base
echo Usuario::getTabla();              // base  ? self:: no funciona aquí
echo Usuario::getTablaDinamica();      // usuarios  ? static:: sí funciona
echo Producto::getTablaDinamica();     // productos
?>

Patrón Singleton

El Singleton garantiza que solo exista una instancia de una clase. Se implementa con una propiedad estática privada y un método de fábrica estático:

<?php
class ConexionBD
{
    private static ?self $instancia = null;

    private function __construct(
        private readonly string $dsn,
    ) {}

    public static function getInstance(): static
    {
        if (static::$instancia === null) {
            static::$instancia = new static('mysql:host=localhost;dbname=app');
        }
        return static::$instancia;
    }

    // Evitar clonado y deserialización
    private function __clone() {}
    public function __wakeup(): never
    {
        throw new RuntimeException('No se puede deserializar el Singleton');
    }

    public function getDsn(): string
    {
        return $this->dsn;
    }
}

$a = ConexionBD::getInstance();
$b = ConexionBD::getInstance();
var_dump($a === $b); // true — misma instancia
?>

Fábricas estáticas (named constructors)

<?php
class Color
{
    private function __construct(
        public readonly int $r,
        public readonly int $g,
        public readonly int $b,
    ) {}

    public static function desdeHex(string $hex): static
    {
        $hex = ltrim($hex, '#');
        return new static(
            hexdec(substr($hex, 0, 2)),
            hexdec(substr($hex, 2, 2)),
            hexdec(substr($hex, 4, 2)),
        );
    }

    public static function desdeRGB(int $r, int $g, int $b): static
    {
        return new static($r, $g, $b);
    }

    public function toHex(): string
    {
        return sprintf('#%02x%02x%02x', $this->r, $this->g, $this->b);
    }
}

$c1 = Color::desdeHex('#1a2b3c');
$c2 = Color::desdeRGB(255, 128, 0);
echo $c1->toHex(); // #1a2b3c
echo $c2->toHex(); // #ff8000
?>

Cuándo evitar static

El estado estático es global y persiste durante toda la petición, lo que dificulta el testing. Antes de usar static, valora si una instancia inyectada por dependencias no es mejor opción:

<?php
// PROBLEMÁTICO en tests: estado compartido entre casos de prueba
class Cache
{
    private static array $datos = [];
    public static function get(string $k): mixed { return self::$datos[$k] ?? null; }
    public static function set(string $k, mixed $v): void { self::$datos[$k] = $v; }
}

// MEJOR: instancia inyectable, fácil de mockear
class CacheServicio
{
    private array $datos = [];
    public function get(string $k): mixed { return $this->datos[$k] ?? null; }
    public function set(string $k, mixed $v): void { $this->datos[$k] = $v; }
}
?>

La documentación oficial sobre propiedades y métodos estáticos y la de Late Static Binding explican el comportamiento de static:: en contextos de herencia compleja.

COMPARTE ESTE ARTÍCULO

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