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 ejemplo anterior mostrará:
 
   
  
   
   
    __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 ejemplo anterior mostrará:
 
   
    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 ejemplo anterior mostrará:
     
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 ejemplo anterior mostrará:
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 ejemplo anterior mostrará:
object(C)#1 (1) {
  ["propSquared"]=>
  int(1764)
}