I metodi magici sono metodi speciali predefiniti che vengono chiamati automaticamente quando vengono soddisfatte determinate condizioni, ad esempio quando si cerca di accedere ad una variabile inaccessibile o inesistente o quando un oggetto viene convertito in una stringa.

__construct()

Il metodo __construct() viene eseguito quando si crea un oggetto di una classe.

__destruct()

Il metodo __destruct() viene eseguito quando un oggetto viene cancellato dalla memoria perchè non ha più riferimenti o perchè lo script termina.

__call()

Il metodo __call() viene eseguito quando si tenta di chiamare un metodo che non esiste o non è accessibile.

__callStatic()

Il metodo __call() viene eseguito quando si tenta di chiamare un metodo statico che non esiste o non è accessibile.

__get()

Con il metodo magico __get($nome_variabile) se si cerca di accedere ad una proprietà della classe che non esiste (oppure che è private o protected) anzichè ricevere un errore si riceve il nome della variabile. Se invece la variabile esiste il metodo non viene eseguito. Un caso d'uso è quello di generare dinamicamente un valore per la proprietà non esistente oppure rilanciare un'eccezione.

<?php

class SimpleClass {
    function __get($name){
        echo "Stai cercando di accedere alla proprietà '" . $name . 
        "' che non esiste o non è accessibile"; 
        throw new Exception('La proprietà ' . $name . ' non esiste');
    }
}

$obj = new SimpleClass();
$obj->value; //non esiste nessuna proprietà value nella classe quindi verrà eseguito il metodo magico

?>

__set()

Con il metodo magico __set($nome_variabile) se si cerca di modificare una proprietà della classe che non esiste (oppure che è private o protected) anzichè ricevere un errore si riceve il nome della variabile. Se invece la variabile esiste il metodo non viene eseguito.

<?php

class SimpleClass {
    function __set($name , $value) {
        echo "Stai cercando di modificare la proprietà '" 
          . $name . "' con il valore '" . $value . 
          "' ma la proprietà non esiste o non è accessibile";
            throw new Exception('La proprietà ' . $name . ' non esiste');
    }
}
$obj = new SimpleClass();
$obj->value = "Ciao"; // non esiste nessuna proprietà value nella classe

?>

__isset()

Il metodo __isset() viene eseguito quando la funzione isset() viene applicata ad una proprietà non esistente o non accessibile

__unset()

Il metodo __unset() viene eseguito quando la funzione unset() viene applicata ad una proprietà non esistente o non accessibile.

__sleep()

__wakeup()

__serialize()

__unserialize()

__toString()

Il metodo __toString($oggetto) viene eseguito quando si tenta di convertire un oggetto in una stringa, ad esempio per debug.

<?php

class BankAccount
{
	private $accountNumber;
    
    public function __toString() {
    return "<p>Numero di conto" . $this->accountNumber . "</p>";
}
    
}

$account = new BankAccount();
//se non ci fosse il metodo __toString() questo darebbe l'errore
//PHP Fatal error:  Uncaught Error: Object of class BankAccount could not be converted to string...
echo $account; 
echo 'Oggetto conto: ' . $account;

?>

__invoke()

__set_state()

__clone()

Il metodo __clone() viene eseguito al temine del clone di un oggetto.

<?php

class Person
{
	public $name;

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

$bob = new Person('Bob');
$alex = $bob; //le due variabili puntano allo stesso oggetto nella memoria 
$alex->name = 'Alex';

var_dump($bob); //stampa Alex
var_dump($alex);

// con la parola chiave clone si evita il puntamento allo stesso oggetto
$alex = clone $bob;
$alex->name = 'Alex';

var_dump($bob); //stampa Bob
var_dump($alex);

Se l'oggetto ha una proprietà che punta al riferimento di un altro oggetto allora anche il clone punterà allo stesso oggetto:

<?php

class Address
{
	public $street;

	public $city;
}

class Person
{
	public $name;

	public $address;

	public function __construct($name)
	{
		$this->name = $name;
		$this->address = new Address(); //riferimento all'oggetto della classe Address
	}
}

$bob = new Person('Bob');
$bob->address->street = 'via Federico Confalonieri 4';
$bob->address->city = 'Milano';

$alex = clone $bob; //usiamo clone e non =

$alex->name = 'Alex'; //questo non modifica $bob
$alex->address->street = 'corso Protopisani 70'; //questo modifica anche $bob
$alex->address->city = 'San Giovanni a Teduccio'; //questo modifica anche $bob

?>

Per evitare questo nella classe si può usare il metodo __clone() per clonare gli oggetti referenziati dalle proprietà della classe

<?php

class Person
{
	public $name;

	public $address;

	public function __construct($name)
	{
		$this->name = $name;
		$this->address = new Address();
	}

	public function __clone()
	{
		$this->address = clone $this->address;
	}
}

?>

@TODO fare approfondimento su copia oggetti con serialize e unserialize

__debugInfo()

Il metodo __debugInfo($oggetto) viene eseguito quando si fa il var_dump() di un oggetto.