Métodos mágicos
Los métodos mágicos son métodos especiales que sobrescriben la acción
por omisión de PHP cuando se realizan ciertas acciones sobre un objeto.
Precaución
Todos los métodos que comienzan por __
están reservados por PHP.
Por lo tanto, no se recomienda utilizar un nombre de método de este tipo, excepto cuando
se sobrescribe el comportamiento de PHP.
Los siguientes métodos se consideran mágicos:
__construct(),
__destruct(),
__call(),
__callStatic(),
__get(),
__set(),
__isset(),
__unset(),
__sleep(),
__wakeup(),
__serialize(),
__unserialize(),
__toString(),
__invoke(),
__set_state()
__clone(), y
__debugInfo().
Advertencia
Todos los métodos mágicos, excepto
__construct(),
__destruct(), y
__clone(),
deben ser declarados como public
,
de lo contrario se emitirá una E_WARNING
.
Anterior a PHP 8.0.0, no se emitía ningún diagnóstico para los métodos mágicos
__sleep(),
__wakeup(),
__serialize(),
__unserialize(), y
__set_state().
Advertencia
Si se utilizan declaraciones de tipos en la definición de un método
mágico, deben ser idénticas a la firma descrita en este documento.
De lo contrario, se emitirá un error fatal.
Anterior a PHP 8.0.0, no se emitía ningún diagnóstico.
Sin embargo, __construct() y
__destruct() no deben declarar
un tipo de retorno; de lo contrario, se emitirá un error fatal.
serialize() verifica si la clase tiene un método con el
nombre mágico __sleep().
Si es así, este método será ejecutado antes de cualquier serialización. Puede
limpiar el objeto, y se supone que devuelve un array con los nombres de todas
las variables del objeto que deben ser serializadas.
Si el método no devuelve nada, entonces null
será serializado, y se emitirá una alerta de tipo
E_NOTICE
.
Nota:
No es posible para __sleep() devolver
nombres de propiedades privadas de las clases padres. Hacerlo
resultará en un error de nivel E_NOTICE
.
Utilice __serialize() en su lugar.
Nota:
A partir de PHP 8.0.0, devolver un valor que no sea un array desde
__sleep() emite una advertencia.
Anteriormente se emitía una notificación.
El propósito declarado de __sleep() es validar datos pendientes
o realizar operaciones de limpieza.
Además, esta función es útil si un objeto muy grande no necesita
ser guardado en su totalidad.
Reciprocamente, la función unserialize() verifica
la presencia de un método cuyo nombre es el nombre mágico
__wakeup(). Si está presente, esta función
puede reconstruir cualquier recurso que el objeto pudiera poseer.
El propósito declarado de __wakeup() es restablecer
cualquier conexión de base de datos que se haya perdido
durante la serialización y realizar tareas de reinicialización.
Ejemplo #1 Uso de sleep() y wakeup()
<?php
class Connection
{
protected $link;
private $dsn, $username, $password;
public function __construct($dsn, $username, $password)
{
$this->dsn = $dsn;
$this->username = $username;
$this->password = $password;
$this->connect();
}
private function connect()
{
$this->link = new PDO($this->dsn, $this->username, $this->password);
}
public function __sleep()
{
return array('dsn', 'username', 'password');
}
public function __wakeup()
{
$this->connect();
}
}
?>
public __serialize():
array
serialize() verifica si la clase tiene un método con el
nombre mágico __serialize().
Si es así, este método será ejecutado antes de cualquier serialización.
Debe construir y devolver un array asociativo de pares clave/valor
que represente la forma serializada del objeto. Si no se devuelve ningún array, se lanzará una TypeError.
Nota:
Si __serialize() y
__sleep() están ambas definidas
en el mismo objeto, entonces solo __serialize()
será llamada.
__sleep() será ignorada. Si el objeto
implementa la interfaz Serializable,
el método serialize()
de la interfaz será ignorado y
__serialize() será utilizada en su lugar.
El uso previsto de __serialize()
es definir una representación arbitraria del objeto para serializarlo
fácilmente. Los elementos del array pueden corresponder a las propiedades
del objeto, pero esto no es requerido.
Inversamente, unserialize() verifica la presencia de
una función con el nombre mágico
__unserialize().
Si está presente, esta función recibirá el array restaurado devuelto
por __serialize(). Puede entonces
restaurar las propiedades del objeto desde este array como sea apropiado.
Nota:
Si __unserialize() y
__wakeup() están ambas definidas
en el mismo objeto, entonces solo __unserialize()
será llamada.
__wakeup() será ignorada.
Nota:
Esta funcionalidad está disponible a partir de PHP 7.4.0.
Ejemplo #2 Serialize y unserialize
<?php
class Connection
{
protected $link;
private $dsn, $username, $password;
public function __construct($dsn, $username, $password)
{
$this->dsn = $dsn;
$this->username = $username;
$this->password = $password;
$this->connect();
}
private function connect()
{
$this->link = new PDO($this->dsn, $this->username, $this->password);
}
public function __serialize(): array
{
return [
'dsn' => $this->dsn,
'user' => $this->username,
'pass' => $this->password,
];
}
public function __unserialize(array $data): void
{
$this->dsn = $data['dsn'];
$this->username = $data['user'];
$this->password = $data['pass'];
$this->connect();
}
}?>
El método __toString() determina cómo el objeto
debe reaccionar cuando se trata como una cadena de caracteres.
Por ejemplo, lo que echo $obj;
mostrará.
Advertencia
Un objeto Stringable
no será aceptado por una declaración de tipo string si la
declaración de tipo estricta está activada.
Si se desea tal comportamiento, la declaración de tipo debe aceptar
tanto Stringable como string a través de un tipo de unión.
A partir de PHP 8.0.0, el valor de retorno sigue las semánticas estándar
de PHP, lo que significa que el valor será convertido en una string
si es posible y si el
typing stricte
está desactivado.
A partir de PHP 8.0.0, cualquier clase que contenga un método
__toString() implementa también
implícitamente la interfaz Stringable,
y por lo tanto pasará las verificaciones de tipos para esta interfaz.
Se recomienda implementar explícitamente la interfaz de todos modos.
En PHP 7.4, el valor de retorno debe ser una
string, de lo contrario se lanzará un Error.
Anterior a PHP 7.4.0, el valor de retorno debe
ser una string, de lo contrario se emitirá una E_RECOVERABLE_ERROR
fatal.
Advertencia
Era imposible lanzar una excepción desde el método
__toString() anterior a PHP 7.4.0.
Esto resultaría en un error fatal.
Ejemplo #3 Ejemplo simple
<?php
// Declaración de una clase simple
class ClasseTest
{
public $foo;
public function __construct($foo)
{
$this->foo = $foo;
}
public function __toString()
{
return $this->foo;
}
}
$class = new ClasseTest('Bonjour');
echo $class;
?>
El resultado del ejemplo sería:
__invoke(
...$values
):
mixed
El método __invoke() es llamado cuando un script intenta
llamar a un objeto como una función.
Ejemplo #4 Ejemplo con __invoke()
<?php
class CallableClass
{
public function __invoke($x)
{
var_dump($x);
}
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
?>
El resultado del ejemplo sería:
Ejemplo #5 Ejemplo con __invoke()
<?php
class Sort
{
private $key;
public function __construct(string $key)
{
$this->key = $key;
}
public function __invoke(array $a, array $b): int
{
return $a[$this->key] <=> $b[$this->key];
}
}
$customers = [
['id' => 1, 'first_name' => 'John', 'last_name' => 'Do'],
['id' => 3, 'first_name' => 'Alice', 'last_name' => 'Gustav'],
['id' => 2, 'first_name' => 'Bob', 'last_name' => 'Filipe']
];
// ordenar los clientes por nombre
usort($customers, new Sort('first_name'));
print_r($customers);
// ordenar los clientes por apellido
usort($customers, new Sort('last_name'));
print_r($customers);
?>
El resultado del ejemplo sería:
Array
(
[0] => Array
(
[id] => 3
[first_name] => Alice
[last_name] => Gustav
)
[1] => Array
(
[id] => 2
[first_name] => Bob
[last_name] => Filipe
)
[2] => Array
(
[id] => 1
[first_name] => John
[last_name] => Do
)
)
Array
(
[0] => Array
(
[id] => 1
[first_name] => John
[last_name] => Do
)
[1] => Array
(
[id] => 2
[first_name] => Bob
[last_name] => Filipe
)
[2] => Array
(
[id] => 3
[first_name] => Alice
[last_name] => Gustav
)
)
Este método estático es llamado
para las clases exportadas por la función var_export().
El único parámetro de este método es un array que contiene las propiedades
exportadas en la forma ['property' => value, ...]
.
Ejemplo #6 Uso de __set_state()
class A
{
public $var1;
public $var2;
public static function __set_state($an_array)
{
$obj = new A;
$obj->var1 = $an_array['var1'];
$obj->var2 = $an_array['var2'];
return $obj;
}
}
$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';
$b = var_export($a, true);
var_dump($b);
eval('$c = ' . $b . ';');
var_dump($c);
?>
El resultado del ejemplo sería:
string(60) "A::__set_state(array(
'var1' => 5,
'var2' => 'foo',
))"
object(A)#2 (2) {
["var1"]=>
int(5)
["var2"]=>
string(3) "foo"
}
Nota:
Al exportar un objeto, var_export() no
verifica si __set_state() está
implementada por la clase del objeto, por lo que la reimportación de objetos
resultará en una excepción Error,
si __set_state() no está implementada.
En particular, esto afecta a ciertas clases internas.
Es responsabilidad del programador verificar que solo los objetos cuya clase implementa __set_state() serán re-importados.
Este método es llamado por var_dump() al
procesar un objeto para recuperar las propiedades que
deben ser mostradas. Si el método no está definido en un objeto,
entonces todas las propiedades públicas, protegidas y privadas serán
mostradas.
Ejemplo #7 Uso de __debugInfo()
<?php
class C {
private $prop;
public function __construct($val) {
$this->prop = $val;
}
public function __debugInfo() {
return [
'propSquared' => $this->prop ** 2,
];
}
}
var_dump(new C(42));
?>
El resultado del ejemplo sería:
object(C)#1 (1) {
["propSquared"]=>
int(1764)
}