Vai al contenuto

I metodi magici di PHP: quanti ne hai mai usati?

Chi ha programmato ad oggetti in PHP, conoscerà sicuramente almeno un paio di metodi magici di PHP. Tuttavia non tutti gli sviluppatori utilizzano o conoscono tutti i metodi che PHP mette a loro disposizione.

Ma cosa sono i metodi magici? In breve, sono dei metodi speciali presenti in una classe che vengono chiamate automaticamente al verificarsi di un evento su un oggetto e non richiedono alcuna chiamata di funzione per eseguire il codice al loro interno. Per convenzione, i nomi dei metodi magici iniziano con un doppio underscore (__). Ad esempio, il costruttore e il distruttore di una classe, sono metodi magici: __construct()e __destruct(). Il __construct()metodo viene richiamato automaticamente quando viene creato un oggetto e __destruct()viene chiamato quando l’oggetto viene eliminato.

Oltre a questi due metodi, PHP ha anche i seguenti:

  • __construct()
  • __destruct()
  • __set($property, $valore)
  • __get($property)
  • __toString()
  • __call($fun, $arg)
  • __callStatic($fun, $arg)
  • __isset($content)
  • __unset($content)
  • __sleep()
  • __wakeup()
  • __invoke()
  • __clone()
  • __debugInfo()
  • __set_state($array)

__construct()

E’ il metodo più usato e conosciuto, che viene chiamato automaticamente dopo la creazione di un oggetto. Qui è possibile definire qualsiasi numero di argomenti che verranno passati quando vengono creati gli oggetti. In questo caso si passano 2 argomenti:

class Studente {
    private $nome;
    private $email;
 
    public function __construct($nome, $email) 
    {
        $this->nome = $nome;
        $this->email = $email;
    }
}
 
$objStudente = new Studente('Luca', 'luca@tramontana.com');
?>

__destruct()

Questo metodo è chiamato distruttore e viene chiamato quando l’oggetto viene distrutto. In genere, viene chiamato anche quando lo script viene interrotto o termina. Lo scopo di questo metodo è fornire un’opportunità per salvare lo stato dell’oggetto o qualsiasi altra pulizia che si desidera eseguire.

<?php 
class sample
{
     function __construct()
     {
          echo "costruttore inizializzato";
     }
     function user_def()
     {
          echo "funzione utente";
     }
     function __destruct() { 
          echo "distruttore dell'oggetto"; 
     } 
} 

$obj = new sample();  // viene chiamato il __construct()
$obj->user_def();     // viene chiamato il user_def()
echo is_object($obj); // l'oggetto è ancora esistente

// dopo aver terminato tutte le chiamate all'oggetto, viene chiamato automaticamente __destruct()
?>

__set()

Il __set()metodo magic viene chiamato quando si tenta di impostare un attributo dell’oggetto inaccessibile o inesistente. Lo scopo di questo metodo è impostare attributi aggiuntivi per i quali non sono state definite le proprietà in modo esplicito. Nell’esempio, cerchiamo di impostare l’attributo telefono inesistente. E così, viene chiamato il metodo __set(). Il primo argomento del __set() è il nome della proprietà a cui si accede e il secondo argomento è il valore che stiamo cercando di impostare.

<?php
class Studente {
    private $data = array();

    public function __set($nome, $valore)
    {
        $this->data[$nome] = $valore;
    }
}

$objStudente = new Studente();

$objStudente->telefono = '0491 570 156';  // viene chiamato __set()
?>

__get()

Il __get()è esattamente l’opposto del metodo __set(). Il __get() viene chiamato quando si tenta di leggere i dati da attributi dell’oggetto inaccessibili o inesistenti. Lo scopo di questo metodo è fornire valori a tali proprietà.

<?php
class Studente {
    private $data = array();

    public function __set($nome, $valore)
    {
        $this->data[$nome] = $valore;
    }

    public function __get($nome)
    {
        if(isset($this->data[$nome])) {
            return $this->data[$nome];
        }
    }
}

$objStudente = new Studente();
$objStudente->telefono = '0491 570 156';   // viene chiamato __set()
echo $objStudente->telefono;   // viene chiamato __get()
?>

__toString()

