• seguici su feed rss
  • seguici su twitter
  • seguici su linkedin
  • seguici su facebook
  • cerca

SEI GIA' REGISTRATO? EFFETTUA ADESSO IL LOGIN.



ricordami per 365 giorni

HAI DIMENTICATO LA PASSWORD? CLICCA QUI

NON SEI ANCORA REGISTRATO ? CLICCA QUI E REGISTRATI !

I metodi magici in PHP.

di :: 29 ottobre 2020
I metodi magici in PHP.

Un metodo magico è un metodo di una classe che viene chiamato automaticamente al verificarsi determinati eventi all'interno dell'oggetto istanziato della classe.

Durante la presenziane della classi, ci siamo già imbattuti nel metodo magico costruttore, che non è altro che un metodo di un classe che viene chiamato automaticamente non appena viene istanziato un oggetto della classe stessa. Lo rivediamo oggi.

In questo articolo ci occupiamo di questi metodi magici:

  • __construct
  • __destruct
  • __toString
  • __get
  • __set
  • __call
  • __callStatic

Il metodo magico __construct

Questo metodo viene chiamato automaticamente quando istanziamo un oggetto della classe.

In questo esempio abbiamo una class Users che contiene il metodo costruttore

<?php

class Users{
	public function __construct(){
		echo "ciao";
	}
}

$obj=new Users();
?>

Non appena istanziamo un oggetto $obj di classe Users, viene chiamata automaticamente la classe costruttore, e questa rende la frase "ciao".

Grazi al costruttore possiamo anche passare parametri alla classe, come in questo esempio.

<?php
class Users{
	public function __construct($nome){
		echo "ciao da $nome";
	}
}

$obj=new Users('giulio');
?>

Il metodo magico __destruct

Viene chiamato automaticamente quando

  • o non ci sono altri rifimenenti all'oggetto all'interno dello script
  • o stiamo provando manualmente a distruggere l'oggetto

Vediamo un esempio

<?php
class Users{
	public function __construct(){
		echo "metodo costruttore<br />";
	}

	public function __destruct(){
		echo "metodo distruttore<br />";
	}
}

$obj=new Users;
echo "<h1>hello world</h1>";
?>

Lanciando lo script il risultato sarà

metodo costruttore
hello world
metodo distruttore

Il metodo __destruct non distrugge l'oggetto ma è di aiuto allo sviluppatore per eseguire delle operazioni un attimo prima che l'oggetto venga distrutto.

Se invece vogliamo distruggere manualmente l'oggetto, in un punto del codice, utilizziamo unset

Ecco il nostro esempio

<?php
class Users{
	public function __construct(){
		echo "metodo costruttore<br />";
	}

	public function __destruct(){
		echo "metodo distruttore";
	}
}

$obj=new Users;
unset($obj);
echo "<h1>hello world</h1>";
?>

Il risultato adesso sarà

metodo costruttore
metodo distruttore
hello world

Vediamo un altro esempio, più articolato, che contiene una classe "DistruggoTest" e vediamo come creare un file, scriverci dentro, e distruggerlo

<?php

class DistruggoTest{
	public $fileHandler;

	// apre file o lo crea w se non esiste
	public function openFile(){
		$this->fileHandler=fopen("file.txt","w");
	}

	// scrive il file
	public function scriviSuFile($testo){
		fwrite($this->fileHandler,$testo);
	}

	// chiude il file se non è ancora stato fatto
	public function __destruct()
	{
		if($this->fileHandler){
			fclose($this->fileHandler);
		}
		echo "adesso sono distrutto";
	}
}

// istanziamo un oggetto di classe DistruggoTest
$obj=new DistruggoTest;
$obj->openFile();
$obj->scriviSuFile('ciao');

?>

Il metodo magico __toString

Viene richiamato quando proviamo a trattare l'oggetto, cioè l'istanza di una classe, come una stringa, ad esempio facendo un "echo".

Riprendiamo l'esempio precedente. Se dopo aver creato l'oggetto $obj, faccio subito un "echo $obj", avrò questo errore

Recoverable fatal error: Object of class DistruggoTest could not be converted to string in /var/www/html/test/index.php on line 28

Per evitare questo errore aggiungiamo il metodo __toString alla classe, in questo modo:

<?php

class DistruggoTest{
	public $fileHandler;

	// apre file o lo crea w se non esiste
	public function openFile(){
		$this->fileHandler=fopen("file.txt","w");
	}

	// scrive il file
	public function scriviSuFile($testo){
		fwrite($this->fileHandler,$testo);
	}

	// chiude il file se non è ancora stato fatto
	public function __destruct()
	{
		if($this->fileHandler){
			fclose($this->fileHandler);
		}
		echo "adesso sono distrutto";
	}

	// chiude il file se non è ancora stato fatto
	public function __toString()
	{
		return "siamo nel metodo __toString<br>";
	}
}

// istanziamo un oggetto di classe DistruggoTest
$obj=new DistruggoTest;
echo $obj;
?>

Facendo l'echo dell'oggetto, risponderà automaticamente il metodo __toString, e poi verrà automaticamente chiamata anche la __destruct

