Los métodos mágicos de PHP son funciones con nombres reservados que el motor del lenguaje invoca automáticamente cuando ocurren ciertas operaciones sobre un objeto. No los llamas tú directamente: PHP los llama por ti cuando conviertes un objeto a string, accedes a una propiedad que no existe, invocas un método inexistente o usas el objeto como función.
__toString: convertir un objeto en cadena
<?php
class Dinero
{
public function __construct(
private float $cantidad,
private string $moneda = 'EUR',
) {}
public function __toString(): string
{
return number_format($this->cantidad, 2, ',', '.') . ' ' . $this->moneda;
}
}
$precio = new Dinero(1234.5);
echo $precio; // 1.234,50 EUR
echo "Precio: $precio"; // Precio: 1.234,50 EUR
?>
__get y __set: interceptar propiedades inexistentes
<?php
class ConfiguracionDinamica
{
private array $datos = [];
public function __get(string $nombre): mixed
{
if (!array_key_exists($nombre, $this->datos)) {
throw new RuntimeException("Propiedad '$nombre' no definida");
}
return $this->datos[$nombre];
}
public function __set(string $nombre, mixed $valor): void
{
$this->datos[$nombre] = $valor;
}
public function __isset(string $nombre): bool
{
return isset($this->datos[$nombre]);
}
public function __unset(string $nombre): void
{
unset($this->datos[$nombre]);
}
}
$config = new ConfiguracionDinamica();
$config->tema = 'oscuro';
$config->idioma = 'es';
$config->maxResultados = 25;
echo $config->tema; // oscuro
echo isset($config->idioma); // 1 (true)
unset($config->idioma);
?>
__call y __callStatic: interceptar métodos inexistentes
<?php
class ConstructorSQL
{
private string $tabla = '';
private array $campos = [];
private array $condiciones = [];
public function __call(string $nombre, array $args): static
{
if (str_starts_with($nombre, 'donde')) {
$campo = lcfirst(substr($nombre, 5)); // 'dondeId' ? 'id'
$this->condiciones[] = "$campo = '{$args[0]}'";
return $this;
}
throw new BadMethodCallException("Método '$nombre' no existe");
}
public function tabla(string $tabla): static
{
$this->tabla = $tabla;
return $this;
}
public function construir(): string
{
$where = $this->condiciones
? ' WHERE ' . implode(' AND ', $this->condiciones)
: '';
return "SELECT * FROM {$this->tabla}{$where}";
}
}
$sql = (new ConstructorSQL())
->tabla('usuarios')
->dondeActivo(1)
->dondeRol('admin')
->construir();
echo $sql;
// SELECT * FROM usuarios WHERE activo = '1' AND rol = 'admin'
?>
__invoke: usar un objeto como callable
<?php
class ValidadorEmail
{
private array $dominiosBloqueados;
public function __construct(array $dominiosBloqueados = [])
{
$this->dominiosBloqueados = $dominiosBloqueados;
}
public function __invoke(string $email): bool
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return false;
}
$dominio = strtolower(substr(strrchr($email, '@'), 1));
return !in_array($dominio, $this->dominiosBloqueados, true);
}
}
$validar = new ValidadorEmail(['spam.com', 'basura.net']);
echo $validar('[email protected]'); // 1 (true)
echo $validar('[email protected]'); // '' (false)
// Se puede usar directamente como callback
$emails = ['[email protected]', '[email protected]', '[email protected]'];
$validos = array_filter($emails, $validar);
// ['[email protected]', '[email protected]']
?>
Otros métodos mágicos útiles
<?php
class ObjetoCloneable
{
public array $datos;
public function __construct(array $datos)
{
$this->datos = $datos;
}
// Se llama justo después de clone
public function __clone(): void
{
// Asegurar copia profunda si hay objetos anidados
$this->datos = array_map(
fn($v) => is_object($v) ? clone $v : $v,
$this->datos
);
}
}
$original = new ObjetoCloneable(['a' => 1, 'b' => 2]);
$copia = clone $original;
$copia->datos['a'] = 99;
echo $original->datos['a']; // 1 (no afectado)
echo $copia->datos['a']; // 99
?>
La documentación oficial de métodos mágicos en PHP lista todos los métodos reservados con sus firmas exactas, restricciones de visibilidad y comportamiento en contextos de serialización y clonado.