Il metodo __toString() consente di definire cosa si desidera visualizzare quando un oggetto della classe viene trattato come una stringa. Se usiamo echoprint su un oggetto e non abbiamo definito il __toString(), verrà restituito un errore. Nell’esempio sotto, quando facciamo eco $objStudente, verrà chiamato il metodo __toString(). In questo metodo, possiamo decidere cosa visualizzare. Se non avessimo definito il __toString(), la riga avrebbe comportato un errore.

<?php
class Studente {
    private $nome;
    private $email;

    public function __construct($nome, $email)
    {
        $this->nome = $nome;
        $this->email = $email;
    }

    public function __toString()
    {
        return 'Studente nome: '.$this->nome . '<br>' . 'Studente email: '.$this->email;
    }
}

$objStudente = new Studente('John', 'luca@tramontana.com');
echo $objStudente;
?>

__call() e __callStatic()

Se i metodi __get() e __set() vengono chiamati quando abbiamo a che fare con proprietà inesistenti, il __call() viene chiamato quando stiamo tentando di invocare metodi inaccessibili, cioè  metodi non definito nella classe.  Nell’esempio, chiamando il metodo getStudenteDetails, che non è definito, viene chiamato il metodo magico __call(). Il primo argomento è il nome del metodo chiamato e il secondo argomento è un array di argomenti passati nel metodo inesistente.

<?php
class Studente {
    public function __call($methodnome, $arguments)
    {
        // $methodnome corrisponderà a getStudenteDetails
        // $arguments corrisponderà a un array('1')
    }
}

$objStudente = new Studente();
$objStudente->getStudenteDetails(1);
?>

Il metodo __callStatic() è molto simile al __call(); l’unica differenza è che viene chiamato quando si tenta di invocare metodi inaccessibili in un contesto statico. Quindi, se stiamo cercando di accedere a qualsiasi metodo statico che non è definito, verrà chiamata la funzione __callStatic().

Annuncio pubblicitario

__isset() e __unset()

Il metodo __isset() viene chiamato quando si chiama il metodo isset() su proprietà dell’oggetto inaccessibili o inesistenti. Vediamo come funziona attraverso un esempio:

<?php
class Studente {
    private $data = array();

    public function __isset($nome)
    {
        return isset($this->data[$nome]);
    }
}

$objStudente = new Studente();
echo isset($objStudente->telefono);
?>

Nell’esempio sopra, la proprietà telefono non è definita nella classe e quindi verrà chiamato il metodo __isset() che andrà a controllare se esiste l’attributo nell’array definito con set.

Viceversa, il metodo __unset() viene chiamato quando si chiama il metodo unset() su proprietà dell’oggetto inaccessibili o inesistenti.

__sleep() e __wakeup()

Il __sleep() è diverso rispetto ai metodi che abbiamo visto finora. Viene chiamato quando si chiama la funzione serialize() sull’oggetto. Nel caso di un oggetto molto grande, si desidera salvare solo gli attributi selezionati durante la serializzazione e ripulire l’oggetto. Il metodo __sleep() deve in questo caso restituire un array con i nomi di tutti gli attributi dell’oggetto da serializzare.

<?php
class Studente {
    private $nome;
    private $email;
    private $telefono;
    private $db_connection_link;

    public function __construct($nome, $email, $telefono)
    {
        $this->nome = $nome;
        $this->email = $email;
        $this->telefono = $telefono;
    }

    public function __sleep()
    {
        return array('nome', 'email');
    }

    public function __wakeup()
    {
        $this->db_connection_link = your_db_connection_function();
    }

}

$obj = new Studente('Luca', 'email@tramontana.com', '384893848349');
echo serialize($obj);

// OUTPUT
// O:8:"Studente":2:{s:14:"Studentenome";s:4:"Luca";s:15:"Studenteemail";s:20:"email@tramontana.com";}
?>

Nell’esempio di cui sopra, quando si serializza l’oggetto Studente con il metodo serialize(), il metodo __sleep() verrà chiamato automaticamente per preservare solo i valori delle due variabili nome, email. D’altra parte, l’uso del __wakeup() consiste nel ristabilire eventuali connessioni e avviare attività quando la funzione unserialize() viene chiamata sull’oggetto.

__invoke()