siamo nel metodo __toString
adesso sono distrutto

Il metodo magico __get

Questo metodo viene richiamato quando proviamo a leggere il valore di una proprietà che non esistente o che non è accessibile perchè ha un livello di visibilità che non ne consente l'accesso.

In questo esempio, tento di accecedere alla proprietà "nome33" che non esiste. L'oggetto non rende nulla.

<?php
class User{
	public $nome;
	public $email;
}

$obj=new User;
$obj->nome33;
?>

Implementiamo quindi il metodo magico __get, che ha come argomento il nome della proprietà, e che renda una frase in cui si avvisa che quella proprietà non è accessibile

<?php
class User{
	public $nome;
	public $email;
	public function __get($name)
	{
		echo "la proprietà $name non è accessibile";
	}
}

$obj=new User;
$obj->nome33;
?>

Ecco la risposta

la proprietà nome33 non è accessibile

Il metodo magico __set

Questo metodo viene richiamato quando proviamo a settare il valore di una proprietà che non esistente o che non è accessibile perchè ha un livello di visibilità che non ne consente l'accesso.

Se nell'esempio precedente, dopo aver istanziato l'oggetto tentassi di settare la proprietà di "nome35" che non esiste

$obj=new User;
$obj->nome35=10;

non otterrei risposta perchè non esite.

Per evitare questo, estendiamo l'esempio precedente, ed aggiungiamo il metodo magico __set che ha due argomenti: il nome della proprietà ed il valore da settare

<?php

class User{
	public $nome;
	public $email;
	public function __get($name)
	{
		echo "la proprietà $name non è accessibile";
	}
	public function __set($name,$value)
	{
		echo "Stai provando a settare $name con il valore $value. Ma non esiste $name";
	}
}

$obj=new User;
$obj->nome35=10;
?>

Rilancia lo script e la risposta che otterrai sarà

Stai provando a settare nome35 con il valore 10. Ma non esiste nome35

Modifichiamo lo script e rendiamo private le due proprietà.

<?php

class User{
	private $nome;
	private $email;
	public function __get($name)
	{
		echo "la proprietà $name non è accessibile";
	}
	public function __set($name,$value)
	{
		echo "Stai provando a settare $name con il valore $value. Ma non esiste $name";
	}
}

$obj=new User;
$obj->nome=10;
?>

Dopo aver istanziato la classe User proviamo ad accedere alla proprietà "nome" che esiste, ma essendo private non è accessibile dall'esterno. Il risultato quindi sarà l'esecuzione del metodo __set.

Avete quindi capito come utilizzarle i metodi __get e __set.

Nella pratica, questi vengono utilizzati per evitare di implementare dei metodi getter (che vanno a prendere il valore di una determinata proprietà) e setter (che vanno a settare una determinata proprietà).

Vediamo subito di cosa stiamo parlando. Dato che nel nostro esempio non possiamo accedere, dall'esterno della classe, alle proprietà di tipo "private", possiamo avere necessità di utilizzare un metodo getter, come questo, per setter per impostare nome ed email, e getter per prelevarlo, come in questo script:

<?php

class User(){
	private $nome;
	private $email;

    	// metodo getter il nome
	public function getNome(){
		return $this->nome;
	}
    	// metodo setter il nome
	public function setNome($nome){
		$this->nome=$nome;
	}
    	// metodo getter la email
	public function getEmail(){
		return $this->email;
	}
    	// metodo setter la email
	public function setEmail($mail){
		$this->email=$mail;
	}
	public function __get($name)
	{
		echo "la proprietà $name non è accessibile";
	}
	public function __set($name,$value)
	{
		echo "Stai provando a settare $name con il valore $value. Ma non esiste $name";
	}
}

$obj=new User;

// impostiamo nome ed eamil
$obj->setNome('giulio');
$obj->setEmail('giuliodb@libero.it');

// facciamo un echo le nome e del cognome
echo $obj->getNome();
echo $obj->getEmail();

?>

Bene, immaginiamo però che ci siano 10 proprietà e non due. Il codice diventerebbe lunghissimo. In auto arrivano i metodi magici __get e __set.

Riprendiamo il nostro esempio, eliminiamo i metodi getter e setter appena inseriti, ed invece con rispondere con un "echo" nei metodi __get e __set, scriviamo

<?php

class User{
	private $nome;
	private $email;

	public function __get($name)
	{
		$this->$name;
	}
	public function __set($name,$value)
	{
		$this->$name=$value;
	}
}

$obj=new User;

$obj->nome="Giulio";
echo $obj->nome;
echo "<br>";

$obj->email="test@gmail.com";
echo $obj->email;
?>

Quando provo a settare il metodo nome, con il valore Giulio

$obj->nome="Giulio";

viene chiamato il metodo __set

$this->$name=$value;

che è equivalente a

$this->nome="Giulio";

Stiamo quindi simulando il metodo setter ! La stessa cosa vale ovviamente per la proprietà email.

Quando invece recupero il valore della proprietà "nome"

echo $obj->nome;

all'interno del metodo magico __get riceverà "$name = nome", per cui tornerà