Il metodo __invoke() viene chiamato quando si tenta di chiamare un oggetto come se fosse una funzione. Innanzitutto, vediamo come funziona, e poi vedremo lo scopo di questo metodo magico.

<?php
class Studente {
    private $nome;
    private $email;

    public function __construct($nome, $email)
    {
        $this->nome = $nome;
        $this->email = $email;
    }

    public function __invoke()
    {
        echo 'oggetto Studente chiamato come fosse una funzione...';
    }
}

$objStudente = new Studente('John', 'luca@tramontana.com');
$objStudente();
?>

L’objStudente viene trattato come se fosse una funzione e, siccome abbiamo definito il metodo __invoke(), verrà chiamato lui invece di ritornare un errore. Lo scopo principale di questo metodo è appunto quello di trattare gli oggetti come se fossero chiamabili.

__clone()

Per duplicare un oggetto si usa la parola riservata clone. Il metodo magico __clone() viene chiamato automaticamente quando si clona un oggetto. Questo metodo viene usato quando si vuole, ad esempio, modificare le proprietà dell’oggetto clonato.

<?php
Class Studente_Scuola {
}

class Studente {
    private $nome;
    private $email;
    private $object_studente_scuola;

    public function __construct()
    {
        $this->object_studente_scuola = new Studente_Scuola();
    }

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

$objStudente1 = new Studente();
$objStudente2 = clone $objStudente1;
?>

Il problema con l’approccio di cui sopra è che la clonazione crea una copia dell’oggetto ma non clona automaticamente gli oggetti interni dell’oggetto clonato (come in questo caso l’oggetto Studente_Scuola); quindi $ogjStudente1 e $pbjStudente2 utilizzeranno gli stessi riferimenti a object_studente_scuola. Nell’esempio precedente, se non avessimo definito il __clone(), nella variabile  $objStudente2 (contenente l’oggetto clonato), ci sarebbe un puntatore all’oggetto $object_studente_scuola. Implementando il __clone(), ci assicuriamo che l’oggetto Studente_Scuola sia clonato assieme all’oggetto principale.

__debugInfo()

Un’altro comodo metodo magico è il __debugInfo() che viene chiamato quando si tenta di eseguire il dump di un oggetto utilizzando il metodo var_dump(). Se non abbiamo definito questo metodo nella classe, verrà eseguito il dump di tutte le proprietà pubbliche, private e protette. Se invece vogliamo limitare le informazioni visualizzate durante il dumping, possiamo implementare il metodo magico e stabilire cosa visualizzare. Questo metodo restituisce un array di coppie chiave-valore che verrà visualizzato quando la funzione var_dump() viene chiamata sull’oggetto. Possiamo  controllare completamente ciò che vogliamo visualizzare quando l’oggetto viene debuggato con la var_dump().

<?php
class Studente {
    public $nome;
    private $email;
    private $ssn;

    public function __debugInfo()
    {
        return array('Studente_nome' => $this->nome);
    }
}

$objStudente = new Studente();
var_dump($objStudente);
// object(Studente)#1 (1) { ["Studente_nome"]=> NULL }
?>

__set_state()

Ultimo ma non ultimo, è un metodo magico molto particolare che non viene usato molto spesso. Il __set_state() viene utilizzato insieme al metodo var_export(), una funzione di PHP per convertire una variabile in codice PHP. È simile a var_dump() con un’eccezione: la rappresentazione restituita è un codice PHP valido. Quando usiamo questa funzione per esportare le classi, dobbiamo definire il __set_state(). Come si vede nell’esempio, la stringa esportata è un codice PHP valido e possiamo usarla per ripristinare l’oggetto originale.

<?php
class Studente {
    public $nome;
    private $email;  

    public function __construct($nome, $email)
    {
        $this->nome = $nome;
        $this->email = $email;
    }

    public static function __set_state(array $array)
    {
        $obj = new Studente;
        $obj->nome = $array['nome'];
        $obj->email = $array['email'];

        return $obj;
    }
}

$objStudente = new Studente('Luca','luca@tramontana.com');
var_export($objStudente);

// Output: Studente::__set_state(array( 'nome' => 'Luca', 'email' => 'luca@tramontana.com', ))
?>