$this->nome;

Lo stesso vale per la email.

La risposta sarà quindi

Giulio
test@gmail.com

Nota: se abbiamo poche proprietà è meglio usare i metodi getter e setter per una questione di velocità di esecuzione (i metodi magici infatti risultano essere più lenti). Se invece abbiamo molte proprietà usiamo i metodi magici __get e __set

I metodi magici __call e __callStatic

Il metodo __call viene richiamato automaticamente quando proviamo ad accedere ad un metodo non esistente nella classe, o non è accessibile.
Il metodo __callStatic è simile al precedente ma si fa riferimento ad metodo statico.

Accettano 2 parametri: il nome della funzione (metodo chiamato) e l’array contenente i parametri passati al metodo.

In questo esempio li vediamo entrambi in azione.

<?php
class MyClass {
	public function __call($name, $arguments) {
		echo "<br>"."Chiamata metodo '$name' " . implode(', ', $arguments). "\n";
	}
	public static function __callStatic($name, $arguments) {
		echo "<br>"."Chiamata metodo Static '$name' " . implode(', ', $arguments). "\n";
	}
}

// istanzio un oggetto di classe MyClass
$obj = new MyClass;
// chiamo il metodo runTest, non esistente
$obj->runTest('Oggetto nel contesto');
// chiamo il metodo statico runTest, non esistente
MyClass::runTest('Oggetto nel contesto statico');
?>

In questo esempio al metodo "rumTest" ho passato solo un argomento, ma avrei potuto passarne più di uno, ed esempio

$obj->runTest('Oggetto nel contesto','Pluto')

Adesso vediamo come simulare, utilizzando questi metodi magici, l'ereditarietà multipla in php.

Analizziamo il seguente codice.

<?php

class SimulazioneEM(){
	public function m1(){
		echo "ciao dal metodo m1";
	}
}

class User(){
	private $simulazioneEM;
	public function __construct(){
		$this->simulazioneEM = new simulazioneEM;
	}

	public function m2(){
		echo "ciao dal metodo m2";
	}

	public function __call($nomeMetodo,$argomenti){
		if(method_exists($this->simulazioneEM,$nomeMetodo)){
			call_user_func_array([$this->simulazioneEM,$nomeMetodo],$argomenti);
		}
		else {
			echo "il metodo non è presente neppure nella classe SimulazioneEM";
		}
	}
}

$obj=new User;
$obj->miometodo('Giulio');

?>

Dopo aver istanziato la classe "User" proviamo a chiamare il metodo "miometodo", che non esiste, e passare a questo il valore "Giulio".

Il metodo _call risponderà, perchè il metodo "miometodo" non esiste. In questo metodo magico, verifichiamo se  per caso il metodo è presente all'interno della classe SimulazioneEM:

if(method_exists($this->simulazioneEM,$nomeMetodo))

La funzione method_exists riceve come paramatri l'oggetto di classe simulazioneEM ($this->simulazioneEM) e il nome del metodo.

Se effettivamente esiste in SimulazioneEM allora con la funzione call_user_func_array lo andiamo a richiamare.

Se andiamo a richiamare, tramite l'oggetto di classe User, un metodo "m1" definito nella classe simulazioneEM

$obj->m1();

otteniamo come risposta

ciao dal metodo m1

Quindi riusciamo a richiamare, tramite l'oggetto User, anche il metodo definito nella classe SimulazioneEM

Ovviamente tutto queste complicazioni le potevamo risolvere con un semplice

class User extends SimulazioneEM()

ma il nostro obiettivo era simulare l'ereditarietà multipla, cioè vogliamo richiamare, con un oggetto della classe User, dei metodi presenti in altre classi.

Ultimo esempio

<?php

class SimulazioneEM(){
	public function m1(){
		echo "ciao dal metodo m1";
	}
}

class SimulazioneEM2(){
	public function m4(){
		echo "ciao dal metodo m4";
	}
}

class User(){

	private $simulazioneEM;
	private $simulazioneEM2;

	public function __construct(){
		$this->simulazioneEM = new simulazioneEM;
		$this->simulazioneEM2 = new simulazioneEM2;
	}

	public function m2(){
		echo "ciao dal metodo m2";
	}

	public function __call($nomeMetodo,$argomenti){
		if(method_exists($this->simulazioneEM,$nomeMetodo)){
			call_user_func_array([$this->simulazioneEM,$nomeMetodo],$argomenti);
		}

		else if(method_exists($this->simulazioneEM2,$nomeMetodo)){
			call_user_func_array([$this->simulazioneEM2,$nomeMetodo],$argomenti);

		}

		else {
			echo "il metodo non è presente neppure nella classe SimulazioneEM";
		}
	}
}
$obj=new User;
$obj->m4();

?>

E con questo esempio, abbiamo terminato l'argomento dedicato ai metodi magici in PHP.

Potrebbe interessarti

 
pay per script

Hai bisogno di uno script PHP personalizzato, di una particolare configurazione su Linux, di una gestione dei tuoi server Linux, o di una consulenza per il tuo progetto?

 
 
 
x

ATTENZIONE